Visual Studio Lightswitch 2010 – Einen Search Screen bei Änderungen automatisch aktualisieren.

Eine Applikation mit Visual Studio Lightswitch zu machen, ist eine spaßige Angelegenheit, man kommt schnell voran weil man sich nicht um die Infrastruktur der Applikation kümmern muss, aber trotzdem eine schöne, mehrschichtige Anwendung bekommt. Der Anfänger kann leicht einfache Use-Cases umsetzen, die Fortgeschrittenen können sich viel anpassen und auch erweitern.

Ein kleines Manko, welches mir bei der Umsetzung einer Anwendung aufgefallen ist (bzw. deren Anwender):

Man hat eine Listenansicht (SearchScreen) die eine bestimmte Menge an Datensätzen anzeigt. Fügt man nun in einen anderen Bildschirm (z.B. einer Detailansicht (Details Screen) oder einer Maske zum Hinzufügen (Add New Screen)) einen neuen Datensatz zu dieser Menge hinzu, schließt den Bildschirm und kehrt wieder zur Listenansicht zurück, so stellt man fest, dass der neue Datensatz nicht angezeigt wird, erst nachdem man auf die standardmäßig vorhandene Schaltfläche „Aktualisieren“ drückt, erscheinen die neuen Daten.

Ich möchte hier einen Weg vorstellen wie das auch automatisch funktioniert, erfunden habe es aber nicht ich, sondern Sheel Shah vom Visual Studio Lightswitch Team. Den Originalbeitrag findet ihr hier. Die Idee daran ist es, das ganze über einen eigenen EventHandler abzuhandeln. Das heisst, die Applikation bekommt einen Eventhandler verpasst, der Search Screen registriert sich für alle Events dieses Handlers. Wird nun in einem anderen Screen ein Datensatz hinzugefügt, oder verändert, so wird beim speichern dieses Event ausgelöst. Der Search Screen bekommt das Event mitgeteilt und kann damit die Datenquelle aktualiseren. Hier greifen dann die normalen Lightswitch-Mechanismen und die GUI wird aktualisiert.

Der einzige zu Sheel’s Beitrag ist, dass ich bei mir einen Search Screen aktualisiere, und kein einzelnes Steuerelement.

Ausgangslage, die Tabelle mit den Namen „Kunden“:

Tabelle Kunden - Übersicht
Tabelle Kunden

Ausgehend von dieser Tabelle gibt es zum Einem den Search Screen „Kunden“ sowie die Eingabemaske zum Anlegen eines neuen Kunden „Neuer Kunde“

Beginnen wir nur nun mit den Adaptionen an der Eingabemaske „Neuer Kunde“, die erste Änderung muss im Code an der Stelle „KundeNeu_CanRun“ vorgenommen werden.

Bearbeiten von "KundeNeu_CanRun"
Bearbeiten von „KundeNeu_CanRun“

Folgende Anpassungen sind dort zu machen: Anlegen eines Delegaten und eines Eventhandlers, sowie einer Methode die das Event „feuert“.

public delegate void RecordAdded();
public event RecordAdded CustomerAdded;

public void RaiseCustomerAdded() {
  if (this.Details.Dispatcher.CheckAccess()) {
    CustomerAdded();
  } else {
    this.Details.Dispatcher.BeginInvoke(delegate() { CustomerAdded(); });
  }
}

Wir bleiben im Detailformular, jetzt wird etwas Code zum Ereignis „KundeNeu_Saved“ hinzugefügt, die Ergänzung ist einfach, es wird einfach das Event „abgefeuert“:

partial void KundeNeu_Saved() {
  this.Close(false);
  Application.RaiseCustomerAdded();
}

So, wir haben nun den Event-Handler definiert, wir haben dafür gesorgt, dass das Event auch ausgelöst wird, nun brauchen wir noch jemanden, der sich für diese Events auch interessiert, und das ist nun das Suchformular „Kunden“. In dieser Ansicht gibt es oben im Ribbon eine Schaltfläche, mit der man einen Kunden neu anlegen kann – hier registriert man sich für das Event:

partial void NewCustomer_Execute() {
  Application.CustomerAdded += new Application.RecordAdded(CustomerAdded);
  Application.ShowKundeNeu();
}

Das ist die Methode, die dann aufgerufen wird, es wird einfach ein Refresh der Datenquelle vorgenommen, die dem Search Screen zugrundeliegt. Der Rest wird dann vom internen Eventsystem abgehandelt, und die GUI initialisiert.

private void CustomerAdded() {
  this.Details.Dispatcher.BeginInvoke(() => this.CustomersByName.Refresh());
}

Zu guterletzt sollte man auch noch aufräumen, ich denke aber, dass diese Lösung hier noch nicht unbedingt sauber ist, das muss ich mir bei Gelegenheit noch genauer ansehen, wenn der Search Screen geschlossen wird, wird der der Eventhandler wieder „deregistriert“:

partial void Kunden_Closing(ref bool cancel) {
  Application.CustomerAdded -= new Application.RecordAdded(CustomerAdded);
}

Kurzes Fazit: Die Implementierung ist doch ein wenig aufwändig, vor allem wenn man die Mechanismen des Event-Handlings noch nicht so gut versteht, aber es ist eine saubere und zuverlässige Umsetzung.

Mit Lambda-Expressions in C# den Code verkürzen

Lambda-Expressions sind zwar schon ein alter Hut in C#, trotzdem möchte ich auch mal ein kleines Beispiel bringen, wo diese Dinger ungemein praktisch sind. Sie verkürzen nicht nur die LOC sonder tragen meiner Meinung nach ganz wesentlich dazu bei, den Code auch übersichtlich zu behalten.

Ich verwende es um in einem Silverlight WCF-RIA Projekt die Callbacks des asynchronen Webserviceaufrufs entgegenzunehmen. Um den Unterschied zu verdeutlichen, das ganze natürlich in der klassischen „Vorher / Nachher“-Variante

Vorher: Natürlich zuerst einmal ohne Lambdas, 3 Methoden, 17 Zeilen Code:

public override void LoadData() {
  LoadOperation loadOperationCountry = _context.Load(_context.GetCountrySetQuery(), loadOperationCountryCallback, null);
  LoadOperation loadOperationCustomer = _context.Load(_context.GetCustomerSetQuery(), loadOperationCustomerCallback, null);
}

void loadOperationCustomerCallback(LoadOperation customers) {
  if (customers != null) {
    Customers = new ObservableCollection(customers.Entities);
    CurrentCustomer = Customers[0];
  }
}

void loadOperationBranchCallback(LoadOperation branches) {
  if (branches != null) {
    Branches = new ObservableCollection(branches.Entities);
  }
}

Nachher: 11 Zeilen Code, also ca. 1/3 weniger, und nur mehr 1 Methode. Man muss also nicht mehr aufpassen, dass man die Methoden im Quellcode zusammenhält um später mal nicht so lange suchen zu müssen wo denn nu der verdammte Quellcode für den Callback ist.

public override void LoadData() {
  LoadOperation loadOperationCountry = _context.Load(_context.GetCountrySetQuery(), 
    (e) => {
      if (e!=null) Countries = new ObservableCollection(e.Entities);
    }, null);

  LoadOperation loadOperationCustomer = _context.Load(_context.GetCustomerSetQuery(), 
    (e) => { 
      if (e != null) Customers = new ObservableCollection(e.Entities); 
    }, null);
}

 

Einfache FTP Klasse für C#

Vor kurzem hab ich einen FTP-Datentransfer innerhalb eines C# Programmes benötigt, nach etwas Gehirnschmalzvernichtung ist dieses nichtssagende, undokumentierte Stück Code rausgekommen. Nichts besonderes, keine ausgefuchste Fehlerbehandlung, keine Spezialfunktionen, keine ausführlichen Tests durchlaufen – wozu auch, es läuft ja. 😉

Zur meiner Entschuldigung muss ich auch vorbringen, dass das ja nur der erste Schuss ist – zumindest bevor es produktiv geht, sollten zumindest noch ein paar Fehlerbehandlungsroutinen da reinwachsen.

using System;
using System.Net;
using System.IO;

class FTP {

    private string ftpServerIP;
    private string ftpUserName;
    private string ftpPassWord;

    public FTP(string serverIP, string userName, string passWord) {
        this.ftpServerIP = serverIP;
        this.ftpUserName = userName;
        this.ftpPassWord = passWord;
    }

    public bool Upload(string fileName) {
        FileInfo fi = new FileInfo(fileName);
        string uri = "ftp://" + ftpServerIP + "/" + fi.Name;

        FtpWebRequest ftpRequest = CreateRequest(uri, WebRequestMethods.Ftp.UploadFile);
        ftpRequest.ContentLength = fi.Length;

        int buffLength = 2048;
        byte[] buff = new byte[buffLength];
        int count;
            
        bool result = false;
            
        using (FileStream fileStream = fi.OpenRead()) {
            Stream ftpStream = ftpRequest.GetRequestStream();

            count = fileStream.Read(buff, 0, buffLength);

            while (count > 0) {
                ftpStream.Write(buff, 0, count);
                count = fileStream.Read(buff, 0, buffLength);
            }

            ftpStream.Close();
            fileStream.Close();

            result = true;
        }

        return result;

    }

    public bool Download(string filePath, string fileName) {

        string uri = "ftp://" + ftpServerIP + "/" + fileName;
        FtpWebRequest ftpRequest = CreateRequest(uri, WebRequestMethods.Ftp.DownloadFile);

        int buffLength = 2048;
        int count;
        byte[] buff = new byte[buffLength];

        bool result = false;

        using (FileStream outputStream = new FileStream(filePath + "\\" + fileName, FileMode.Create)) {

            FtpWebResponse response = (FtpWebResponse) ftpRequest.GetResponse();
            Stream ftpStream = response.GetResponseStream();

            count = ftpStream.Read(buff, 0, buffLength);
            while (count > 0) {
                outputStream.Write(buff, 0, count);
                count = ftpStream.Read(buff, 0, buffLength);
            }

                
            ftpStream.Close();
            response.Close();
            outputStream.Close();

            result = true;
        }

        return result;
    }

    private FtpWebRequest CreateRequest(string uri, string requestMethod) {
        FtpWebRequest ftpRequest;
        ftpRequest = FtpWebRequest.Create(new Uri(uri)) as FtpWebRequest;
        ftpRequest.Credentials = new NetworkCredential(this.ftpUserName, this.ftpPassWord);
        ftpRequest.KeepAlive = false;
        ftpRequest.Method = requestMethod;
        ftpRequest.UseBinary = true;
        return ftpRequest;
    }
}

 

Lotus Domino und C#

Mittlerweile darf ich mich beruflich ja ein wenig mit Lotus Domino Softwareentwicklung auseinandersetzen. Die Dominowelt ist eine ganz ganz eigene Welt – aber schön langsam gewöhne ich mich daran.

Meine ersten Schritte passierten allerdings von ausserhalb, Ziel war der Zugriff auf Dominodatenbanken von Java Programmen (Servlets) aus. Dies funktioniert auch ganz gut, vielleicht veröffentliche ich später einmal ein paar Codeschnipsel.

Heute hab ich mich einmal daran versucht das ganz in C# zu testen, und als Ergebnis gilt es zu sagen, dass das auch viel besser funktioniert, vor allem weil man die COM Klassen ja direkt aus C# raus ansprechen kann.

Dabei ist folgender Codeschnippsel entstanden, der nix anderes macht als die lokale Maildatenbank zu öffnen, alle Mails zu durchwühlen, die Attachments die sich darin befinden an einen „sicheren“ Ort speicher und dann aus dem eigentlichen Mail rauslöscht, aber nicht ohne vorher einen Hinweis zu hinterlassen wo sich denn nun der Anhang befindet.

Vielleicht braucht ja sowas mal wer wirklich. 🙂

Read more

C# Farbenmanagement #2

Ein paar kleine Optimierungen / Refactorings haben sich in meiner Farbenkonvertierungsklasse breitgemacht.

Zum einen habe ich den Code vereinfacht, indem ich sich wiederholenden Code entfernt habe, zum anderen brauchte ich noch eine weitere Konvertierungsart, und zwar vom XYZ Farbraum zurück in den L*ab Farbraum.

Farbmessgeräte werden üblicherweise einmal am Tag kalibriert, dazu haben sie einen Weiß-Standard der gemessen wird und an dem sich das Gerät orienterien kann. Somit werden die Alterungserscheinungen des Blitzlichtes wieder kompensiert und man bekommt gleichmässige Messergebnisse.

Die Werte dieses Standards liegen aber nur als XYZ Werte vor, das Messgerät liefert aber in der gebrauchten Einstellung nur L*ab Werte zurück, daher die kleine zusätzliche Konvertierungsmethode.

Bei den Refactorings hat sich gezeigt wie unendlich praktisch Unit-Tests sind. Man kann seine Methoden optimieren, kürzen, verändern. Ist man fertig lässt man die Unit-Tests drüberlaufen und kann sich dann sicher sein, dass die Überarbeitungen nichts an den Ergebnissen geändert hat.

Und hier nun der gesamte neue Code:

Read more