Archive

Posts Tagged ‘WPF’

WPF Databinding in Verbindung mit einem Defaultbutton

March 10th, 2009 Bernd 2 comments

Vor kurzem ist ein Kollege von mir auf ein kleines Problem mit Databinding gestoßen. Und zwar in Verbindung mit einem Button dessen “IsDefault” Eigenschaft auf True gesetzt ist. Es wurde die Bindingquelle nicht aktualisiert.

Zur Verdeutlichung soll folgendes UI dienen:

Defaultbutton with databinding

Die TextBox für den Username ist über Databinding an die “Username” Eigenschaft des ViewModels angebunden. Bei dem Ok Button ist “IsDefault” auf True gesetzt. Mit Hilfe einiger Traces kann man im Ausgabefenster des Visual Studio sehen, was vom Benutzer eingegeben wurde.

...
public string Username
{
    get { return _userName; }
    set
    {
        _userName = value;
        Trace.WriteLine(string.Format("Username {0} entered.", _userName));
    }
}
...
private void OnOkCommand()
{
    Trace.WriteLine("Ok button pressed.");
    Trace.WriteLine(string.Format("Current username {0}", _userName));

}
...
Wenn nach der Eingabe eines Benutzernamens der Ok Button mit der Maus geklickt wird, klappt alles wie erwartet. Der Setter der Username Eigenschaft wird aufgerufen und dannach die Methode OnOkCommand() ausgeführt. Wird allerdings direkt nach der Eingabe die Enter Taste gedrückt, wird zwar OnOkCommand() ausgeführt, aber nicht der Username Setter. Was ist hier los?

Bei einer TextBox die per Databinding angebunden ist, wird die Quelle per Default bei Verlust des Fokus aktualisiert. D.h. die Eigenschaft “UpdateSourceTrigger” des Bindings hat den Wert “LostFocus”. Scheinbar wird beim Auslösen eines Defaultbuttons der Fokus nicht auf diesen Button gesetzt. Es wird dann zwar das Command ausgeführt welches mit dem Button verknüpft ist, aber die Quelle der TextBox wurde nicht aktualisiert.

Es gibt mehrere Möglichkeiten das Problem zu lösen.

  1. Durch Setzen der “UpdateSourceTrigger” Eigenschaft am Binding auf “PropertyChanged”. Dadurch wird die Quelle bei jeder Veränderung aktualisiert.
  2. Durch manuelles Setzen des Fokus im Click-Eventhandler des Buttons
  3. Durch manuelles Aktualisieren des Bindings im Click-Eventhandler des Buttons

Bei Variante zwei muss der entsprechende Code in die Code-Behind Datei der View verlagert werden, da ein direkter Zugriff auf das Button Control notwendig ist:

private void OnOkButtonClick(object sender, RoutedEventArgs e)
{
    FocusManager.SetFocusedElement(this, (Button)sender);
}

Den Code für die dritte Lösung könnte man (technisch) auch ins ViewModel packen. Gefällt mir aber nicht, da ich im ViewModel keine Kenntnis von UI Controls haben möchte. Deshalb kommt auch hier die Code-Behind Datei zu Zuge:

private void OnOkButtonClick(object sender, RoutedEventArgs e)
{
    TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
    if( null != focusedTextBox )
    {
        BindingExpression bindingExpression = focusedTextBox.GetBindingExpression(TextBox.TextProperty);
        if (null != bindingExpression)
        {
            bindingExpression.UpdateSource();
        }
    }
}

Bisher gefällt mir die zweite Variante am Besten. Mir sind auch noch keine Nachteile oder Probleme aufgefallen. Den “richtigen” Code, der beim Auslösen des Defaultbuttons ausgeführt werden soll, kann man im ViewModel lassen. Das Command welches mit dem Button verknüpft ist, wird erst nach dem Click Event ausgeführt.

Weiss zufällig jemand, ob es diesen Effekt mit dem Defaultbutton und LostFocus auch unter WinForms gibt?

Categories: Softwareentwicklung Tags:

Tools für WPF und Silverlight Entwickler

February 14th, 2009 Bernd No comments

Ich sehe schon die Gedanken in euren Köpfen : “Oh je, schon wieder einer der ‘ne Toolliste postet”, “Nicht schon wieder. Scott Hanselman macht das doch schon seit Jahren”. Ihr habt ja recht – trotzdem tue ich es, in der Hoffnung Ergänzungsvorschläge zu bekommen.

  • KAXAML – Ein super XAML Editor, mit dem mal schnell mal was ausprobieren kann ohne gleich das Visual Studio zu starten. Er bietet ein geteiltes Fenster, so dass der XAML Code und das gerenderte Ergebnis auf einem Blick zu sehen sind. Syntax-Highlighting und Codevervollständigung gehören auch zum Funktionsumfang. In der aktuellen Beta Version wird auch Silverlight 2 unterstützt.
  • Snoop – Snoop könnte man als “Visual Debugger” bezeichnen. Das Tool kann sich zur Laufzeit an eine WPF Anwendung “attachen” und zeigt dann den Visual Tree derselben an. Dort lassen sich z.B. Properties verändern und Routed Events verfolgen. Wie man Databindingprobleme mit Snoop findet, habe ich hier beschrieben.
  • ShowMeTheTemplate – Zeigt die Templates (ControlTemplate, DataTemplate und ItemsPanelTemplate) für alle mit WPF ausgelieferten Controls an. Auf MSDN gibt es auch Beispiele für ControlTemplates: “Control Template Examples”.
  • Crack.Net – Crack.Net ist zwar kein Tool das speziell für WPF entwickelt wurde, es wurde aber mit WPF entwickelt. Ähnlich wie Snoop “attached” sich Crack.Net an eine laufende .Net Anwendung. Es ist möglich, den managed Heap der Applikation zu untersuchen, Objekte können direkt verändert werden und mit IronPython ist sogar Scripting Support vorhanden. Der Entwickler Josh Smith hat hier konsequent das M-V-VM Pattern verwendet. Ein Blick auf den Sourcecode kann hier nicht schaden.
  • Performance Profiling Tools for WPF – Sammlung von fünf Tools von Microsoft, die helfen sollen Performanceprobleme bei WPF Anwendungen zu finden bzw. zu vermeiden. Mit dem enthaltenen Visual Profiler lässt sich auch mal schnell ein Performancevergleich zwischen zwei Rechnern machen.
  • Mole – Ein Visualizer für Visual Studio mit dem während des Debuggens nicht nur sämtliche Eigenschaften eines Objekts editiert werden können, sondern bei WPF Anwendungen auch der Logical/Visual Tree betrachtet werden kann.
  • Dr. WPF’s Code Snippets – sind zwar kein Tool, aber auch sehr hilfreich. Momentan sind Snippets für Dependency Properties, Routed Commands und Routed Events enthalten. Wer selber Code Snippets schreibt, findet mit dem Snippet Editor einen Helfer.
  • Microsoft Expression Blend – Das Design Tool für WPF und Silverlight (mit SP1 für Expression Blend). Bisher habe ich es nur rudimentär verwendet. Ich glaube aber, dass es auch für Entwickler sehr hilfreich ist, dieses Tool zumindest in den Grundzügen zu beherrschen.

Tools können sehr hilfreich sein, sind aber natürlich kein Allheilmittel! Bei vielen Problemen kann es auch helfen, einfach einen Kollegen über den Code schauen zu lassen.

Welche Tools verwendet ihr bei der WPF Entwicklung?

Categories: Softwareentwicklung Tags:

The type reference cannot find a public type named ‘InsertYourType’

January 31st, 2009 Bernd No comments

Heute hatte ich eine interessante Diskussion mit Norbert auf .NET GUI. Unter anderem habe ich gelernt, dass es Probleme geben kann, wenn beim Import eines lokalen Namespace in ein XAML File der Assemblyname mit angegeben wird. Hier ein Beispiel:

In der Assembly DataTemplateDemo gibt es eine Klasse Data:

namespace DatatemplateDemo
{
    public class Data
    {
        private string _name;

        public Data(string name)
        {
            _name = name;
        }

        public string Name
        {
            get { return _name; }
        }
    }
}

Im UI wird ein DataTemplate für diesen Typ definiert:

<Window x:Class="DatatemplateDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DatatemplateDemo;assembly=DatatemplateDemo"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>
        <DataTemplate x:Key="DataTemplate_Data" DataType="{x:Type local:Data}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="2" Text="Name: "/>
                <TextBlock Margin="2" Text="{Binding Path=Name}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <ListBox x:Name="_listBox" ItemTemplate="{StaticResource DataTemplate_Data}"/>
    </Grid>
</Window>

Wenn man nun versucht diesen Code zu übersetzen wird man mit dieser Fehlermeldung konfrontiert:

error MC3066: The type reference cannot find a public type named ‘Data’. Line 8 Position 80.

Nach einigem Rumprobieren hat sich herausgestellt, dass die Übersetzung wunderbar klappt, wenn bei der Deklaration des XML Namespace die Assembly weggelassen wird:

<Window x:Class="DatatemplateDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DatatemplateDemo"
    Title="Window1" Height="300" Width="300">

    ...

</Window>

Der Fehler tritt nicht nur mit DataTemplates auf, sondern z.B. auch wenn man einen Style definiert und der TargetType entsprechend angegeben wird.

Ich merke mir: wenn ein Namespace aus einem lokalen Assembly in ein XAML File mit xmlns importiert wird, gebe ich den “assembly” Parameter NICHT mit an! Vielleicht erspart die Info ja dem einen oder anderen die Fehlersuche. Ach ja, das Intellisense des Visual Studio macht es automatisch so und gibt den “assembly” Parameter nicht mit an, wenn der importierte CLR Namespace im gleichen Assembly liegt.

Categories: Softwareentwicklung Tags:

Artikel über M-V-VM im MSDN Magazin

January 28th, 2009 Bernd No comments

Josh Smith hat für die Februar Ausgabe des englischen MSDN Magazins einen Artikel über das M-V-VM (Model-View-ViewModel) Pattern geschrieben. Dieses, mittlerweile sehr populäre, UI Pattern für WPF wurde von John Gossman geprägt und findet auch in der Architektur von Expression Blend Anwendung. M-V-VM bietet Richtlinien, einen Leitfaden um testbare WPF Anwendungen mit einer sauberen Trennung von Daten, Businesslogik und UI zu entwickeln.

Categories: Neuigkeiten, Softwareentwicklung Tags: ,

Databinding und Validierung in WPF – erste Vorbereitungen

January 27th, 2009 Bernd No comments

Databinding in WPF ist ein sehr weites Feld. Nicht zuletzt aufgrund der Vielseitigkeit und Mächtigkeit spielt Databinding in WPF eine sehr wichtige Rolle. Einige wichtige Merkmale sind:

  • Bindings können sowohl in XAML als auch im Code erzeugt werden.
  • Konvertierung von Daten. Über sog. Value Converter ist es möglich, die Daten die von der Quelle kommen anzupassen bzw. zu verändern.
  • Data Templates – mit Data Templates kann man die Visualisierung der Daten festlegen.
  • Data Validation – wie unterstützt mich WPF bei der Validierung der vom Benutzer eingegebenen Daten?

Um Data Validation soll es in den nächsten Posts gehen. Ich kann leider keine Weisheiten zu dem Thema bieten, sondern möchte vielmehr meinen Lernprozess (mit)teilen. In mehreren Postings nähere ich mich Schritt für Schritt dem Thema. Danach wissen wir hoffentlich alle ein bisschen mehr über Databinding und Data Validation mit WPF.

Um erst mal die Grundlagen zu verstehen, fange ich ganz klein an. So klein, dass es mir schon fast zu klein vorkommt… aber nur fast. So sieht das Fenster der Testanwendung aus:

Das zugehörige XAML ist auch ganz einfach:

<Window x:Class="DataValidationWPF.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataValidationWPF"
    Title="Data validation with WPF"
    Height="130" Width="300">

    <Window.Resources>
        <local:CompactDisc x:Key="cd" />
    </Window.Resources>

    <StackPanel>
        <TextBlock Text="Please enter a value between 1980 and 2009"
                   Margin="10"/>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Year:" Margin="10" />
            <TextBox ToolTip="Please enter year of release"
                     Width="60"
                     Margin="10"
                     Text="{Binding Source={StaticResource cd},
                            Path=Year,
                            Mode=TwoWay,
                            UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
    </StackPanel>
</Window>

Als Datenklasse muss wieder die CompactDisc herhalten. Diesmal total abgespeckt in ihrer Minimalausführung:

public class CompactDisc
{
    private int _year;

    public int Year
    {
        get
        {
            Debug.WriteLine("Get year: " + _year);
            return _year;
        }

        set
        {
            _year = value;
            Debug.WriteLine("Set year to: " + _year);
        }
    }
}

Es wird also die “Year” Eigenschaft einer Instanz der CompactDisc Klasse an die “Text” Eigenschaft einer TextBox im UI gebunden. Als Mode für die Bindung habe ich “TwoWay” angegeben. D.h. der Datenaustauch erfolgt in beide Richtungen. Nach dem Starten der Applikation wird direkt der Wert ‘0’ in der TextBox angezeigt. 0 ist der Default Wert für den Typ int. Das TwoWay Binding funktioniert also. Selbst bei diesem einfachen Beispiel passiert schon einiges unter der Databinding Haube. Damit überhaupt ein Integer Wert in einer TextBox angezeigt werden kann, durchläuft dieser eine implizite Konvertierung zu einem String.

Zu Diagnosezwecken wurden im Setter und Getter der Eigenschaft jeweils eine Debugausgabe eingebaut. Damit kann man dann im Output Fenster des Visual Studios sehen, ob überhaupt etwas geändert wurde. Um auch wirklich alle Änderungen, die im UI in der TextBox gemacht werden mitzubekommen, habe ich die Eigenschaft “UpdateSourceTrigger” auf den Wert “PropertyChanged” gesetzt. Das bewirkt, dass die Quelle des Bindings (die Year Eigenschaft) aktualisiert wird, sobald in der TextBox editiert wird. Los geht’s. Applikation im Debug Modus aus dem Visual Studio starten und die Debug Ausgaben im Auge behalten.

Sobald die ‘0’ aus der TextBox gelöscht wird, taucht auch schon die erste Fehlermeldung im Output Fenster auf:

A first chance exception of type ‘System.FormatException’ occurred in mscorlib.dll

A first chance exception of type ‘System.Reflection.TargetInvocationException’ occurred in mscorlib.dll

A first chance exception of type ‘System.FormatException’ occurred in mscorlib.dll

System.Windows.Data Error: 7 : ConvertBack cannot convert value ” (type ‘String’). BindingExpression:Path=Year; DataItem=’CompactDisc’ (HashCode=59835590); target element is ‘TextBox’ (Name=”); target property is ‘Text’ (type ‘String’) FormatException:’System.FormatException: Input string was not in a correct format.

   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)

   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)

   at System.String.System.IConvertible.ToInt32(IFormatProvider provider)

   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)

   at MS.Internal.Data.SystemConvertConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)

   at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)’

The thread 0xf44 has exited with code 0 (0×0).

 

Bei der impliziten Konvertierung gibt es eine FormatException. Die Anwendung läuft trotzdem ohne Probleme weiter. Offensichtlich wird die Exception vom Databinding System geschluckt. Man sieht auch, dass der Setter der Year Eigenschaft nicht aufgerufen wird. Bisher bekommen wir also in unserem Code nichts mit und haben dadurch auch nicht die Möglichkeit dem Benutzer einen entsprechenden Hinweis zu geben (z.B. eine Meldung “Year must not be empty”). Aber hier soll uns ja Data Validation weiterhelfen. Wird eine gültige Zahl in die TextBox eingegeben läuft alles nach Plan, der Setter wird aufgerufen und das Datenobjekt ist auf Stand.

So, das war’s für den ersten Teil. Man sieht, dass selbst bei so einfachen Anwendungen mit Databinding eine Menge passiert. Ich bin gespannt, was WPF bzgl. Validierung für uns Entwickler bereithält.

Categories: Softwareentwicklung Tags: