Biggle's Blog

Web- und Software Development

by Mario Priebe

Toggle – WindowState Maximize / Normal

Wenn man in seiner WPF Applikation eigene Application-Icons zum minimieren, maximieren, schließen, usw. einsetzt, muss man erstens das Verhalten und zweitens das Aussehen selbst implementieren.

In diesem Snippet möchte ich zeigen, wie man zwischen den angezeigten Grafiken wechselt, wenn der entsprechende Zustand eintrifft.

Ist die Applikation im maximierten Zustand soll das Icon für “minimieren” angezeigt werden und wenn der Zustand der Applikation im normalen Zustand ist, anders herum.

Dies geht recht einfach mit einem Style und einem DataTrigger:

    <Style TargetType="Image" x:Key="ToggleIcon">
        <Setter Property="Source" Value="{DynamicResource MaximizeIcon}" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding WindowState, ElementName=TheNameOfMyWindow}" Value="Maximized">
                <Setter Property="Source" Value="{DynamicResource NormalIcon}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>

Die DynamicResources in dem Beispiel sind gebunden an eine ImageSource:

    <ImageSource x:Key="MaximizeIcon">/MyApp.UI.Resources;component/Images/icon_maximize.png</ImageSource>
    <ImageSource x:Key="NormalIcon">/MyApp.UI.Resources;component/Images/icon_normal.png</ImageSource>

Die Verwendung ist dann nur noch Formsache…

<Image Stretch="None" Style="{DynamicResource ToggleIcon}" HorizontalAlignment="Right" VerticalAlignment="Top" Cursor="Hand" 
ToolTip="{x:Static Resources:Resources.maximize}" MouseLeftButtonDown="ToggleMaxNormal" />
private void ToggleMaxNormal(object sender, MouseButtonEventArgs e)
{
    switch (WindowState)
    {
        case WindowState.Normal:
            WindowState = WindowState.Maximized;
            break;
        case WindowState.Maximized:
            WindowState = WindowState.Normal;
            break;
    }
}

Viel Spaß beim entwickeln : )

by Mario Priebe

WPF und Windows Forms – Die Wahl zwischen den Windows GUI Technologien.

Wenn wir Entwickler für Windows grafische Benutzeroberflächen (GUIs) entwickeln möchten, haben wir die Wahl zwischen Windows Forms und WPF.

Jede dieser Technologien haben ihre Vor- und Nachteile. In diesem Artikel möchte ich auf die Hauptunterschiede beider Technologien eingehen und bei der Entscheidung helfen, welche der beiden für den Einsatzzweck vom Vorteil ist.

Windows Forms

Windows Forms ist die ältere der beiden Technologien und ist die Basis für die meisten (älteren) Windows Applikationen. Die meisten Entwickler haben bereits umfängliche Erfahrungen mit dieser Technologie machen können.

Mit Windows Forms können Formulare in unterschiedlichen Größen und Formen von Fenstern erstellt werden und haben im Prinzip immer die selbe Anordnung für Container, Menüs und Struktur. Einen großen Spielraum für die Umsetzung von grafisch, anspruchsvollen Anforderungen hat man leider nicht, bzw. ist dies sehr schwierig mit Windows Forms umzusetzen, machbar ist es aber dennoch.

Windows Forms hat aber eine sehr gute Unterstützung für multilinguale Anwendungen. Wenn man also eine mehrsprachige Business-Applikation, bei der Usability und Optik nicht so wichtig sind, erstellen möchte, könnte man Windows Forms einsetzen. Man sollte sich aber dennoch den folgenden WPF-Teil anschauen.

Die Navigation innerhalb einer Windows Forms-Anwendung ermöglicht ein Wechseln innerhalb verschiedener Formulare im Single Document Interface Modus (SDI, der Benutzer sieht immer nur ein Fenster mit Informationen) und im Multi Document Interface Modus (MDI, der Benutzer kann mehrere Formulare zeitgleich geöffnet haben).

Für ein entsprechendes UI Pattern um eine Trennung zwischen Code und Design zu erzielen, bekommt man in Windows Forms von Hause aus leider nichts mitgeliefert. Hier kann man sich aber das MVP (Model View Presenter) Pattern anschauen. Es gibt auch ein Framework namens MVC#, welches einem dabei unterstützt. Aber auch der MVVM Ansatz kann beispielsweise über das Windows Application Framework (WAF) mittels Windows Forms Adapter nach implementiert werden.

WPF

WPF steht für Windows Presentation Foundation und ist der direkte Nachfolger von Windows Forms. Der wichtigste Unterschied zwischen WPF und Windows Forms ist, dass in WPF eine klare Trennung zwischen Code und Design herrscht, sprich UI und Logik können getrennt voneinander entwickelt werden.

Der Code kann dabei mit allen .NET basierenden Sprachen entwickelt werden. Die Beschreibungssprache für die UI nennt sich XAML und steht für Extensible Application Markup Language. Wichtig dabei ist, dass bei der Entwicklung der UI, dem Designer keinerlei Grenzen gesetzt sind. Alles ist möglich!

WPF-Applikationen werden in drei verschiedenen Typen unterteilt, welche für verschiedene Aufgaben zuständig sind. Windows-Applikation, Navigations-Applikation und XAML-Browser-Applikation (XBAP).

Windows-Applikation

Die Windows-Applikation ist Fensterbasierend und Windows Forms am ähnlichsten. Man erstellt SDI und MDI Applikationen und verschiedene Formulare können über einen Menü-Link direkt angewählt und geöffnet werden. Für Navigation oder Historien ist von Hause aus nichts implementiert. Man muss hier selbst Hand anlegen.

Navigations-Applikation

Unterstützt die Entwicklung von seitenbasierten Anwendungen und ist dem der Webentwicklung sehr ähnlich. Man verfügt über Vor- und Zurück Funktionalitäten und der automatisierten Speicherung der aufgerufenen Seiten in einem Journal. Im Gegensatz zur Webentwicklung, muss die Applikation kompiliert werden und läuft unter Windows, man hat aber vollen Zugriff auf die Ressourcen des Betriebssystems, wie das File-System und/oder der Registry.

XAML-Browser-Applikation

XBAPs verfügen, ähnlich einer Navigations-Applikation, über die Navigations- und Journalfunktionalitäten. Eine XBAP-Applikation kann auf einem Server oder einer Website veröffentlicht werden und werden bei der Instanziierung heruntergeladen. Mit einer XBAP hat man beschränken Zugriff auf die Ressourcen des Computers und man kann weder auf das Filesystem, noch auf die Registry zugreifen.

Die Wahl des entsprechenden Applikations-Typen hängt von verschiedenen Faktoren ab. Die beiden wichtigsten wären, User Experience und spezielle Anforderungen an die Applikation.

User Experience

Möchte man beispielsweise eine seitenbasierte Anwendung oder einen Wizard erstellen, sollte die Wahl auf den Navigations-Typen, oder je nach Anforderung (Zugriff) auf den XBAP-Typen fallen.

Für das Deployment macht sich eine XBAP Applikation recht komfortabel. Man veröffentlicht die Applikation auf einen Webserver und via Hyperlink kann die Applikation gestartet werden. Applikations-Update Mechanismen sind innerhalb einer XBAP-Applikation ebenfalls recht gut umgesetzt.

Anforderungen

Wenn die Anwendung es erfordert, dass man auf das Filesystem zugreifen, oder Einträge in der Registry verwalten muss, sollte man auf alle Fälle eine der beiden ersten Typen wählen.

Egal für welchen Typen man sich entscheidet. WPF unterstützt für alle dieser Typen Möglichkeiten für das Theming und Styling. Auch für Lokalisierung hat man entsprechenden Support, jedoch lange nicht diesen, wie man das von Windows Forms her gewohnt ist. Hier bedarf es eine etwas längere Entwicklungszeit.

Interoperabilität

In WPF fehlen leider auch einige Controls, wie beispielsweise die FileDialog-Klassen für OpenFileDialog und SaveFileDialog. Man ist aber in der Lage, die Windows Forms Assembly (System.Windows.Forms) in einer WPF Applikation einzubinden und die o.g. Klassen stehen einem danach zur Verfügung.

Es ist aber nicht ratsam diesen Weg zu gehen, da es hier zu Namenskonflikten mit Klassen in WPF kommen kann. Empfehlenswert ist der Zugriff über den Microsoft.Win32 Namespace in der PresentationFramework Assembly.

Aber auch den ColorDialog, den FontDialog und anderes findet man vergebens in WPF. In der folgenden Übersicht Windows Forms Controls und  äquivalente WPF Controls bekommt man eine Vergleichstabelle, welche Forms-Controls in WPF nicht vorhanden sind. Auch einige Properties unterscheiden sich, hier sollte man sich Windows Forms and WPF Property Mapping zum Vergleich anschauen.

Nun, wenn man sich für eine Technologie entschieden hat, kann man dennoch beide miteinander kombinieren. Das heißt, man hat entsprechende Controls, um die jeweils andere Technologie einzubinden. In Windows Forms ist das das ElementHost-Control und in WPF das WindowsFormsHost-Control. Einige HowTos zum Verwenden der Controls findet man direkt im MSDN (ElementHost, WindowsFormsHost).

Also, welche Faktoren spielen bei der Wahl zwischen Windows Forms und WPF eine Rolle? Hier ein paar Fragen, um die richtige Technologie, oder den Technologietypen zu ermitteln.

  1. Wie stark sind die Fähigkeiten der Entwickler in der jeweiligen Technologie?
  2. Können durch den Entwickler fehlenden Forms-Controls ersetzt werden?
  3. Verfügt man über Grafik-, oder UI-Designer?
  4. Welche Unterstützung von Styling und Theming wird in der Applikation erwartet?
  5. Soll die Applikation individuell gestaltet sein?
  6. Benötigt die Applikation volle Rechte auf das Betriebssystem?
  7. Wird großer Wert auf eine Seitennavigation ähnlich einer Website gelegt?
  8. Muss die Applikation mehrsprachig sein?

Fazit

Mit WPF ist man in der Lage, grafisch hochwertige Applikationen zu entwickeln. Es fehlen zwar einige Controls, jedoch ist man in der Lage einen passenden Ersatz zu finden, bzw. in der Lage diesen zu entwickeln. Weiterhin kann durch die Trennung von UI und Logik, die Applikation in einem Team aus Developer und Designer entwickelt werden. Durch die Einteilung in verschiedenen Typen, kann man den richtigen Typen für die jeweilige Aufgabe finden und erhält Unterstützung für nahezu jede Anforderung vom Framework.

Ich hoffe dieser kleine Überblick von Vor- und Nachteilen der beiden GUI Technologien hilft dabei, sich für die richtige Technologie zu entscheiden.

Viel Spaß beim entwickeln : )

by Mario Priebe

DataBindings debuggen – WPF

Beim DataBinding ist es nicht immer einfach festzustellen, warum denn nun ein Wert nicht angezeigt wird. Die Fehlersuche zeigt sich mitunter, gerade auch in immer größer werdenden Projekten, als äußerst schwierig.

Das liegt daran, das im DataBinding keine Exception geworfen werden, wenn hier ein Element, eine Property, oder ein Path nicht gefunden wird.

Ein paar kleine Tricks können Abhilfe und für ein stressfreies Wochenende sorgen.

Tipp 1: Ausgabefenster

Lässt man sich beim Debuggen das Ausgabefenster von Visual Studio anzeigen, kann man diesem schon wertvolle Informationen entlocken.

Eine Fehlermeldung könnte z.B. wie folgt aussehen:

System.Windows.Data Error: 40 : BindingExpression path error: 'Mesage' property not found on 'object' ''MainWindow' (Name='')'. BindingExpression:Path=Mesage; DataItem='MainWindow' (Name=''); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')

Hier finde ich dann, das ich mich bei der Property “Message” verschrieben habe (“Mesage”).

Um das Ausgabefenster anzuzeigen, geht man unter Ansicht > Ausgabe oder zeigt dieses über die Tastenkombination CTRL+W+O an. Man muss hier aber darauf achten, dass man sich bereits im Debugging befindet.

Vorher sollte man überprüfen ob die Einstellungen in Visual Studio richtig sind.

Dazu geht man nach Extras > Optionen > Debugging > Ausgabefenster und wählt hier unter WPF-Ablaufverfolgung im DropDown “DataBinding” die Option “Alle

image

 

Tipp 2: PresentationTraceSources

Wem die Informationen in der Ausgabe nicht ausreichen, kann sein Control um weitere, ausführlichere Informationen erweitern.

Hierzu referenziert man auf den Namespace System.Diagnostics aus der Assembly WindowsBase

image

 

Anschließend erweitert man sein Binding um das AttachedProperty TraceLevel aus der Klasse PresentationTraceSources und dieses um den Wert High:

<TextBox Text="{Binding Mesage, debugging:PresentationTraceSources.TraceLevel=High}"  />

Hier bekommt man dann richtig viele Informationen zu einem Binding, die ich hier auszugsweise nur als Grafik zeigen möchte:

image

Mögliche Werte in TraceLevel können sein: High, Low, Medium und None.

Tipp 3: IValueConverter

Nun, es kommt vor, dass obwohl die Properties richtig definiert sind, immer noch nicht das angezeigt wird, was man eigentlich erwartet. Hier kommt die dritte Möglichkeit ins Spiel.

Es wäre doch toll, wenn man irgendwie einen BreakPoint irgendwo setzen könnte um nach gewohnter Manier zu debuggen. Ja, auch das ist möglich.

Hierzu definiert man sich einen Converter, der lediglich den Value durchreicht:

public class DebuggingConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}

Nun referenziert man den Namespace zum Converter und in den Resources des Elements wird der Converter bekannt gemacht:

xmlns:converter="clr-namespace:Debugging"

<Grid.Resources>
    <converter:DebuggingConverter x:Key="debugger" />
</Grid.Resources>

Im Binding wird der Converter dann noch definiert

<TextBox Text="{Binding Message, Converter={StaticResource debugger}}"  />

Jetzt setze ich meine BreakPoints im Convert oder beim TwoWay-Binding auch im ConvertBack und kann mir den Wert genauer anschauen.

 

Viel Spaß beim entwickeln : )

by Mario Priebe

Gemeinsamen Validation Style für Controls – WPF

Um einen gemeinsamen Style für Validierungsfehler in der WPF zu verwenden, definiert man einen Style für den TargetType Control und vergibt diesen einen Key.

<Style x:Key="validationTriggerBase" TargetType="Control">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent   }" />
        </Trigger>
    </Style.Triggers>
</Style>

Im Anschluss, kann man einzelne Controls diesen Style zuweisen:

<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource validationTriggerBase}" />

image

<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource validationTriggerBase}" />

image

Nicht vergessen, das bindende Control muss ValidatesOnDataErrors=True aufrufen.

<StackPanel Width="250" HorizontalAlignment="Center" DataContext="{StaticResource Person}">
    <TextBox Text="{Binding Name, ValidatesOnDataErrors=True, 
        UpdateSourceTrigger=PropertyChanged}" />

    <DatePicker SelectedDate="{Binding Birthday, StringFormat=dd.mm.yyyy, 
        ValidatesOnDataErrors=True,
        UpdateSourceTrigger=PropertyChanged, Converter={StaticResource conv}}" />
</StackPanel>

Viel Spaß beim entwickeln : )

by Mario Priebe

Enumeration in einer ComboBox darstellen – WPF Quicky

Um eine Aufzählung unter WPF in einer ComboBox darzustellen, brauch man nicht wirklich viel.

Mit der Methode GetValues() aus der Klasse Enum, bekommt man einen Array mit den Konstanten aus der Aufzählung zurück.

<ComboBox ItemsSource="{Binding AutoCompleteFilters}" SelectedItem="{Binding SelectedFilterMode}" />

public List<AutoCompleteFilterMode> AutoCompleteFilters { get; set; }

private void SetAutoCompleteFilters()
{
    Array values = Enum.GetValues((typeof(AutoCompleteFilterMode)));

    AutoCompleteFilters = new List<AutoCompleteFilterMode>();
    foreach (AutoCompleteFilterMode value in values)
    {
        AutoCompleteFilters.Add(value);
    }
}

image

Viel Spaß beim entwickeln : )

by Mario Priebe

Cookies in einer WPF Applikation

Möchte man beispielsweise Authentifikationsinformationen während einer WPF-Client-Sitzung speichern, kann man hier an der Klasse Application die Methoden SetCookie und GetCookie verwenden.

Folgendes einfaches Beispiel zeigt wie die Cookieinformationen abgelegt und gelesen werden können:

XAML

1
2
3
4
5
6
7
8
9
<StackPanel>
    <TextBox Name="tbUserName" />
    <TextBox Name="tbPassword" />        
    <Button Content="SetCookie" Click="Button_Click" />   
 
    <Button Content="GetCookie" Click="Button_Click_1" /> 
    <TextBlock Name="tbGetUserName" />        
    <TextBlock Name="tbGetPassword" />
</StackPanel>

C#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public partial class MainWindow : Window
{
    Uri cookiePath = new Uri(System.Environment.GetEnvironmentVariable("tmp"));
 
    public MainWindow()
    {
        InitializeComponent();
    }
 
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        SetCookie(cookiePath, tbUserName.Text, tbPassword.Text);
    }
 
 
    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        string c = GetCookie(cookiePath);
 
        if (!String.IsNullOrEmpty(c))
        {
            var auth = GetAuthentification(c);
            tbGetUserName.Text = auth["Username"];
            tbGetPassword.Text = auth["Password"];
        }
    }
 
 
    public static void SetCookie(Uri uri, String name, String value)
    {
        String cookie = String.Format("Username={0}Password={1}", name, value);
        Application.SetCookie(uri, cookie);
    }
 
    public static String GetCookie(Uri uri)
    {
        string c = String.Empty;
        try
        {
            c = Application.GetCookie(uri);
        }
        catch (Exception)
        {
            //set new cookie here
        }
 
        return c;
    }
 
    private Dictionary<string, string> GetAuthentification(string cookie)
    {
        var username = cookie.Substring(0, cookie.IndexOf("Password=")).Replace("Username=", string.Empty);
        var password = cookie.Substring(cookie.IndexOf("Password=")).Replace("Password=", string.Empty);
 
        Dictionary<string, string> auth = new Dictionary<string, string>();
        auth.Add("Username", username);
        auth.Add("Password", password);
 
        return auth;
    }
}

Viel Spaß beim entwickeln : )