Seiten

Herzlich willkommen im technischen Blog der MindBusiness GmbH
In diesem Blog veröffentlichen wir praxisnahes Know-how, neue Erkenntnisse und Erfahrungen zu Microsoft SharePoint- und Office-Themen direkt aus unserer Berater-, Trainer- und Entwickler-Praxis. Hier finden Sie interessante Lösungen und neue Ideen zu den verschiedensten Anforderungen und Problemen. Unseren News-Blog finden Sie unter newsblog.mindbusiness.de/newsblog. Wir wünschen Ihnen viel Spaß beim Lesen und Stöbern und freuen uns auf den Austausch mit Ihnen.

Microsoft Online Services – BPOS - Anychrone Web Service Requests in Silverlight verarbeiten

Sie möchten mehrere Listen in eine Ausgabemaske auslesen um damit ein Desktop Look and Feel zu erzielen. In diesem Blog möchte ich zeigen, wie man mehrere Liste auslesen kann, um damit in einer Silverlight-Maske Autocomplete- und Comboboxen zu füllen. Im folgenden sind einige Screenshots abgebildet, die das Look and Feel zeigen:

Die Maske nach dem Laden

Alle ComboBoxen und AutoCompleteBoxen werden angezeigt und wurde via Web Services mit Inhalten aus unterschiedlichen Listen gefüllt.

image

Eine ComboxBox und AutoCompleteBox:

UnbenanntUnbenannt

Ich möchte einen Weg aufzeigen, wie ich durch konsequente Benamung relativ einfach meine bestehende Maske erweitern kann. Die Benamung wird in den folgenden Quellcodes hervorgehoben. Hier benutze ich das Beispiel einer Nachschlageliste für Bankleitzahlen, BLZ, die in einer AutoCompleteBox verwendet wird.

Ich gehe bei den folgenden Beschreibungen davon aus, das der Leser den Aufruf von Web Services in Silverlight oder generell in einer SharePoint Anwendung kennt. Ich werde also nicht beschreiben, wie ich meinen SOAP Client, hier proxy genannt, generiere.

Mein Design muss folgende Probleme lösen:

(1) unabhängig vom Internal Name einer Liste sein

(2) jederzeit wissen, welche Daten gerade unterwegs sind

(3) AutoComplete- und Comboxboxen erst dann anzeigen, wenn sie auch gefüllt sind.

(4) Den User darüber informieren, dass und welche Daten geladen werden.

(5) Möglichst wenig Code schreiben, wenn weitere Listen bzw. Felder in die Maske aufgenommen werden sollen.

 
Unabhängigkeit vom Internal Name

Dazu habe ich eine eigene Klasse definiert, die mir die GUIDs der Listen und Views zur Verfügung stellt.

namespace de.mindbusiness.dsd.InputMitglieder
{
    public class ListenGUID
    {
        public static string Mitgliederliste = “{A3803E84-55FC-4774-ABB1-15B6E9A42C5B}”;
        public static string Spartenliste = “{E480DC22-E336-4230-92A5-91DC9BE0E400}”;
        public static string Kontodaten = “{F71BB799-1A37-4521-930B-EAE87FB31EBC}”;

        public static string NachschlageListeSparte = “{9FEFBB96-D46A-4EAC-9D41-181CCE44AAC6}”;
        public static string NachschlageListeMitgliedschaft = “{475ABBC4-D9C1-4147-AD31-9540EAAE83A3}”;
        public static string NachschlageListeAnrede = “{586A8B0B-A481-47EC-BF8B-1BE90797975E}”;
        public static string NachschlageListeZahlungsweise = “{7F9DF3FB-61A8-4BDF-9600-8CB5F3ED7E06}”;
        public static string NachschlageListeLand          = “{25CEA6D0-E3AA-4B6C-8A55-07C7BF68B737}”;
        public static string NachschlageListeBLZ           = “{2DEF46DF-947F-4E49-BC44-B856DEF4E542}”;

        public static string NachschlageListeFamilienstatus = “{B4F8A124-26E7-4C5F-B4E9-E65EFC242828}”;

        // View
        // nur Mitgliedsnummern
        public static string NamenUndID = “{3CFF3C86-6F7B-48E7-A648-FB349BF599AC}”;

       // alle Familien
        public static string AlleFamilien = ” {AB45E518-E8A8-420B-BAC0-1706F85A740D}”;

    }

 

Welche Daten sind unterwegs

Dazu brauche ich ein Gedächtnis. Da habe ich mit einer Enumeration gelöst. Hier definiere ich jeweils eine Status, z.B. BLZ, der hier für den Vorgang “BLZ Daten werden geladen” steht.

public enum DSDListen
       {
           UPDATE_MITGLIED,
           UPDATE_KONTODATEN,
           MODUS_BEARBEITEN,
           MITGLIED_EINFUEGEN,
           KONTODATEN_EINFUEGEN,
           MNUMMERN,
           GESCHLECHT,
           MITGLIEDSCHAFT,
           LAND,
           ANREDE,
           FAMILIENSTATUS,
           ZAHLUNGSWEISE,
           BLZ,
           FAMILIE,
           SPEICHERN_MITGLIED,
           NAMEN_UND_ID_LADEN,
           SPEICHERN_KONTODATEN
       }

Jetzt definieren ich ein Flag, das mir zeigen wird, welche Daten gerade geladen werden.

private DSDListen flagListe;

 

Autocomple- und Comboxboxen erst dann anzeigen, wenn sie auch gefüllt sind.

siehe dazu User informieren

User informieren

Problem Nummer 4 löse ich in einem ersten Schritt mit Hilfe eines ProgressBar.Der Algorithmus sieht so aus:

  • alle Controls der Maske ausblenden
  • Progressbar einblenden und Info anzeigen
  • nach dem Laden aller Daten Progressbar aus und alle Maskencontrols ein.

Hier in Ausschnitten der Quellcode:

Ausschalten der Controls: Dazu schaltet ich die jeweiligen Borders aus. Es gibt Border0, Border1 bis n. Da es zu jedem Border ein Label gibt, werden diese natürlich auch ausgeschaltet.

private void hideAtStart()
       {
           string name = “”;
           for (int i = 0; i < this.maxBorders; i++)
           {
               name = “Border” + i;
               Border border = (Border)FindName(name);
               border.Visibility = Visibility.Collapsed;
               if (i < this.maxLabels)
               {
                   name = “lblTitel” + i;
                   Label label = (Label)FindName(name);
                   label.Visibility = Visibility.Collapsed;
               }
           }
           
       }

 

Anzeigen der Progressbar:

private void showProgressBar()
        {
            this.lblBitteWarten.Visibility = Visibility.Visible;
            this.progressBar.Visibility = Visibility.Visible;

        }

Wie ich die je spezifischen Infos während des Ladevorgangs anzeige, werden Sie sehen, wenn ich zeige, wie ich das laden der Daten kontrolliere.

Nach dem Laden die Maske zeigen.

private void showAfterStart()
       {
           string name = “”;
           for (int i = 0; i < this.maxBorders; i++)
           {
               name = “Border” + i;
               Border border = (Border)FindName(name);
               border.Visibility = Visibility.Visible;
               if (i < this.maxLabels)
               {
                   name = “lblTitel” + i;
                   Label label = (Label)FindName(name);
                   label.Visibility = Visibility.Visible;
               }

           }
           this.progressBar.Visibility = Visibility.Collapsed;
           this.lblBitteWarten.Visibility = Visibility.Collapsed;          

}

 

In einem zweiten Schritt steuere ich über den  Callback-Handler der asynchronen Methode den gesamten Vorgang des Ladens der Daten.

Hierbei ist folgendes zu beachten:

Die ListItems werden sequentiell geladen, welche Daten geladen werden wird dem User angezeigt. Als letztes wird die größte Liste, nämlich die Bankleitzahlen inklusive der Bankbezeichnung geladen. Da dieser Vorgang am längsten dauert, wird er am Schluss ausgeführt, die Maske aber schon angezeigt.

 

Schauen wir uns als erste die Registrierung des Callback-Handler im Konstruktor an:

// Callback Handler registrieren
           proxy.GetListItemsCompleted += new System.EventHandler<GetListItemsCompletedEventArgs>(proxy_GetListItemsCompleted);
           proxy.UpdateListItemsCompleted += new System.EventHandler<UpdateListItemsCompletedEventArgs>(proxy_UpdateListItemsCompleted);

Nach der Registrierung des Callback-Handlers werden auch schon die ersten ListI tems geladen. Dies geschieht unmittelbar nach der Registrierung:

flagListe = DSDListen.MITGLIEDSCHAFT;
proxy.GetListItemsAsync(ListenGUID.NachschlageListeMitgliedschaft, null, null, null, null, null, null);

Hier sehen Sie auch den Einsatz meines Flag und der GUID.

Ich lade eine Nachschlageliste für die Mitgliedschaft und diese Liste hat auch den “passenden” Namen. Die Items werden in eine typisierte Liste geladen, die eine ComboBox abbildet.

//Nachschlagelisten
        private List<Nachschlageliste> ItemListeMitgliedschaft = new List<Nachschlageliste>();

Die Klasse Nachschlageliste sieht wie folgt aus:

namespace de.mindbusiness.dsd.InputMitglieder
{
    public class Nachschlageliste
    {

        public int ID {set;get;}
        public String dasItem {set;get;}

    }
}

Für jede Nachschlageliste gibt es einen entsprechende typisierte Liste, so auch für die BLZ.

private List<BLZ> ItemListeBLZ = new List<BLZ>();

Die BLZ ist aber eine ganze spezielle Liste, deshalb gibt es auch eine eigene Klasse dazu.

namespace de.mindbusiness.dsd.Repository
{
    public class BLZ
    {
        public string blz { get; set; }
        public string kurzbezeichnung { get; set; }

    }
}

Halten wir an dieser Stelle fest:

Daten werden im Hintergrund geladen. Wir wissen, dass es sich um Daten handelt, die Informationen zur Mitgliedschaft laden, die in einer bestimmten Liste abgelegt sind. Auf die Liste greifen wir über die GUID zu.

Alle weiteren Vorgänge werden jetzt über den Callback-Handler gesteuert. Wie das geht, zeige ich jetzt in Ausschnitten.

Zunächst die Steuerung des ersten Request:

void proxy_GetListItemsCompleted(object sender, GetListItemsCompletedEventArgs e)
       {
           // …. Quellcode ausgeblendet

           #region Mitgliedschaft, Anrede, Land, Zahlungsweise

           getItemliste(e);

           // Mitgliedschaft
           if (flagListe == DSDListen.MITGLIEDSCHAFT)
           {
               this.comboMitgliedschaft.Items.Clear();
               foreach (Nachschlageliste nl in this.ItemListeMitgliedschaft)
               {
                   this.comboMitgliedschaft.Items.Add(nl.dasItem);
               }
              
flagListe = DSDListen.ANREDE;
               this.lblBitteWarten.Content = “Anrede wird geladen …”;
               proxy.GetListItemsAsync(ListenGUID.NachschlageListeAnrede, null, null, null, null, null, null);
               return;
           }

// … Quellcode ausgeblendet

           #endregion

       }

 

Was geschieht in diesem Quellcode:

Sie können erkennen, dass ich zunächst die ComboBox fülle, wie ist im Moment noch nicht wichtig. Wichtig ist, dass ich danach das Flag ändere, jetzt ANREDE, dann denn Text für die Progressbar ändere und dann den nächsten Request absetze, jetzt für die Nachschlageliste ANREDE!

Wenn ich dann einen weiteren Teil des Handlers “einblende”, dann sehen Sie, dass sich der Vorgang wiederholt. Nach der Anrede lade ich die Länderliste, dann die Zahlungsweise usw.

// jetzt Anrede
            if (flagListe == DSDListen.ANREDE)
            {
                this.comboAnrede.Items.Clear();
                foreach (Nachschlageliste nl in this.ItemListeAnrede)
                {
                    this.comboAnrede.Items.Add(nl.dasItem);
                }

                flagListe = DSDListen.LAND;
                this.lblBitteWarten.Content = “Länderinformationen werden geladen …”;
                proxy.GetListItemsAsync(ListenGUID.NachschlageListeLand, null, null, null, null, null, null);
                return;
            }

            // neu
            // jetzt Land
            if (flagListe == DSDListen.LAND)
            {

                this.comboNationalitaet.Items.Clear();

                foreach (Nachschlageliste nl in this.ItemListeLand)
                {
                    this.comboNationalitaet.Items.Add(nl.dasItem);
                }

                this.comboNationalitaet.SelectedIndex = 33;

                flagListe = DSDListen.ZAHLUNGSWEISE;
                this.lblBitteWarten.Content = “Zahlungsweise wird geladen …”;
                proxy.GetListItemsAsync(ListenGUID.NachschlageListeZahlungsweise, null, null, null, null, null, null);
                return;
            }

 

Möglichst wenig Code schreiben, oder zumindest einfachen Code

Wie schaffe ich es, wenig Code oder einfachen Code zu schreiben. Dazu zeige ich zuerst das Füllen einer ComboBox.

Ich habe hierfür die folgende Funktion geschrieben. Hier sehen Sie, dass ich meinen Status abfrage, spezielle Variablen setze und dann die Daten in eine typisierte Liste vom Typ Nachschlageliste schreibe.

Danach werden die Nachschlagelisten gefüllt und zwar abhängig vom Status, sprich abhängig davon, welche Liste ich gerade geladen habe. In meiner Liste finde ich dann sowohl den String für meine ComboBox als auch die ID, da ich den Inhalt als Nachschlageliste auch zurückschreiben will.

// befüllt eine String Itemliste für ComboBoxen mit Nachschlagelisten
        private void getItemliste(GetListItemsCompletedEventArgs e)
        {

            if (flagListe == DSDListen.FAMILIE)
            {
                this.getItemlisteFamilie(e);
                return;
            }

            String dasAttribut = “ows_Title”;
            String dieID = “ows_ID”;

            // neu
            if (flagListe == DSDListen.LAND)
                this.ItemListeLand.Clear();

            // ende neu

            if (flagListe == DSDListen.MITGLIEDSCHAFT)
                this.ItemListeMitgliedschaft.Clear();

            if (flagListe == DSDListen.ANREDE)
                this.ItemListeAnrede.Clear();

            if (flagListe == DSDListen.ZAHLUNGSWEISE)
                this.ItemListeZahlungsweise.Clear();

            // neu 08.03.10
            if (flagListe == DSDListen.FAMILIENSTATUS)
            {
                dasAttribut = “ows_Familienstatus”;
                this.ItemListeFamilienstatus.Clear();
            }

            // ende neu

            var dieListe = (from x in e.Result.Elements().First().Elements()
                            select new Nachschlageliste
                            {
                                dasItem = x.Attribute(dasAttribut).Value,
                                ID = Int32.Parse(x.Attribute(dieID).Value)

                            }).ToList();

            foreach (Nachschlageliste item in dieListe)
            {
                if (flagListe == DSDListen.MITGLIEDSCHAFT)
                {
                    this.ItemListeMitgliedschaft.Add(item);

                }
                if (flagListe == DSDListen.ANREDE)
                    this.ItemListeAnrede.Add(item);
                if (flagListe == DSDListen.ZAHLUNGSWEISE)
                    this.ItemListeZahlungsweise.Add(item);

                // ende neu
                if (flagListe == DSDListen.LAND)
                    this.ItemListeLand.Add(item);

                // neu 06.04.2010

            }

Was ist aber, wenn ich eine Liste lade, die anders als die Standardnachschlagelisten aufgebaut ist, z.B. die Bankleitzahlen. Wie das geht, zeige ich in den folgenden Abschnitten. Vor die Aufgabe gestellt, eine User-freundliche Eingabe für Bankleitzahlen zu ermöglichen, einfach und fehlerfrei, bin ich so vorgegangen:

Schritt Aktion Benamung
1 AutoCompleteBox anlegen AutoBox
2 Klasse für BLZ anlegen BLZ
3 Generische Liste für BLZ anlegen ItemListBLZ
4 Enum-Value festlegen BLZ
5 GUID der Liste anlegen NachschlagelisteBLZ
6 Methode zum Befüllen der Liste schreiben private void getItemlisteBLZ(GetListItemsCompletedEventArgs e)
7 Proxy erweitern, genauer den CallBack-Handler  

 

Was ich noch zeigen muss, sind die Schritte 6 und 7:

Hier die neue Methode für Schritt 6:

// befüllt die Autocomplete Box für BLZ
private void getItemlisteBLZ(GetListItemsCompletedEventArgs e)
{
    this.ItemListeBLZ.Clear();

    var dieListe = (from x in e.Result.Elements().First().Elements()
                    select new BLZ
                    {
                        blz = x.Attribute(“ows_Title”).Value,
                        kurzbezeichnung = x.Attribute(“ows_Kurzbezeichnung”).Value
                    }).ToList();

    string[] name = new string[dieListe.Count];
    for (int i = 0; i < dieListe.Count; i++)
    {
        name[i] = dieListe[i].blz;
    }
    this.AutoBLZ.ItemsSource = name;
    this.ItemListeBLZ = dieListe;

}

Ich erzeuge wie gewohnt eine typsierte Liste. Danach ein String-Array mit den Bankleitzahlen, welches dann als ItemSource an AutoBLZ gebunden wird. Zum Schluss befülle ich die ItemListBLZ. Hier stehen jetzt BLZ und Bankbezeichnung drinn.

Nun zum Abschluss Schritt 7: Sie sehen wiederum einen Auszug aus dem CallBack-Handler

#region NAMEN_UND_ID_LADEN, lädt BLZ und zeigt Startbildschirm
            if (flagListe == DSDListen.NAMEN_UND_ID_LADEN)
            {
                this.getItemlisteAutoComplete(e);
                flagListe = DSDListen.BLZ;
                proxy.GetListItemsAsync(ListenGUID.NachschlageListeBLZ, null, null, null, null, null, null);
                showAfterStart();
                return;
            }

            #endregion

            #region BLZ, lädt BLZ im Hintergrund
            if (flagListe == DSDListen.BLZ)
            {
                this.getItemlisteBLZ(e);
                return;

            }

Jetzt ist alles “ganz einfach”, allerdings schaltet wir am Ende der Aufrufkette den ProgressBar aus, showAfterStart();, und es werden im Status BLZ keine weiteren Requests abgesetzt.

:-) jetzt darf der Kunde weitere Anforderungen stellen, eine Erweiterung der Maske ist nahezu automatisiert.

Im nächsten Blog werde ich zeige, wie ich mit dem gezeigten AutoComplexBox arbeite.

Hinterlasse eine Antwort

 

 

 

Du kannst diese HTML-Tags benutzen

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">