Blue Orange Green Pink Purple

Archive for February, 2010

You can use the search form below to go through the content and find a specific post or page:

Feb 27

INotifyPropertyChanged mit Hilfe von AOP lösen

Heute möchte ich einmal eine Technik vorstellen, die ich schon öfters bei meinen WPF Projekten eingesetzt habte. Verwendet man in WPF die MVVM-Architektur (ModelView – View – Model), so kommt man um das Konzept des mächtigen WPF Databindings nicht herum. Wer mit Databinding noch nicht so vertraut ist, soll sich zuerst mal hier http://msdn.microsoft.com/en-us/library/ms750612.aspx etwas schlau machen.

Das interessante am WPF Databinding ist die Möglichkeit, Werte bidirektional zu binden: Das heißt, dass man einerseits die Daten von der Datenquelle zur Datensenke binden kann und andererseits auch Änderungen der Datensenke automatisch in die Datenquelle übernehmen kann. In MVVM würde das bedeuten, dass man auf der View (XAML) UI-Controls aus Daten des ViewModels befüllen kann und Änderungen, die auf der View auftreten automatisch zurück in das ViewModel speichern kann, ohne dabei eine Benutzerinteraktion zu benötigen. Wichtig ist auch, dass bereits gebundene Werte auf der View automatisch aktualisiert werden sollen, sobald im ViewModel – also bei der Datenquelle – Änderungen auftreten.

INotifyPropertyChanged oder DependencyProperties
Um ein automatisches Binding-Update zu erreichen, gibt es zwei unterschiedliche Wege, das zu erreichen:

  1. Man leitet eine Klasse von DependencyObject ab und implementiert DependencyProperties. Da ein ViewModel normalerweise nicht von DependencyObject ableitet, ist diese Möglichkeit im Kontext von MVVM nicht wirklich brauchbar. Mehr über DependencyObject und DependencyProperty gibts unter http://msdn.microsoft.com/en-us/library/system.windows.dependencyobject.aspx
  2. Man implementiert das Interface INotifyPropertyChanged (http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx). Dieses Interface implementiert einen Event names PropertyChanged, der gefeuert wird, sobald sich eine Property bzw. der Wert einer Property verändert hat. Bindet man nun in der View (XAML) auf eine Property eines ViewModels, muss diese Property jedesmal, wenn sich der Wert der Property verändert, diesen Event feuern, um automatisch das Databinding zu aktualisieren. Wirft es den Event nicht, gibts auch keine automatische Aktualisierung der View.

Das war mal das Grundgerüst, dass wir für unsere weiteres Thema heute benötigen.

Wo ist das Problem?
Sehen wir uns einmal folgende Klasse an, die INotifyPropertyChanged implementiert und ein paar Properties für das Databinding zur Verfügung stellt:


public class ApplicationViewModel : INotifyPropertyChanged
{
   public event PropertyChangedEventHandler PropertyChanged;

    private string _ApplicationTitleField = string.Empty;
    public string ApplicationTitle
    {
       get { return _ApplicationTitleField; }
       set
       {
          _ApplicationTitleField = value;
          if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("ApplicationTitle"));
       }
    }

    private string _ApplicationVersionField = string.Empty;
    public string ApplicationVersion
    {
        get { return _ApplicationVersionField; }
        set
        {
           _ApplicationVersionField = value;
           if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("ApplicationVersion"));
        }
     }
}

So, wo ist hier jetzt das Problem? Genau, die Properties selbst. Im Grunde genommen ist es kein Problem bzw. ist syntaktisch alles in Ordnung. Ich persönlich habe aber ein Problem damit, aus den Automatic Properties “alte .NET 2.0″ Properties zu erzeugen, nur um im Setter der Property den PropertyChanged Event feuern zu können. Wie lösen wir das Ganze jetzt: Wir verwenden ein tolle Bibliothek names PostSharp, die die Prinzipen von AOP (Aspect Oriented Programming) implementiert.

Lösung: AOP mittel PostSharp
Mit AOP können wir Funktionalität in sogenannte Aspekte auslagern und diese nach Bedarf wiederverwenden. Genau so einen Aspekt können wir jetzt für unser Problem mit dem PropertyChanged Event einsetzen. Wir können hergehen und uns selbst ein die notwendige Funktionalität programmieren oder wir verwenden bereits vorhandene Bibliotheken, die uns bei unserer Problemlösung unterstützen.

In der Community gibt es mittlerweile eine handvoll verfügbaren Bibliotheken, die AOP Funktionalität für .NET zur Verfügung stellen. Mein persönlicher Favorit ist auf alle Fälle PostSharp entwickelt von Gael Fraiteur. Einer der großen Vorteile von PostSharp ist , dass sich die Bibliothek in den MSBuild Process hängt, direkt den CIL Code manipuliert und somit keine Perfomanceeinbussen entstehen. Wie gehen wir jetzt also vor, um unser Problem der alten .NET Properties mit Hilfe eines Aspekts zu lösen?

  1. Zuerst laden wir uns mal PostSharp herunten und installieren es: http://www.sharpcrafters.com/postsharp/download
  2. Um PostHsarp verwenden zu können, müssen wir 2 Referenzen zu unserem Projekt hinzufügen: PostSharp.Public und PostSharp.Laos
  3. Dann erstellen wir uns den neuen Aspekt. Im Grunde ist das eine einfach Klasse, die von einer bestimmten Basisklasse ableitet und als Serializeable gekenntzeichnet werden muss:
    [Serializable]
    public class NotifyAspect : OnMethodBoundaryAspect
    {
    }
    
  4. Die Basisklasse OnMethodBoundaryAspect stellt PostSharp zur Verfügung. Diese Klasse bietet eine Methode names OnExit an, die wir in unserem Apsekt einfach überschreiben und implementieren. Diese Methode wird aufgerufen, wenn eine Methode verlassen wird — also wenn das Ende einer Methode erreicht wurde. Da wir unseren Aspekt für Properties verwenden, ist unsere Methode eigentlich der Getter bzw. Setter – Zugriff der Property:
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
       if (!(eventArgs.Instance is ViewModelBase))
          throw new InvalidCastException("Cannot raise PropertyChanged on viewmodel that doesn't derived from ViewModelBase");
    
       if (!eventArgs.Method.IsSetter()) return;
    
       ViewModelBase viewModel = eventArgs.Instance as ViewModelBase;
    
       viewModel.OnPropertyChanged(eventArgs.Method.SetterName());
    }
    

    Wir prüfen,ob die Instanz, zu der die Methode gehört von einer bestimmten Basisklasse ist. Ist das nicht so, dann werfen wir einen Fehler, da der Aspekt nur mit bestimmen Typen umgehen kann. Warum? Weil wir später eine Methode der Instanz aufrufen müssen. Jetzt überprüfen wir, ob die Methode, für die der Aspekt aufgerufen wird eigentlich eine Setter-Methode einer Property ist. Das haben wir in eine Extension-Methode ausgelagert (der detaillierte Code ist im Beispielprojekte nachzulesen). Jetzt können wir casten und eine bestimmte Methode der Basisklasse aufrufen: OnPropertyChanged wirft im Endeffekt den PropertyChanged Event des Interfaces INotifyPropertyChanged. Diesen Event können wir nur in der Klasse werfen, in der der Event auch definiert wurde. Daher müssen wir hier in unserem Aspekt diesen Umweg gehen.

    public void OnPropertyChanged(string propertyName)
    {
       if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("Propertyname is null or too short.");
    
       PropertyInfo info = this.GetType().GetProperty(propertyName);
       if (info == null) throw new InvalidProgramException("The requested property doesn't exist.");
    
       if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    
  5. Jetzt fehlt uns nur noch, dass wir den Aspekt in unserem ViewModel verwenden:
    public class ApplicationViewModel : ViewModelBase
    {
       [Notify]
       public string ApplicationTitle { get; set; }
    
       [Notify]
       public string ApplicationVersion { get; set; }
    }
    

Wir können uns jetzt eine View bzw. XAML Seite bauen, die dieses ViewModel als DataContext gesetzt hat und auf die beiden Properties databinden. Das Demoprojekt ist wie immer unter http://downloads.juergenoberngruber.at/blog/INotifyPropertyChangedWithAOP.zip zu finden.

In diesem Sinne.

Feb 24

HowTo: Custom Cursor für Windows Forms

Heute wieder ein kurzer Eintrag: Letztens wollte ich in einer WinForms Anwendung bei gewissen Aktionen Cursor verändern. .NET liefert zwar ein paar Curors-Typen (System.Windows.Forms.Cursors) mit, die befriedigen bei sehr individuell angepassten und oft auch selbstgerenderten WinForms Anwendungen nicht ganz. Daher will ich heute kurz beschreiben, wie man seinen eigenen Cursor aus einem lokalen File bzw. Resource lädt und verwendet.

Was benötigen wir?
Das einzige, was wir brauchen ist eine einfache *.ico Datei, die wir später als Cursor darstellen wollen. Am besten erstellt man sich so ein File mit einem Bildverarbeitungsprogramm seines Vertrauens. Hat man dass, können wir bereits mit Visual Studio weitermachen. Wir erstellen uns ein WinForms Projekt und fügen das zuvor erstellte *.ico File zum Projekt hinzu und hängen es in die Resources des Projekts.

Jetzt können wir über den Properties-Namespace des Projekts auf die Resources zugreifen und sollten dort über Intellisense unsere soeben hinzugefügte Grafik finden. Die ist vom Type Icon — die wiederrum eine Property names “Handle” verfügt. Genau diesen IntPtr übergeben wir dem Konstruktor der Cursor-Klasse und weisen diese Cursor-Instanz auf den aktuellen Cursor der Anwendung zu. Fertig.

Cursor.Current = new Cursor(Properties.Resources.Cursor_Wait.Handle);

Aussehen tut das Ganze dann so:

Custom Cursor

Demoprojekt gibts wie immer hier zu finden: http://downloads.juergenoberngruber.at/blog/CustomCursor.zip

In diesem Sinne.

Feb 18

HowTo: Erzeugen einer Liste von aufeinanderfolgenden Jahreszahlten?

Heute mal ein kurzer Eintrag, der die Frage “Wie kann ich eine Liste von aufeinanderfolgenden Jahreszahlen einfach erzeugen?” beantworten soll. Out-of-the-box wird das Ganze nicht unterstützt, deshalb schreiben wir uns einfach eine kleine Hilfsmethode, die wir ja in eine Bibliothek oder dergleichen auslagern können:

public static List<DateTime> FindYearsBetween(DateTime start, DateTime end, bool includeStart, bool includeEnd)
{
   if (start == null) throw new ArgumentNullException("Start-DateTime is null.");
   if (end == null) throw new ArgumentNullException("End-DateTime is null.");

   // Zuerst überprüfen wir, ob das Start-Datum gültig ist ...
   if (start == System.DateTime.MaxValue || start == System.DateTime.MinValue)
      throw new ArgumentOutOfRangeException("Start-DateTime cannot be MaxValue or MinValue.");

   // ... und das gleiche für das End-Datum.
   if (end == System.DateTime.MaxValue || end == System.DateTime.MinValue)
      throw new ArgumentOutOfRangeException("End-DateTime cannot be MaxValue or MinValue.");

   // Das Start-Datum muss vor dem End-Datum liegen.
   if (start.CompareTo(end) >= 0)
      throw new ArgumentOutOfRangeException("End-DateTime must be later than Start-DateTime.");

    // Soll das Anfangsjahr nicht in die Ergebnismenge inkludiert werden, dann zählen
    // wir zu dem Anfangsjahr einfach ein Jahr dazu, somit beginnen wir beim Folgejahr.
    if (!includeStart) start = start.AddYears(1);

    // Soll das Endjahr nicht in die Ergebnismenge inkludiert werden, dann holen wir
    // uns das Vorgängerjahr. Somit beenden wir die Suche schon beim Vorgängerjahr.
    if (!includeEnd) end = end.AddYears(-1);

    List<System.DateTime> range = new List<System.DateTime>();

    // Jetzt fürgen wir solange neue Datumswerte hinzu, bis das
    // Anfangsjahr gleich dem Endjahr ist.
    while (start <= end)
    {
        range.Add(start);
        start = start.AddYears(1);
     }

     return range;
}

Der Code inkl. Kommentare ist großteils selbsterklärend. Wichtig ist, die übergebenen Datumswerte auf Gültigkeit zu prüfen – zB muss das Enddatum nach dem Startdatum liegen. Die beiden boolschen Parameter geben an, ob das Startdatum (also das Jahr) in die Ergebnismenge inkludiert werden soll — das gleiche gilt für das Enddatum.

Demoprojekt inklusive Unit.Tests (nunit) gibts wie immer als Download: http://downloads.juergenoberngruber.at/blog/DateTimeList.zip

In diesem Sinne.

Feb 17

HowTo: Existierende VHD im Bootmanager von Windows 7 registrieren

Da ich in letzter Zeit schon öfters gefragt wurde, wie man ein vorhandes VHD File im Bootmanager von Windows7 regiestrieren kann, hier eine kurze Beschreibung dazu.

Warum eigentlich?
Gerade in der Softwareentwicklung verwenden viele Entwickler virtualisierte Systeme, um isolierte Umgegebungen zu gewährleisten und den unterschiedlichen ansprüchen diverser Projekte gerecht zu werden. Beispielsweise sind bei zwei Projekten unterschiedliche Datenbanken im Einsatz, die dann man nich beide auf ein und dem selben System installieren möchte. Oder es sind grundsätzlich zwei verschiedene Technologie wie bspw. .NET und Java im Einsatz.

Daher versucht man, diese unterschiedlichen Anforderungen auf mehreren virtualisierten Systemen abzubilden –> in unserem Fall eben VHD Images.

Um diese Systeme nun zu verwenden, kann man sie entweder im Dualboot booten — also zusätzlich zum Host/Master-System. Dh, man hat zur gleichen Zeit zwei (oder vielleicht sogar mehrere) Systeme am laufen, die natürlich ein und den selber Speicher verwenden. Somit bekommt man vermutlich relativ bald mal Performanceprobleme, weil beide Systeme nicht mehr so tun, wie man das will. Daher gibts die Möglichkeit, diese Systeme im Bootmanager von Windows 7 zu registrieren. Somit hat man die Möglichkeit vor dem Booten das gewünschte System zu wählen, dass schlussendlich hochgefahren werden soll (das ist übrigens nichts neues — gabs auch schon bei Windows Vista).

Wie funktioniert das Ganze?
Es gibt dafür ein Kommando names “bcdedit”, das einfach über die Kommandozeile ausgeführt werden kann (WICHTIG: Die Kommandozeile muss “als Administrator” gestartet werden). Führt man das Kommando ohne Parameter aus, dann bekommt man eine Liste der bereits registrieten Systemen im Bootmanager (das gleiche passiert auch, wenn man es mit dem Parameter /v ausführt):

c:\bcdedit

So um nun ein neues VHD File in den Bootmanager reinzuhängen, brauchen wir ein paar mehr Befehle. Als erstes, um das Ganze zu vereinfachen, erstellen wir uns eine Kopie des aktuelle verwendeten Systems (dh wir kopieren uns aus dem Bootmanager alle Einstellungen des Systems, das wir gerade hochgefahren und laufen haben)

c:\bcdedit /copy {current} /d "Name des Eintrags"

Der Parameter /d beschreibt den Namen des Eintrags, der später beim Bootmanger angezeigt wird. Hier sollten wir sprechende Namen vergeben, um später im Bootmanager nicht den Überblick zu verlieren. Klappt der Befehl liefert uns das Kommande eine neue ID zurück, die wir uns in die Zwischenablage kopieren sollten. Führen wir jetzt erneut bcdedit ohne Parameter aus, sehen wir, das es zusätzlich einen neuen Eintrag mit dem von uns vergebenen Namen gibt.

Als nächstes müssen dem neu erstellten Eintrag einige Parameter setzen:

c:\bcdedit /set {guid} device vhd=[driveletter:]\{directory}\{vhdfilename}.vhd
c:\bcdedit /set {guid} osdevice vhd=[driveletter:]\{directory}\{vhdfilename}.vhd
c:\bcdedit /set {guid} detecthal on

Was passiert hier: Zuerst setzen wir den Parameter device und geben den Dateipfad zu dem VHD File an, das wir booten möchten. Das gleiche passiert für den Parameter osdevice. detecthal gibt an, dass sich der Bootmanager selbst darum kümmern soll, welchen Kernel und HAL (Hardware Abstraction Layer) er laden soll. guid ist die ID des Eintrags, die wir vorher nach dem kopieren bekommen haben und die eigentlich in der Zwischenablage liegen sollte. Der Verzeichnispfad muss einer gewissen Syntax entsprechen (siehe oben) und könnte bspw. so aussehen:

c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} device vhd=[D:]\Images\W7_Evaluation.vhd
c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} osdevice vhd=[D:]\Images\W7_Evaluation.vhd
c:\bcdedit /set {5f9fb961-9357-11de-8ce0-86b14e4a0753} detecthal on

Jetzt sind wir eigentlich fertig. Wir können nun das System runterfahren und wollten vor dem Booten den neuen Eintrag im Bootmanager sehen. Falls es das erste Image ist, das wir in den Bootmanager gehängt haben, brauchen wir sonst nichts tun. Der Bootmanager erkennt selbständig, dass es nun mehrere Optionen gibt und listet die möglichen Systeme von selbst auf.

Möchte man bestehende Systeme aus dem Bootmanager rauslöschen, geht das ebenfalls über bcdedit

c:\bcdedit /delete {guid} /cleanup

{guid} ist die ID des Eintrags, den wir löschen wollen. Die ID finden wir, wenn wir über bcdedit /v alle bestehenden Einträge auflisten.

Weitere Infos zu bcddedit findet man unter http://technet.microsoft.com/en-us/library/cc709667%28WS.10%29.aspx.

In diesem Sinne.

  • Recent Posts
    • HowTo: Eigene Regeln für Microsoft StyleCop erstellen
    • HowTo: Microsoft StyleCop Integration mit Visual Studio und MSBuild
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 2
    • HowTo: Eigene Templates für Visual Studio 2008 erstellen, Teil 1
    • Performance des Cassini Webservers in Kombination mit Firefox
  • Archives
    • June 2010 (2)
    • May 2010 (3)
    • March 2010 (3)
    • February 2010 (4)
    • January 2010 (3)
    • December 2009 (1)
    • November 2009 (9)
  • Tags
    .net AOP ASP.NET ASP.NET MVC blend Bootmanager C# ci Codequality Configuration Continuous Integration css Cursor DateTime DDD Deployment dynamisch emit Exrpession Extensibility Fluent HowTo Microsoft MVVM Pattern PostSharp Reflection Repository ruby silverlight Software Design StyleCop System teamcity Templates VHD Virtual Images Visual Studio Vorlagen web Windows 7 Windows Mobile WinForms WPF XAML
  • About

    Jürgen Oberngruber is a project manager and software architect living in Wels, Austria and currently working at ecomplexx Austria, Wels. During his study at the University of Applied Sciences in Hagenberg, Austria he gained a deep knowledge in the field of software engineering using a lot of different programming languages. Since a few years he's focusing on the Microsofts .NET platform including all relevant technologies. One of his passion is to explore, to test and to evaluate new technologies and programming languages (mostly in the field of Microsofts .NET platform). Checkout more information on www.juergenoberngruber.at

  • Meta
    • Log in
    • Entries RSS
    • Comments RSS
    • WordPress.org
  • Archives
    • June 2010
    • May 2010
    • March 2010
    • February 2010
    • January 2010
    • December 2009
    • November 2009
  • Search






  • Home

© Copyright Jürgen Oberngruber's Blog. All rights reserved.
Designed by FTL WordPress Themes brought to you by DT Web Template

Back to Top