Sonntag, 8. Dezember 2013

Modulare Anwendung mit Prism?

Da meine nächste Anwendung wieder in WPF entwickelt werden soll und diese diesmal in viele verschiedene Module aufgeteilt wird, habe ich mich auf die Suche nach Patterns und Frameworks gemacht. Schnell bin ich wieder einmal auf Prism gestoßen. Schon vor einiger Zeit habe ich mich mit dem Framework befasst, es damals allerdings als nicht passend für meine damalige Anwendung gehalten. Mit dem neuen Projekt sieht das nun anders aus!

Verschiedene Module sollen von verschiedenen Entwicklern entwickelt werden. Wichtig dabei ist mir, dass die Abhängigkeiten zwischen den Modulen bis auf's Minimum reduziert werden. Die Anwendung soll wie immer Test Driven entwickelt werden können und außerdem soll es möglich sein, dass wenn ein Modul nicht geladen werden kann, nicht die ganze Anwendung nicht benutzt werden kann, sondern alle anderen Module zur Verfügung stehen!

Die letzten Projekte wurden mit Cinch, das auf MEFedMVVM aufsetzt, entwickelt. Auf die vielen Features, die diese Frameworks mit sich bringen, möchte ich natürlich auch in den nächsten Projekten nicht verzichten müssen. Aber Sacha Barber hat sich (wie immer) auch darum bemüht und evaluiert, wie diese Frameworks miteinander harmonieren. Einen Beitrag dazu hat er auf Codeproject verfasst.

Prism legt viel Wert auf unabhängige Module und Dependency Injection, was das Testen der Software Komponenten vereinfacht. Es können verschiedene Dependency Injection Manager, wie Unity oder das im .NET Framework integrierte MEF genutzt werden. Die Entscheidung viel schnell und leicht, da ich ja weiterhin Cinch bzw. MEFedMVVM nutzen möchte.

Prism selbst ist in der Version 4 sehr gut Dokumentiert. Ich bin derzeit fleißig am Lesen und Evaluieren, ob alle meine Anforderungen erfüllt werden. Momentan sieht es aber so aus, als ob Prism genau das richtige wäre!

Samstag, 7. Dezember 2013

Fazit für Feedly

Am 1. Juli 2013 hatte google den google reader aus dem Chrome Web Store genommen und feedly übernahm die meisten User.

Fazit: Die Übernahme von vorhandenen Blog Abos hatte ohne Probleme funktioniert. Kategorien können eingerichtet werden und es kann zwischen verschiedenen Themes ausgewählt werden. Außerdem und wie ich finde das wichtigste ist, dass die verschiedenesten Plattformen unterstützt werden. So kann ich nicht nur auf dem PC News lesen, sondern auch auf meinem IPhone!

Freitag, 6. Dezember 2013

Nokia verleiht Entwicklergeräte

Wer gerne mal eine App entwickeln möchte, aber leider kein passendes Smartphone zur Hand hat, kann sich bei Nokia diese nun leihen. Da möchte Nokia die Entwicklerwelt mal wieder ein bisschen nach vorne bringen!

http://developer.nokia.com/forms/Entwicklergeraet.xhtml

Freitag, 13. September 2013

Das neue iOS 7 kommt

Endlich ist es soweit!
Das neue iOS von Apple kommt und ich bin schon wahnsinnig gespannt drauf. Es kommt in einem neuen Design mit einer ganz neuen Farbpalette und steht für iPhones ab der Version 4 zur Verfügung. Das Video zur siebten Version vom iOS kann man sich hier anschauen und verspricht so einiges.
Ich zumindest freu mich jetzt schon auf den 18. September :)

Die ApplicationException im .NET Framework

Wozu wurde eigentlich die ApplicationException im .NET Framework eingeführt und wer sollte diese nutzen?
Sinn und Zweck dieser Exception war urspünglich alle selbst geschriebenen Exceptions von der ApplicationException ableiten zu lassen. So konnte man in einem try-catch Block einfach unterscheiden, ob eine Ausnahme aus der eigenen Anwendung oder aus dem .NET Framework geschmissen wurde.
Leider wurde dieses Konzept schnell wieder von Microsoft selbst über den Haufen geworfen, indem direkt aus dem Framework heraus schon ApplicationExceptions geworfen werden. So ist es zum Beispiel möglich beim Nutzen des Namespaces Reflection oder auch beim Laden eines FixedDocuments eine ApplicationException zu bekommen.

Donnerstag, 20. Juni 2013

Mit MEFedMVVM zur WPF Anwendung

Ich nutze nun schon seit geraumer Zeit das Framework Cinch von Sacha Barber um meine Anwendungen zu entwickeln. Cinch selber macht sich MEFedMVVM zu nutze um die Bindung zwischen View und ViewModel zu erstellen.

Mit dem Framework ist es leicht für die Design Zeit die gebundenen Klassen auszutauschen und das Testen wird durch die Schnitstellen sehr vereinfacht.

Hier die Links:

Dienstag, 18. Juni 2013

Command Line Args mit MEFedMVVM

Wie bekommt man eigentlich Command Line Argumente mit MEFedMVVM in sein ViewModel? Die Argumente kommen ja wie üblich in WPF der StartUp Methode der App Klasse mit.

Es gibt zwei Wege:
  1. Entweder man nutzt die Argumente der StartUp Methode und weißt diese einem Service des Containers zu.
     ICommandLineArgs service = ViewModelRepository.Instance.Resolver.Container.GetExport<ICommandLineArgs>().Value;  
    
  2. Oder man greift ganz einfach auf die Methode System.Environment.GetCommandLineArgs() zu. Das hat den Vorteil, das es nicht nötig ist, in der StartUp Methode die Argumente zuzuweisen.
    Nutzt man aber trotzdem einen eigenen Service, der intern die System.Environment.GetCommandLineArgs() nutzt, kann man diesen Service, wie gewohnt, seinem ViewModel übergeben. Für die Tests kann dann ganz einfach ein Test Service geschrieben werden, um die Argumente für die Testfälle vorzugeben.

Sonntag, 16. Juni 2013

Lesen um zu lernen

Jeden Tag schreitet die Entwicklung ein Stückchen vorran. Immer auf dem aktuellen Stand zu sein, ist nicht immer leicht. Clean Code Developer rät deshalb sechs Fachbücher im Jahr zu lesen. Wenn man nun mal überlegt, wie viel Fachbücher kosten, kann das schnell mal ins Geld gehen. Nehmen wir mal an ein Buch kostet 60 Euro und sollen sechs im Jahr lesen. Das wären schon mal 360 Euro im Jahr. Dabei gibt es immer wieder Bücher, bei denen schnell auffällt, dass das Buch für einen selbst nicht geeignet ist. Das heißt man würde sich, um die Anzahl sechs auch wirklich zu lesen, nochmal ein bis zwei Bücher extra kaufen. Und so landet man schnell bei kosten zwischen 400 und 500 Euro im Jahr.

safaribooksonline.com kann die Geldbörse entlasten. Für gut 240 Euro im Jahr, kann man eine viele viele Bücher online und auf mobilen Geräten lesen. Es gibt dort Fachbücher ohne ende und man kann sich zehn Bücher im Monat anschauen. Außerdem ist eine Volltextsuche über alle Bücher möglich, was einem das Suchen bei Problemen extrem vereinfacht.
Für 35 Euro monatlich bekommt man sogar einen komplett Zugang, in dem so viele Bücher gelesen werden können, wie man möchte.
Für die mobilen Geräte gibt es sogar die Möglichkeit die Bücher offline zu lesen!

Reinschauen lohnt sich in jedem Fall. Für 15 Tage kann man kostenfrei alles ausprobieren!

Donnerstag, 13. Juni 2013

Nachtrag zu: Mauszeiger im Sourcecode setzen


Wie ich ja schon beschrieben hatte, ist es möglich den Cursor aus dem Sourcecode heraus zu setzen. Allerdings ist dabei zu beachten, dass dies bedeutet, dass das setzen der Eigenschaft Cursor am WPF Control keine Auswirkung mehr hat. Erst wenn man die Eigenschaft wieder auf null setzt, wird die Cursor Eigenschaft des Controls wieder berücksichtigt.

Dementsprechend sollte mein Beispiel viel eher so aussehen:
 private void OnIsBusyChanged(object sender, bool isBusy)  
 {  
      if (isBusy)
           Mouse.OverrideCursor = Cursors.Wait;
      else
           Mouse.OverrideCursor = null;
 }

So wird die Anwendung, wenn sie in den Busy Modus versetzt wird, den Wait Cursor anzeigen. Ansonsten den Cursor, den das WPF Control vorgibt.

Mittwoch, 12. Juni 2013

TFS for free

Mit dem Team Foundation Server von Microsoft können Projekte geplant und verwaltet werden. Sowohl eine Sourcecode Verwaltung, als auch einen Buildagent bringt er mit sich.

Auf der Seite tfs.visualstudio.com kann man diesen kostenfrei nutzen!

Folgend ein paar Argumente, warum man es einfach mal versuchen sollte:

  • Es können beliebig viele Projekte angelegt werden.
  • Bei der Sourcecode Verwaltung kann sogar zwischen der Microsoft eigenen und Git gewählt werden.
  • Agile Projektplanung wird vom TFS auch unterstützt.
  • Tests können ausgeführt werden.
  • Arbeiten in Teams bei der kostenfreien Variante mit bis zu fünf Personen.
Und alles was man braucht, ist eine Windows Live ID. Also ausprobieren lohnt sich!

Dienstag, 11. Juni 2013

Mobile Farbwahl mit dem iPhone

Nachdem ich vor kurzem über den Adobe Service Kuler berichtete, habe ich nun auch eine mobile Version des Services entdeckt. Sobald man die App auf dem iPhone startet, wird die Kamera aktiviert und die Farbwahl beginnt.

Die fünf Farbpunkte sucht sich die App automatisch zusammen. Es ist möglich das aktuelle Bild mit einem Klick zu fixieren und die Farbpunkte beliebig zu verschieben.
Ein Farbschema kann dann gespeichert und wenn gewünscht auch direkt der Community zur Bewertung freigegeben werden.

Folgend der Link zur App:

Montag, 10. Juni 2013

Mauszeiger im Sourcecode setzen

Der Cursor kann, nicht nur wie schon beschrieben im XAML gesetzt werden. Er kann auch im Sourcecode, wie zum Beispiel im ViewModel, überschrieben werden.

Als klassisches Beispiel habe ich einen Event-Handler in dem ich schaue, ob die Anwendung Busy ist oder nicht und dementsprechend einen Wait Cursor oder einen Pfeil anzeigt.

 private void OnIsBusyChanged(object sender, bool isBusy)  
 {  
      if (isBusy)  
           Mouse.OverrideCursor = Cursors.Wait;  
      else  
           Mouse.OverrideCursor = Cursors.Arrow;  
 }

Sonntag, 9. Juni 2013

LED Control in WPF

Wie ich ja schon mal berichtete, ist es in WPF recht einfach ein eigenes Control zu entwickeln. Nun möchte ich das mal anhand eines Beispiels belegen.
Ziel: Ein LED Control, bei dem es möglich ist eine beliebige Farbe vorzugeben und außerdem soll das Control in der Größe auch skalierbar sein, ohne das es an schärfe verliert.

Als erstes legen wir eine neue Klasse mit dem Namen "Led" an. Diese bekommt eine DependencyProperty mit dem Namen Color vom Typ System.Windows.Media.Color. Das ist dann später die Eigenschaft, die im XAML gesetzt werden muss, um der Led eine Farbe zu geben.

Hier die Led.cs Datei
 using System.Windows;  
 using System.Windows.Controls;  
 using System.Windows.Media;  

 namespace LedSpike  
 {  
   public class Led : Control  
   {  
     public static readonly DependencyProperty ColorProperty =  
       DependencyProperty.Register("Color", typeof(Color), typeof(Led), new PropertyMetadata(default(Color)));  
     public Color Color  
     {  
       get { return (Color)GetValue(ColorProperty); }  
       set { SetValue(ColorProperty, value); }  
     }  
     static Led()  
     {  
       DefaultStyleKeyProperty.OverrideMetadata(typeof(Led), new FrameworkPropertyMetadata(typeof(Led)));  
     }  
   }  
 }  

Jedes Control benötigt ein ContentControl, ohne dieses kann es nicht angezeigt werden. Wie man unterschiedliche Themes anlegt, habe ich hier beschrieben.

Wir legen nun erstmal den Themes Ordner in unserem Projekt an und erstellen die Generic.xaml Datei, um das Standard Template für unser Control zu erstellen.
Da das Control in verschiedenen Größen genutzt werden soll, nutzen wir eine Viewbox. Danach erstellen wir eine Ellipse und legen einen Farbverlauf fest, damit das Control auch modern aussieht. Unser Verlauf hat zwei Farben, wobei ich die eine Farbe generell mit Weiß vorbelege, um einen schimmernden Effekt zu erzeugen. Die andere Farbe habe ich an die DependencyProperty gebunden.

Hier die Themes/Generic.xaml Datei:

 <ResourceDictionary  
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
   xmlns:local="clr-namespace:LedSpike">  
   <Style TargetType="{x:Type local:Led}">  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:Led}">  
           <Viewbox>  
             <Ellipse Margin="10,10,10,10" Width="300" Height="300">  
               <Ellipse.Fill>  
                 <RadialGradientBrush Center="0.6,0.35" GradientOrigin="0.6,0.35" RadiusY="0.67" RadiusX="0.67">  
                   <RadialGradientBrush.RelativeTransform>  
                     <TransformGroup>  
                       <ScaleTransform CenterY="0.35" CenterX="0.6" ScaleY="1" ScaleX="1" />  
                       <SkewTransform AngleY="0" AngleX="0" CenterY="0.35" CenterX="0.6" />  
                       <RotateTransform Angle="-4.447" CenterY="0.35" CenterX="0.6" />  
                       <TranslateTransform X="0" Y="0" />  
                     </TransformGroup>  
                   </RadialGradientBrush.RelativeTransform>  
                   <GradientStop Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Color}" Offset="0.8" />  
                   <GradientStop Color="White" />  
                 </RadialGradientBrush>  
               </Ellipse.Fill>  
             </Ellipse>  
           </Viewbox>  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
 </ResourceDictionary>  

Die Benutzung ist sehr einfach. Es bedarf lediglich einen Namespace Eintrag und das Led Element im XAML.

 <Window  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     xmlns:local="clr-namespace:LedSpike" x:Class="LedSpike.MainWindow"  
     Title="LED spike" Height="200" Width="200">  
   <Grid>  
     <local:Led Color="Red" />  
   </Grid>  
 </Window>  

Das Ergebnis:


Samstag, 8. Juni 2013

WPF Resourcen in mehrere Dateien auslagern

In WPF hat man die Möglichkeit Styles und Templates für seine Controls in Dateien auszulagern. Die Klasse ResourceDictionary hilf einem dabei.
 <ResourceDictionary  
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
 </ResourceDictionary>  
Um dann mehrere Resourcen zu laden kommt die MergedDictionaries Property ins Spiel. Wenn man zum Beispiel einem Window verschiedene Resourcen zuweisen möchte, könnte das so aussehen:
 <Window x:Class="WpfSpike.MainWindow"  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">  
   <Window.Resources>  
     <ResourceDictionary>  
       <ResourceDictionary.MergedDictionaries>  
         <ResourceDictionary Source="Sample1.xaml" />  
         <ResourceDictionary Source="Sample2.xaml" />  
       </ResourceDictionary.MergedDictionaries>  
     </ResourceDictionary>  
   </Window.Resources>  
 </Window>h
Wie man sehen kann, wird dem ResourceDictionary über die Source Property der Pfad zur Resource übergeben.

Freitag, 7. Juni 2013

Mauszeiger im XAML setzen

Jedes WPF Control bzw jedes FrameworkElement hat eine Cursor Eigenschaft. Mit der kann man jedem Control den gewünschten Cursor zuordnen.

 <StackPanel>  
      <Label Cursor="Hand">label with hand cursor</Label>  
      <Label Cursor="Help">label with help cursor</Label>  
 </StackPanel>  

Das Ergebnis ist dementsprechend:



Die Cursor Eigenschaft vererbt sich auch weiter, so dass jedes Child Control auch davon betroffen ist.

Donnerstag, 6. Juni 2013

WPF Themes

Warum sieht eigentlich eine WPF Anwendung unter Windows XP anders aus als unter Windows 7 oder einer anderen Windows Version?

WPF macht sich dem System Theme zu nutze, um seine Standard Controls der Windows Umgebung anzupassen. Stellt man unter Windows 7 den System Theme auf "Windows klassisch", so besitzt das Window der eigenen Anwendung wieder den Rahmen, den man noch aus Windows XP Zeiten kennt.

Schreibt man ein eigenes Control, so sollte man sich überlegen, ob es sinnvoll ist, den verschiedenen Themes auch ein unterschiedliches Layout zu geben. Möchte man lediglich ein Design für alle Themes haben, so muss nur in der Generic.xaml Datei im Themes Verzeichnis ein ControlTemplate angelegt werden. Sollen auch andere Themes unterstützt werden, so können zum Beispiel die Datei Themes/Aero.NormalColor.xaml für den Standard Windows 7 Theme oder Themes/Classic.xaml für den klassischen Theme angelegt werden.

Hinweis: Das ThemeInfoAttribute Attribut darf natürlich nicht fehlen, wenn man ein eigenes Control in WPF schreibt!

Mittwoch, 5. Juni 2013

Hilfe bei der Farbwahl

Immer wieder steht man als Entwickler vor der Qual der Wahl. Es gibt tausende verschiedene Farben, aber welche von denen passen wirklich zusammen und lassen sich für die eigene Anwendung verwenden? Das bei gelber Schrift auf blauen Hintergrund einem die Nackenhaare zu Berge stehen, ist mittlerweile vielen bekannt. Aber welche Farbe nun wirklich mit anderen Farben harmoniert, ist trotzdem nicht immer so leicht zu entscheiden, weil solche Entscheidungen oft nur nach "Gefühl" getroffen werden.

Auf der Internetseite kuler.adobe.com bekommt man aber Unterstützung! Zum einen gibt es einige Farbkombinationen als Vorlage und zum anderen kann man auch selber Farbkominationen erstellen und bewerten lassen!

Probiert es einfach mal aus! Ich finde, man merkt auf anhieb, dass die Farben dort sofort miteinander harmonieren und so einem die Farbwahl extrem erleichtert wird!

Dienstag, 4. Juni 2013

SplashScreen in WPF

Einen SplashScreen in WPF Anwendungen anzuzeigen ist leicht und schnell mit dem Visual Studio gemacht. Es muss lediglich ein Bild dem WPF Projekt hinzugefügt und die Build Action auf "SplashScreen" gesetzt werden.


Beim Kompilieren wird dann automatisch eine Instanz der Klasse SplashScreen erstellt und dem Konstruktor den Pfad des Bildes übergeben. Die Instanz besitzt eine Methode Show, die mit dem Parameter true aufgerufen wird, bevor eine Instanz der eigentlichen App erstellt wird. Der Parameter true sorgt dafür, dass sich der SplashScreen nach dem Ladevorgang wieder automatisch schließt.

Es ist natürlich auch möglich einen SplashScreen ohne die Build Action zu nutzen. Dafür muss lediglich die SplashScreen Klasse direkt in der Methode OnStartup der App aufgerufen werden.

Montag, 3. Juni 2013

Übersetzungen? Nein danke!

Eine Anwendung zu übersetzen kann unter umständen sehr lästig sein! Grade wenn man die gewünschte Fremdsprache nur selten oder gar nicht spricht. Deshalb macht es auf jeden fall Sinn die Anwendung erstmal in der Muttersprache zu verfassen. Denn in keiner anderen Sprache weiß man besser, wie man einen Satz am besten formuliert.

Trotzdem sollten schon zu beginn der Anwendung Resource Dateien angelegt werden. Wie man das macht beschreibe ich hier. Das erspart am Ende der Entwicklung das nervige und zeitaufwändige Suche nach den Stellen, die lokalisiert werden müssen.

Hat man die Anwendung fertig, kann man sich ganz in ruhe um die Übersetzung kümmern oder diese von einer Fremdfirma oder Kollegen machen lassen.

Sonntag, 2. Juni 2013

Gruppieren von Resourcen in .NET Anwendungen

Eine Anwendung mehrsprachig zu machen, ist in der .NET Welt schnell und einfach gemacht. Auch im XAML Code von WPF Anwendungen sind diese einfach, wie auch hier beschrieben, zu nutzen. Wichtig für die Nutzung in WPF ist, dass der Zugriff auf die Resource public ist, ansonsten kann diese nicht direkt  gebunden werden.

Aber nun endlich zum eigentlichen Thema. Anwendungen können sehr schnell, sehr groß werden und so kann eine Resourcen Datei schnell mehrere hundert oder tausende Einträge beinhalten. Man sollte sich schon zu beginn der Entwicklung überlegen, ob und wie es nicht Sinn machen könnte, die Resourcen zu gruppieren.

Eine gute Stategie ist mehrere Resource Dateien anzulegen. Zum Beispiel für jeden großen Bereich eine Datei. So könnte eine Anwendung zum Beispiel in die Bereiche Main und Settings getrennt werden. Außerdem lege ich auch immer eine Messages Resource an, diese beinhaltet alle Nachrichten, die in MessageBoxen angezeigt werden.

Freitag, 31. Mai 2013

WPF RibbonWindow

Das Ribbon hat sich mittlerweile bei vielen Anwendungen durchgesetzt und wird nun nicht mehr nur von Microsoft selber genutzt. In WPF ist es denkbar einfach das Grundgerüst eines Ribbons zu erstellen.

Bevor es wirdlich los geht, muss erstmal die benötigte Reference System.Windows.Controls.Ribbon hinzugefügt werden:

Und nun auch schon der Sourcecode, in dem zwei RibbonButtons angezeigt werden:

 <RibbonWindow x:Class="WpfSpike.MainWindow"  
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
     Title="Ribbon" Height="300" Width="500">  
   <Grid>  
     <Grid.RowDefinitions>  
       <RowDefinition Height="Auto" />  
       <RowDefinition Height="*" />  
     </Grid.RowDefinitions>  
     <Ribbon>  
       <RibbonTab Header="Home">  
         <RibbonGroup Header="Group 1">  
           <RibbonButton Label="Button 1" />  
           <RibbonButton Label="Button 2" />  
         </RibbonGroup>  
       </RibbonTab>  
     </Ribbon>  
     <Grid Grid.Row="1">  
       <TextBlock>main content</TextBlock>  
     </Grid>  
   </Grid>  
 </RibbonWindow>  

Wie man sieht, kann dieses Beipiel nun auch ganz einfach um mehrere RibbonTabs, RibbonGroups oder andere Elemente, wie zum Beispiel RibbonRadioButtons oder RibbonTextBox erweitern.

Das Ergebnis sieht dann wie folgt aus:








WPF DataTrigger

Der DataTrigger ist ein guter und einfacher Weg, WPF Controls im Aussehen zu ändern oder zum Beispiel das IsEnabled Flag anzupassen. Im Folgenden werde ich den Content eines Buttons anpassen, je nachdem, ob die Checkbox selektiert ist oder nicht. Es ist natürlich auch möglich an eine Property vom DataContext zu binden.

 <CheckBox x:Name="IsEnabled" IsChecked="True" HorizontalAlignment="Center" />  
 <Button>  
      <Button.Style>  
           <Style>  
                <Style.Triggers>  
                     <DataTrigger Binding="{Binding ElementName=IsEnabled, Path=IsChecked}" Value="True">  
                          <Setter Property="Button.Content" Value="one" />  
                     </DataTrigger>  
                     <DataTrigger Binding="{Binding ElementName=IsEnabled, Path=IsChecked}" Value="False">  
                          <Setter Property="Button.Content" Value="two" />  
                     </DataTrigger>  
                </Style.Triggers>  
           </Style>  
      </Button.Style>  
 </Button>  

Das Ergebnis ist dann wie erwartet:





Samstag, 25. Mai 2013

Die kleinen und großen Helferlein

Es gibt zig unterschiedliche Helfer bei der Entwicklung mit .NET. Hier mal ein Auszug von den Tools, die ich teilweise schon seit vielen Jahren einsetze:

Freitag, 24. Mai 2013

GroupBox Header wird nicht disabled angezeigt

Die WPF GroupBox kann wie jedes Control, das von UIElement ableitet, über die Property IsEnabled enabled bzw. disabled werden.
Leider wird der Header Text dabei nicht wie erwartet ausgegraut. Wenn man aber das HeaderTemplate überschreibt, kann man auf einfache Weise dieses Verhalten implementieren.

 <StackPanel>  
      <CheckBox Content="CheckBox" x:Name="Checkbox" />  
      <GroupBox Header="Text 1" IsEnabled="{Binding ElementName=Checkbox, Path=IsChecked}" />  
      <GroupBox Header="Text 2" IsEnabled="{Binding ElementName=Checkbox, Path=IsChecked}">  
           <GroupBox.HeaderTemplate>  
                <DataTemplate>  
                     <Label Content="{Binding}" />  
                </DataTemplate>  
           </GroupBox.HeaderTemplate>  
      </GroupBox>  
 </StackPanel>  

Hier der Unterschied zwischen den beiden GroupBox Controls:

Donnerstag, 23. Mai 2013

InverseBooleanConverter mit bool? Unterstützung

An manchen Stellen ist es notwendig einen boolischen Wert in WPF zu invertieren. Dafür bieten sich Converter an. Man erstellt einfach eine Klasse, die von IValueConverter ableitet und implementert die benötigten Methoden Convert und ConvertBack.
Mein InverseBooleanConverter berücksichtigt auch den Typ Nullable<bool>. Dies kann zum Beispiel für Checkboxen wichtig sein kein, wenn man die Property IsThreeState auf true gesetzt hat, um 3 States (true, false und null) abzubilden.

Folgend der Converter:
 public class InverseBooleanConverter : IValueConverter  
 {  
      public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
      {  
           return InverseValue(value, targetType);  
      }  
      public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
      {  
           return InverseValue(value, targetType);  
      }  
      private static object InverseValue(object value, Type targetType)  
      {  
           if (targetType != typeof(bool) && targetType != typeof(bool?))  
                throw new InvalidOperationException("The targetType must be a bool!");  
           if (value == null)  
                return null;  
           return !(bool)value;  
      }  
 }  

Und hier die Beispielview mit dem XAML Code.
 <StackPanel>  
      <StackPanel.Resources>  
           <wpf:InverseBooleanConverter x:Key="InverseBooleanConverter" />  
      </StackPanel.Resources>  
      <CheckBox x:Name="IsEnabled" IsChecked="True" HorizontalAlignment="Center" />  
      <TextBox Width="80" Height="20" IsEnabled="{Binding Path=IsChecked, ElementName=IsEnabled}">text 1</TextBox>  
      <TextBox Width="80" Height="20" IsEnabled="{Binding Path=IsChecked, ElementName=IsEnabled, Converter={StaticResource InverseBooleanConverter}}">text 2</TextBox>  
 </StackPanel>  



Mittwoch, 22. Mai 2013

Tracen bei WPF Bindings

Seit dem .NET Framework 3.5 stellt Microsoft für Debugging Zwecke ein Tracing für WPF zur Verfügung.

 <Button Command="{Binding ReleaseCommand, PresentationTraceSources.TraceLevel=High}">Release</Button>  

Damit hat man die Möglichkeit ein bisschen nach zu vollziehen, was bei den WPF Controls unter der Haube so passiert. Zum Beispiel, wie ein gebundener Wert vor und nach einem Converter ist. Der Trace Level kann  None, Low, Medium oder High sein.
Diese angefügte Eigenschaft kann auf alle Klassen angewendet werden, die von BindingBase oder BindingExpressionBase ableiten. Also für folgende Klassen:
  • Binding
  • MultiBinding
  • PriorityBinding
  • BindingExpression
  • MultiBindingExpression
  • PriorityBindingExpression.
Außerdem ist die auch bei ObjectDataProvider oder XmlDataProvider anwendbar.

Hier nochmal ein Auszug in dem ich ein Command am Button gebunden habe:

 System.Windows.Data Warning: 55 : Created BindingExpression (hash=41717140) for Binding (hash=2418355)  
 System.Windows.Data Warning: 57 :  Path: 'ReleaseCommand'  
 System.Windows.Data Warning: 59 : BindingExpression (hash=41717140): Default mode resolved to OneWay  
 System.Windows.Data Warning: 60 : BindingExpression (hash=41717140): Default update trigger resolved to PropertyChanged  
 System.Windows.Data Warning: 61 : BindingExpression (hash=41717140): Attach to System.Windows.Controls.Button.Command (hash=34101444)  
 System.Windows.Data Warning: 66 : BindingExpression (hash=41717140): Resolving source   
 System.Windows.Data Warning: 69 : BindingExpression (hash=41717140): Found data context element: Button (hash=34101444) (OK)  
 System.Windows.Data Warning: 77 : BindingExpression (hash=41717140): Activate with root item MainViewModel (hash=58169988)  
 System.Windows.Data Warning: 107 : BindingExpression (hash=41717140):  At level 0 - for MainViewModel.ReleaseCommand found accessor RuntimePropertyInfo(ReleaseCommand)  
 System.Windows.Data Warning: 103 : BindingExpression (hash=41717140): Replace item at level 0 with MainViewModel (hash=58169988), using accessor RuntimePropertyInfo(ReleaseCommand)  
 System.Windows.Data Warning: 100 : BindingExpression (hash=41717140): GetValue at level 0 from MainViewModel (hash=58169988) using RuntimePropertyInfo(ReleaseCommand): SimpleCommand`2 (hash=26754911)  
 System.Windows.Data Warning: 79 : BindingExpression (hash=41717140): TransferValue - got raw value SimpleCommand`2 (hash=26754911)  
 System.Windows.Data Warning: 88 : BindingExpression (hash=41717140): TransferValue - using final value SimpleCommand`2 (hash=26754911)  
 System.Windows.Data Warning: 55 : Created BindingExpression (hash=31382336) for Binding (hash=5171309)  
 System.Windows.Data Warning: 57 :  Path: 'ReleaseCommand'  
 System.Windows.Data Warning: 59 : BindingExpression (hash=31382336): Default mode resolved to OneWay  
 System.Windows.Data Warning: 60 : BindingExpression (hash=31382336): Default update trigger resolved to PropertyChanged  
 System.Windows.Data Warning: 61 : BindingExpression (hash=31382336): Attach to WpfControls.HoldButton.Command (hash=2919017)  
 System.Windows.Data Warning: 66 : BindingExpression (hash=31382336): Resolving source   
 System.Windows.Data Warning: 69 : BindingExpression (hash=31382336): Found data context element: HoldButton (hash=2919017) (OK)  
 System.Windows.Data Warning: 77 : BindingExpression (hash=31382336): Activate with root item MainViewModel (hash=58169988)  
 System.Windows.Data Warning: 106 : BindingExpression (hash=31382336):  At level 0 using cached accessor for MainViewModel.ReleaseCommand: RuntimePropertyInfo(ReleaseCommand)  
 System.Windows.Data Warning: 103 : BindingExpression (hash=31382336): Replace item at level 0 with MainViewModel (hash=58169988), using accessor RuntimePropertyInfo(ReleaseCommand)  
 System.Windows.Data Warning: 100 : BindingExpression (hash=31382336): GetValue at level 0 from MainViewModel (hash=58169988) using RuntimePropertyInfo(ReleaseCommand): SimpleCommand`2 (hash=26754911)  
 System.Windows.Data Warning: 79 : BindingExpression (hash=31382336): TransferValue - got raw value SimpleCommand`2 (hash=26754911)  
 System.Windows.Data Warning: 88 : BindingExpression (hash=31382336): TransferValue - using final value SimpleCommand`2 (hash=26754911)  

Dienstag, 21. Mai 2013

Nachtrag zu TextFormatting

Wie ich ja schon berichtete, macht es gegebenfalls Sinn, die TextOptions.TextFormattingMode Property auf Display zu setzen. Ich habe auch geschrieben, das es möglich ist diese Einstellung direkt beim Window zu setzen und somit alle darin liegenden Elemente direkt mitzusetzen.

Diese Einstellung sollte aber nur gesetzt werden, wenn es auch wirklich notwendig ist, sprich bei Texten mit einer Schriftgröße kleiner 14.

Montag, 20. Mai 2013

Wie funktioniert eigentlich ein RadioButton?

Wie schon in einem vorherigen Post beschrieben, möchte ich den WPF Expander um die GroupName Eigenschaft erweitern. Um dies zu tun, sollte man sich vorher einfach mal den RadioButton anschauen. Schließlich arbeitet dieses Control nach dem gewünschten Verhalten.

Der RadioButton ist generell eine spezielle Ausprägung des ToggleButtons. Der RadioButton bzw. ToggleButton ist eines der wenigen Controls, die über einen Namen Bezug zu anderen Controls haben. Besitzen zum Beispiel zwei RadioButtons auf einer View den gleich GroupName, kann immer nur ein Control angewählt werden.

Aber wie funktioniert das eigentlich? Was passiert hinter den Kulissen?
An sich ist die Umsetzung gar nicht so kompliziert. Jedes Control überwacht die IsChecked Property. Wird das Control ausgewählt (IsChecked wird auf true gesetzt), wird zwischen zwei Situationen unterschieden und es passiert folgendes:

  1. Es wurde beim aktuellen Control kein GroupName angegeben, so werden alle RadioButtons des selben Parents, die ebenfalls keinen GroupName besitzen und deselektiert (IsChecked wird auf false gesetzt).
    Im folgenden Beispiel kann immer nur einer der Buttons 1, 2 oder 3 gesetzt werden. Button 4 spielt hierbei keine Rolle!
     <StackPanel>  
         <RadioButton x:Name="button1">Button 1</RadioButton>  
         <RadioButton x:Name="button2">Button 2</RadioButton>  
         <RadioButton x:Name="button3">Button 3</RadioButton>  
     </StackPanel>  
     <RadioButton x:Name="button4">Button 4</RadioButton>
    

  2. Wird ein GroupName angegeben, werden alle RadioButtons geprüft, ob sie den gleichen Namen und die gleiche VisualRoot besitzen. Bei all diesen, werden die Elemente, die nicht angeklickt worden sind deselektiert.
    Im folgenden Beispiel bilden die RadioButtons 1, 2 und 4 eine Gruppe. Das StackPanel spielt dabei keine Rolle, ebenso wie der RadioButton 2.
  3.  <StackPanel>  
       <RadioButton x:Name="button1" GroupName="Group1">Button 1</RadioButton>  
       <RadioButton x:Name="button2">Button 2</RadioButton>  
       <RadioButton x:Name="button3" GroupName="Group1">Button 3</RadioButton>  
     </StackPanel>  
     <RadioButton x:Name="button4" GroupName="Group1">Button 4</RadioButton>  
    

Für die Umsetzung des erweiterten ExpanderControls heißt dies nun, dass das Control jede Änderung der IsExpanded, anstatt der IsChecked Property überwachen müsste und sich genauso wie der RadioButton verhalten sollte.

Samstag, 18. Mai 2013

Controls selber bauen

Ein eigenes WPF Control zu schreiben ist im Gegensatz zu anderen Technologien wie z.B. WinForms schnell und einfach gemacht.
Es muss lediglich eine Klasse erstellt werden, die von Control ableitet und es muss ein ControlTemplate dazu angelegt werden.
Fügt man im Visual Studio 2013 ein neues CustomControl hinzu, werden diese beiden Klassen automatisch erstellt. Außerdem wird, wenn noch nicht vorhanden, das Assembly Attribut ThemeInfoAttribute der AssemblyInfo.cs hinzugefügt. Das ControlTemplate wird vom Studio direkt in der /Themes/Generic.xaml Datei angelegt und kann dort beliebig gestaltet werden. Fügt man seinem Control eine DependencyProperty hinzu, kann diese direkt über TemplateBinding an das ControlTemplate gebunden werden.

Freitag, 17. Mai 2013

Text wird unscharf dargestellt

Texte werden in WPF standardmäßig leicht unscharf angezeigt. Dieses Problem hat Microsoft erkannt und mit dem .NET Framework 4.0 eine Lösung mitgebracht.

 TextOptions.TextFormattingMode="Display"  

Nutzt man diese Einstellung an einem Control, so wird der Text wieder klar und deutlich angezeigt. Setzt man diese Eigenschaft direkt am Window Objekt, so wirkt es sich auf alle Child Elemente aus.

 <Window TextOptions.TextFormattingMode="Display" />  

Expander mit GroupName

Mal wieder habe ich die Anforderung ein Outlook ähnliches Menü zu erstellen. Versucht man selber dieses Problem mit Standard WPF Controls zu lösen, würde man wahrscheinlich eine Liste von Expandern erstellen und übers ViewModel ausschließen, dass zwei Expander gleichzeitig geöffnet sein können.
Sucht man im Internet nach Lösungen, so kann man verschiede Accordion Controls herunterladen. So ein Accordion bekommt dann meist direkt beliebig viele Expander als Liste übergeben.
Aber nun mal eine andere Idee: Wie wäre es, das Expander Control um eine GroupName Property zu erweitern? Das Verhalten wäre dann genauso wie bei RadioButtons. Außerdem hätte dies den großen Vorteil, dass die Expander nicht nur untereinander bzw. nebeneinander angezeigt werden könnten, sondern irgendwo auf der View verteilt sein könnten. Ich werde dies demnächst mal umsetzen und davon berichten!

Donnerstag, 16. Mai 2013

TemplateBinding oder TemplatedParent

Nicht immer funktioniert die Markup-Extension TemplateBinding. Dafür gibt es verschiedene Gründe. Zum Beispiel beim Verwenden von Triggern kann es zu Problemen beim Binding kommen.

TemplateBinding ist eine vereinfache Form des Bindings und für ControlTemplates benutzt. Sollte TemplateBinding mal nicht funktionieren, so kann man auch einfach auf die RelativeSource TemplatedParent zugreifen. Dies sollte aber wirklich nur gemacht werden, wenn das TemplateBinding nicht funktioniert, weil dieses speziell zum Binden bei ControlTemplates optimiert wurde.

  Text="{TemplateBinding Title}"  
  Text="{Binding Title, RelativeSource={RelativeSource TemplatedParent}}"  

Mittwoch, 15. Mai 2013

WPF Controls mit falscher Culture

Generell werden alle WPF Controls mit der en-US Culture erstellt. Dadurch werden unter Umständen z.B. Zahlen oder Daten im falschen Format angezeigt. Dieses Problem ist bei Microsoft schon lange bekannt und kann mit einem Workaround umgangen werden.

Wird folgende Zeile Code ausgeführt, wird von allen FrameworkElement's die Sprache auf die aktuelle Culture gesetzt.


 FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));  

Damit hätte man schon mal alle FrameworkElement's abgedeckt.
Nutzt man nun vielleicht auch FrameworkContentElement's wie z.B. das Run Control so kann man auch explizit für diese die Sprache festlegen:

 FrameworkContentElement.LanguageProperty.OverrideMetadata(typeof(Run), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));  
           
Das Überschreiben dieser Metadaten darf allerdings nur einmal aufgerufen werden, ansonsten wird eine Exception ausgelöst. Am Besten ruft man diese Funktionalitäten in der App.xaml.cs in der OnStartup Methode auf.