RubiWanKenubis Blog

Mittwoch, 24. November 2010

ASP.NET MVC in der Praxis auf der VSONE
Wie in jedem Jahr findet vom16.-17.02.2011 in gewohnt angenehmer Atmosphäre die Entwicklerkonferenz VSONE statt. Ich freu mich dieses Jahr wieder als Sprecher dabei zu sein. Und nicht nur das, denn ich werde sicher auch als Teilnehmer so viele Sessions wie möglich mitnehmen

Weitere Infos unter www.vsone.de

ASP.NET MVC 2 Html.DropDownList Problem mit SelectListItem Selected Property
Ja da schau her,
da könnte man meinen man baut sich in der View eine DropDownList, setzt brav die Werte über Model Bindeing mit einer Liste von SelectListItem Objekten im Gepäck und wenn die Selected Eigenschaft gesetzt ist dann haut das auch hin. Aber nix da. Zumindest dann nicht, wenn man der   <%=Html.DropDownList("Owners", Model.Owners )%> den selben Namen gibt wie der Liste von SelectListItes dann haut das gar nicht hin. Es reicht aber einfach   <%=Html.DropDownList("Besitzer", Model.Owners )%> zu verwenden.

Scroll Performance ListView und wohl auch andere ItemControls
Hallo und kreuzbirnbaumundhollerstauern,
hab ich geflucht als ich schon bei wenigen Datensätzen mit 30 Spalten in einer ListView beim Scrollen das kalte Grausen bekommen habe.
Dann hab ich mich auf Artikel gestürzt und rausgefunden was Virtualisierung ist (Artikel).
Hier nun meine nicht empirische Zusammenfassung: Es haut hin wenn die ListeView in einem Panel steckt, dass die Virtualisierung kann und inder ListView der Modus auf VirtualizingStackPanel.VirtualizationMode="Recycling" steht.
Wer genau wissen mag welche Panels die Virtualisierung können mag es einfach mal nachschlagen oder das DockPanel in dem Beispiel  (ganzes Projekt gezippt im Anhang) unten austauschen in ein StackPanel oder einen ScrollViewer
Hier mal ein Stück Code mit schnellem Scrollen:

<Window x:Class="PerformanceScrollviewer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <DockPanel><!-- Hier mal ein Stackpanel rein dann ist die Perfomance ... würg -->
        <ListView Name="ListViewPersonen"  VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="50"/>
                          ......
                            <ColumnDefinition Width="50"/>
                           
                        </Grid.ColumnDefinitions>
                        <TextBox Grid.Column="0" Text="{Binding Path=Vorname}"></TextBox>
                        <TextBox Grid.Column="1" Text="{Binding Path=Nachname}"></TextBox>
                        <TextBox Grid.Column="2" Text="{Binding Path=Alter}"></TextBox>
                        <TextBox Grid.Column="3" Text="{Binding Path=PLZ}"></TextBox>
                        <TextBox Grid.Column="4" Text="{Binding Path=Ort}"></TextBox>
                        <TextBox Grid.Column="5" Text="{Binding Path=Strasse}"></TextBox>
                        <TextBox Grid.Column="4" Text="{Binding Path=Hausnummer}"></TextBox>
                        <TextBox Grid.Column="7" Text="{Binding Path=Zusatz}"></TextBox>
                ......
                       
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </DockPanel>
</Window>

Viel Spass beim rumprobieren
R

Jeder, der sich ernsthaft mit WPF beschäftigt kommt an einer Validierung von Eingaben des Benutzers kaum vorbei. Einfache Validierung z.B. einer einfachen datengebundenen TextBox auf eine Ganzzahl oder eine Range ist in WPF mit Bordmitteln in meinen Augen ziemlich sexy zu lösen. Einfach im Binding eine ValidationRule angeben (muss sich von ValidationRule ableiten) und ab geht es. Wie eine einfache Validierung hinhaut ist für diesen Eintrag als KnowHow vorausgesetzt.

Sobal man aber in die voerst missliche Lage gerät, dass man 2 Controls in Abhängigkeit voneinander validiern muss, reichen die Bordmittel von WPF nicht mehr aus. In folgendem Beispiel werden nun 2 TextBoxes mit den Werten "von" und "bis" definiert.

<Window x:Class="VoneinanderAbhaengigeControlsValidierung.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel Name="StackPanelVonBis">
<StackPanel.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"
/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="validationTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="Red" FontSize="20" VerticalAlignment="Top">!</TextBlock>
<AdornedElementPlaceholder/>
</StackPanel>
</ControlTemplate>
</StackPanel.Resources>
<Label Content="Diese beiden Controls sollen in Abhängigkeit validiert werden"/>
<StackPanel Orientation="Horizontal">
<Label>von:</Label>
<TextBox Name="TextBoxVon" Width="30" Validation.ErrorTemplate="{StaticResource validationTemplate}" LostFocus="TextBoxVonBis_LostFocus">
<Binding Path="PreisVon"  NotifyOnValidationError="True">

</Binding>
</TextBox>
<Label>bis:</Label>
<TextBox Name="TextBoxBis" Width="30" Validation.ErrorTemplate="{StaticResource validationTemplate}" LostFocus="TextBoxVonBis_LostFocus">
<Binding Path="PreisBis"  NotifyOnValidationError="True">

</Binding>
</TextBox>
</StackPanel>
<Label>Ist von nicht kleiner als bis</Label>
<Label>dann soll die Validierung fehl schlagen</Label>
</StackPanel>
</Grid>
</Window>

In den TextBoxes werden Werte datengbunden und es wird definiert, dass bei einem Fehler bei der Validierung ein Template angezogen wird. Aber wo ist denn die ValidationRule geblieben? Typisch wäre doch:

<Binding.ValidationRules>
    <val:VonBisValidierungsRegel/>
</Binding.ValidationRules>


Für das einhängen der Validierung in das Binding müssen wir selbst Hand anlegen und wir fangen dann mal mit dem Setzen der ValidationRules für die Textboxes und deren Binding an:

private void SetzeValidierungsReglen()
{
Binding bindingVon = BindingOperations.GetBinding(TextBoxVon, TextBox.TextProperty);
bindingVon.ValidationRules.Add(new VonBisValdierungsregel());

Binding bindingBis = BindingOperations.GetBinding(TextBoxBis, TextBox.TextProperty);
bindingBis.ValidationRules.Add(new VonBisValdierungsregel());
}

Ich denke das erklärt sich von selbst :-)

Jetzt müssen wir noch die Validierung triggern und das machen wir in den LostFocus Evens der TextBoxes

public void TextBoxVonBis_LostFocus(object sender, RoutedEventArgs args)
{
List<string> vonBisTexte = new List<string>();
vonBisTexte.Add(TextBoxVon.Text);
vonBisTexte.Add(TextBoxBis.Text);



Binding bindingVon = BindingOperations.GetBinding(TextBoxVon, TextBox.TextProperty);
ValidationResult ergebnisVon = bindingVon.ValidationRules[0].Validate(vonBisTexte, null);

if (!ergebnisVon.IsValid)
{
ValidationError validationError =
new ValidationError(bindingVon.ValidationRules[0],
TextBoxVon.GetBindingExpression(TextBox.TextProperty));

validationError.ErrorContent = ergebnisVon.ErrorContent;

Validation.MarkInvalid(
TextBoxVon.GetBindingExpression(TextBox.TextProperty),
validationError);
}
else
{
Validation.ClearInvalid(TextBoxBis.GetBindingExpression(TextBox.TextProperty));
}

Binding bindingBis = BindingOperations.GetBinding(TextBoxBis, TextBox.TextProperty);
ValidationResult ergebnisBis = bindingBis.ValidationRules[0].Validate(vonBisTexte, null);

if (!ergebnisBis.IsValid)
{
ValidationError validationError =
new ValidationError(bindingBis.ValidationRules[0],
TextBoxBis.GetBindingExpression(TextBox.TextProperty));

validationError.ErrorContent = ergebnisBis.ErrorContent;

Validation.MarkInvalid(
TextBoxBis.GetBindingExpression(TextBox.TextProperty),
validationError);
}
else
{
Validation.ClearInvalid(TextBoxBis.GetBindingExpression(TextBox.TextProperty));
}
}

Hier besteht dann die Möglichkeit eine Liste von Werten an die Validierung zu schicken. Die Validate Methode geh dann mit einer Liste um und nicht nur mit einem Wert:

public class VonBisValdierungsregel : ValidationRule
{

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
try
{
List<String> vonBisListe = (List<String>)value;
}
catch
{
return new ValidationResult(true, null);
}

try
{
List<String> vonBisListe = (List<String>)value;

int von = Int32.Parse(vonBisListe[0]);
int bis = Int32.Parse(vonBisListe[1]);

if (von >= bis)
{
return new ValidationResult(false, "Von muss kleiner sein als bis");
}
}
catch (Exception ex)
{
return new ValidationResult(false, "Von und bis müssen eine Ganzzahl sein!");
}
return new ValidationResult(true, null);
}
}

Für jede der TextBoxe von/bis wird folgendes ausgeführt
Binding bindingVon = BindingOperations.GetBinding(TextBoxVon, TextBox.TextProperty);
ValidationResult ergebnisVon = bindingVon.ValidationRules[0].Validate(vonBisTexte, null);

if (!ergebnisVon.IsValid)
{
ValidationError validationError =
new ValidationError(bindingVon.ValidationRules[0],
TextBoxVon.GetBindingExpression(TextBox.TextProperty));

validationError.ErrorContent = ergebnisVon.ErrorContent;

Validation.MarkInvalid(
TextBoxVon.GetBindingExpression(TextBox.TextProperty),
validationError);
}
else
{
Validation.ClearInvalid(TextBoxBis.GetBindingExpression(TextBox.TextProperty));
}
Das Binding wird ermittelt und die vorher verknüpfte Validation Rule angestoßen
Binding bindingVon = BindingOperations.GetBinding(TextBoxVon, TextBox.TextProperty);
ValidationResult ergebnisVon = bindingVon.ValidationRules[0].Validate(vonBisTexte, null);


Und dann das Ergebnis der Validierung auf die TextBoxes losgelassen: