Search content within the blog

Monday, December 5, 2011

Updating Source in WPF Data Binding

In WPF data binding, when source property is updated from target property is called as updating source property and is defined using UpdateSourceTrigger property of Binding object. This property is used to define how you want to update source. There are two binding modes are available TwoWay and OneWayToSource which causes update to source property on change of target property.

You can specify mainly three different values to UpdateSourceTrigger property of Binding. If you don’t specify any value to UpdateSourceTrigger it uses default.


PropertyChanged – It updates source property as soon as target property changed.

<TextBox Name="CustomerName" Height="30" Margin="5"
Text="{Binding Source={StaticResource MyCustomerData},
Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="0" Grid.Column="1" />

LostFocus – When target object loses focus as soon as source property is updated.

<TextBox Name="CustomerName" Height="30" Margin="5"
Text="{Binding Source={StaticResource MyCustomerData},
Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>

Explicit – Source property is updated when you call explicitly UpdateSource method from code.

<TextBox Name="CustomerName" Height="30" Margin="5"
Text="{Binding Source={StaticResource MyCustomerData},
Path=CustomerName, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>

Code:


BindingExpression bindingExpression =
CustomerName.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();

Value Converters in WPF

Start Posting Your Contents Here


Value converters are used in Data binding. When source object type and target object type are different at that time value converts are used to manipulate data between source and target. Converter class must implement IValueConverter interface. The IValueConverter interface consists of two methods Convert and ConvertBack. Convert Method gets called when source updates target object and ConvertBack method gets called when target updates source object.


<StackPanel>
<CheckBox x:Name="ShowBackgroundCheckbox"
Content="Show Background" Margin="5" />
<TextBox Text="Value Converter" Margin="5"
Background="{Binding
ElementName=ShowBackgroundCheckbox, Path=IsChecked}"/>
</StackPanel>

In above example Textbox’s Background property is bound with Checkbox’s IsChecked property. This binding won't work because IsChecked property is Boolean and background is Brush type. So for successful data binding the source and target type must match which is not true in above XAML code. So we need to create one value converter which manipulates checkbox’s IsChecked property and textbox’s Background property.

<Window.Resources>
<local:BooleanToBackgroundConverter x:Key="booleanToBackground" />
</Window.Resources>

<StackPanel>
<CheckBox x:Name="ShowBackgroundCheckbox"
Content="Show Background" Margin="5" />
<TextBox Text="Value Converter" Margin="5"
Background="{Binding
ElementName=ShowBackgroundCheckbox, Path=IsChecked,
Converter={StaticResource booleanToBackground}}"/>
</StackPanel>

public class BooleanToBackgroundConverter:IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
bool result = (bool)value;
if (result)
{
return Brushes.LightBlue;
}
return null;
}

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









When Show Background checkbox is checked the textbox get filled with LightBlue background. To achieve that I created one class named BooleanToBackgroundConverter which implements IValueConverter interface. In our example Checkbox is source for textbox. So whenever user changes checkbox’s value the Convert method will get called. The ConvertBack method converts target value to source value. This method will get called when twoway binding established between source and target.

Converter Parameters
Additional information can be passed to value converter using converter parameter.

<TextBox Text="Value Converter" Margin="5"
Background="{Binding
ElementName=ShowBackgroundCheckbox, Path=IsChecked,
Converter={StaticResource booleanToBackground}, ConverterParameter=1}"/>

public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
bool result = (bool)value;
int param = Int32.Parse(parameter.ToString());
if (result)
{
if (param == 1)
return Brushes.Red;
return Brushes.LightBlue;
}
return null;
}








EventSetters in Style

Event can also be attached inside style. Style provides EventSetter inside you can add EventName and attach appropriate handler for that event.

<Window.Resources>
<Style x:Key="MyButtonEvent" TargetType="Button">
<EventSetter Event="MouseEnter" Handler="Button_MouseEnter" />
<EventSetter Event="MouseLeave" Handler="Button_MouseLeave" />
</Style>
</Window.Resources>
<StackPanel>
<Button Name="Cut" Content="Cut"
Height="40" Width="150"
Style="{StaticResource MyButtonEvent}" />
</StackPanel>

private void Button_MouseEnter(object sender, MouseEventArgs e)
{
Button btn = sender as Button;
btn.Background = Brushes.Red;
}
private void Button_MouseLeave(object sender, MouseEventArgs e)
{
Button btn = sender as Button;
btn.Background = Brushes.Green;
}




In above example you can see that Button_MouseEnter and Button_MouseLeave handler is attached inside EventSetter of Style. Whenever mouse enter and leave it will change button background color.

Validate Data using IDataErrorInfo in WPF

WPF Data binding supports for business layer data validation using IDataErrorInfo interface. IDataErrorInfo interface was exists earlier in Windows forms application. Microsoft added support for binding to report error using IDataErrorInfo interface in WPF 3.5 version. IDataErrorInfo interface has two properties one is string Error and another is string indexer. String indexer accepts property name and returns corresponding error. To enable data validation using IDataErroInfo interface you must set ValidatesOnDataError property to True of binding object.

I have reused and modified example I demonstrated in my previous post Validation Rules in WPF. In that example I have added IDataErrorInfo interface and removed validation rules from XAML code. Let’s have a look on below modified example.

<Window.Resources>
<local:Employee x:Key="EmployeeInformation" />
<Style TargetType="{x:Type 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>
</Window.Resources>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.20*"/>
<RowDefinition Height="0.20*" />
<RowDefinition Height="0" />
</Grid.RowDefinitions>

<TextBlock Text="Enter Name: " Grid.Column="0"
Grid.Row="0" Margin="3"/>
<TextBox Name="Name" Grid.Column="1"
Grid.Row="0" Height="30" Margin="3">
<TextBox.Text>
<Binding Source="{StaticResource EmployeeInformation}"
Path="Name" ValidatesOnDataErrors="True" />
</TextBox.Text>
</TextBox>
<TextBlock Text="Enter Age: " Grid.Column="0"
Grid.Row="1" Margin="3"/>
<TextBox Name="Age" Grid.Column="1"
Grid.Row="1" Height="30" Margin="3">
<TextBox.Text>
<Binding Source="{StaticResource EmployeeInformation}"
Path="Age" ValidatesOnDataErrors="True" />
</TextBox.Text>
</TextBox>
</Grid>

public class Employee : INotifyPropertyChanged, IDataErrorInfo
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int age;
public int Age
{
get { return age; }
set { age = value; OnPropertyChanged("Age"); }
}
//Implements INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
//Implements IDataErrorInfo
public string Error //WPF doesn't use this property
{
get { return null; }
}

//Data Validation here.
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Name":
{
return ValidateName(columnName);
}
case "Age":
{
return ValidateAge(columnName);
}
}
return null;
}
}

private string ValidateName(string columnName)
{
if (Name == null) return null;
if (Name.All(c => char.IsLetter(c) || c == ' '))
return null;
return "Please enter only alphabets";
}

private string ValidateAge(string columnName)
{
if (age == 0) return null;
if (age >= 18 && age <= 35)
{
return null;
}
return "Please enter Age between 18 to 35";
}
}


Validation Rules in WPF

WPF data binding model provides ability to validate user input. You can associate Validation Rule with your Binding as well with MultiBinding. Binding engine automatically checks whether any ValidationRule associated or not if yes then every time value passes to the source it will applies validation. Validation applies only when target value updates source value (i.e. with TwoWay or OneWayToSource binding modes).

To create validation rule you need to inherit ValidationRule class. This class is abstract class and has abstract method named Validate which returns ValidationResult type.

Below example explains how to associate validation rule to data binding.


<Window x:Class="WpfApplication1.ValidationRuleDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Validation Rule" Height="130" Width="280">
<Window.Resources>

<local:Employee x:Key="EmployeeInformation" />
<local:AgeRangeValidationRule x:Key="ageRangeValidation" />
<local:OnlyDigitsValidationRule x:Key="onlyDigitsValidation" />
<local:NameValidationRule x:Key="nameValidation" />

<Style TargetType="{x:Type 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>
</Window.Resources>

<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.20*"/>
<RowDefinition Height="0.20*" />
<RowDefinition Height="0" />
</Grid.RowDefinitions>

<TextBlock Text="Enter Name: " Grid.Column="0"
Grid.Row="0" Margin="3"/>
<TextBox Name="Name" Grid.Column="1"
Grid.Row="0" Height="30" Margin="3">
<TextBox.Text>
<Binding Source="{StaticResource EmployeeInformation}"
Path="Name">
<Binding.ValidationRules>
<local:NameValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Text="Enter Age: " Grid.Column="0"
Grid.Row="1" Margin="3"/>
<TextBox Name="Age" Grid.Column="1"
Grid.Row="1" Height="30" Margin="3">
<TextBox.Text>
<Binding Source="{StaticResource EmployeeInformation}"
Path="Age">
<Binding.ValidationRules>
<local:OnlyDigitsValidationRule />
<local:AgeRangeValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</Window>

Code Behind

public class Employee : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int age;
public int Age
{
get { return age; }
set { age = value; OnPropertyChanged("Age"); }
}

public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}

Validation Rule classes

public class NameValidationRule :ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
string name = value.ToString();
if (name.All(c=> char.IsLetter(c) || c== ' '))
return new ValidationResult(true, null);
return new ValidationResult(false, "Please enter only alphabets");
}
}

public class AgeRangeValidationRule: ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
int age = int.Parse(value.ToString());

if (age >= 18 && age <= 35)
{
return new ValidationResult(true, null);
}

return new ValidationResult(false,
"Please enter Age between 18 to 35");
}
}

public class OnlyDigitsValidationRule : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
if (!value.ToString().All(c => char.IsDigit(c)))
return new ValidationResult(false,
"Please enter only digits");

return new ValidationResult(true, null);
}
}



DataTriggers in WPF

This post shows how you can implement data triggers in WPF.

The code is self explanatory. The window consists of textboxes which turn red in foreground color if a value of a memeber of a itemsource to which the data is bounded is set to false.

The data is bound using XML data provider.

Put the follo0wing ina XML file.

<members>
<member>
<isactive>true</isactive>
<groupcolor>#FFCC3333</groupcolor>
<name>Tom Jackson</name>
</member>
<member>
<isactive>false</isactive>
<groupcolor>#FFA4AB28</groupcolor>
<name>Stacey Footheart</name>
</member>
<member>
<isactive>true</isactive>
<groupcolor>#FFA6D877</groupcolor>
<name>Jack Herdin</name>
</member>
<member>
<isactive>true</isactive>
<groupcolor>#FF138C48</groupcolor>
<name>Kim Getruds</name>
</member>
<member>
<isactive>false</isactive>
<groupcolor>#FF5FADB9</groupcolor>
<name>Larry Nusom</name>
</member>
<member>
<isactive>true</isactive>
<groupcolor>#FF3B1076</groupcolor>
<name>Gary Heart</name>
</member>
</members>

Place the folowing in a XAML file.

<Window x:Class="datatriggers_cs.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="datatriggers_cs" Height="300" Width="300"
>
<Window.Resources>
<XmlDataProvider Source="members.xml" x:Key="memberslist" />

<Style x:Key="itemstyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Green" />
<Style.Triggers>
<DataTrigger Binding="{Binding XPath=isactive}" Value="false">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>

</Window.Resources>

<Grid>
<ItemsControl ItemsSource="{Binding Source={StaticResource memberslist}, XPath=/members/member}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="5" BorderBrush="SlateBlue" BorderThickness="2" Background="White" CornerRadius="3">
<TextBlock Style="{StaticResource itemstyle}" Text="{Binding XPath=name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>


OUTPUT
----------


Friday, December 2, 2011

Epander control in WPF - Exmaple

Just copy paste the code. it is self explanatory.

XAML

<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Expender WPF Sample" Height="300" Width="400"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>

<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Expander HorizontalAlignment="Left" Header="Standard Expander"
VerticalAlignment="Top" ExpandDirection="Down" Width="150">
<TextBlock TextWrapping="Wrap" Background="AntiqueWhite">
This is the standard expander
behavior. The expander opens
and the controls below it
move down.
</TextBlock>
</Expander>
<StackPanel Grid.Row="1" Margin="10,5,0,0">
<RadioButton Content="Choice One"/>
<RadioButton Content="Choice Two"/>
<RadioButton Content="Choice Three"/>
</StackPanel>
</Grid>

<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="33" />
<RowDefinition />
</Grid.RowDefinitions>
<Canvas Panel.ZIndex="99">
<Expander HorizontalAlignment="Left" Header="PopUp Window Expander"
VerticalAlignment="Top" ExpandDirection="Down" Width="175">
<Grid Background="Cornsilk">
<Grid.BitmapEffect>
<DropShadowBitmapEffect />
</Grid.BitmapEffect>

<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>

<TextBlock Text="How Cool Is This!" FontWeight="Bold"
Margin="5"/>
<TextBlock Grid.Row="1" TextWrapping="Wrap" Margin="5">
This is the popup expander
behavior. The expander opens
and overlays the controls
below it.
</TextBlock>
</Grid>
</Expander>
</Canvas>
<StackPanel Grid.Row="1" Margin="10,5,0,0">
<RadioButton Content="Choice One"/>
<RadioButton Content="Choice Two"/>
<RadioButton Content="Choice Three"/>
</StackPanel>
</Grid>
</Grid>
</Window>



OUTPUT


Expander control in WPF

An Expander control provides a way to provide content in an expandable area that resembles a window and includes a header.

The Content and Header of an Expander can also contain complex content, such as RadioButton and Image objects.


Setting the Direction of the Expanding Content Area

You can set the content area of an Expander control to expand in one of four directions (Down, Up, Left, or Right) by using the ExpandDirection property. When the content area is collapsed, only the Expander Header and its toggle button appear. A Button control that displays a directional arrow is used as a toggle button to expand or collapse the content area. When expanded, the Expander tries to display all of its content in a window-like area.

Creating Scrollable Content

When you place an Expander control in a ScrollViewer, set the ScrollViewer dimension property that corresponds to the direction in which the Expander content opens to the size of the Expander content area. For example, if you set the ExpandDirection property on the Expander to Down (the content area opens down), set the Height property on the ScrollViewer control to the required height for the content area. If you instead set the height dimension on the content itself, ScrollViewer does not recognize this setting and therefore, does not provide scrollable content.

The following example shows how to create an Expander control that has complex content and that contains a ScrollViewer control.

XAML

<Window x:Class="Practises.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">
<Grid>
<Expander Width="200" HorizontalContentAlignment="Stretch">
<Expander.Header>
<TextBlock Margin="20,0,0,0">My Expander</TextBlock>
</Expander.Header>
<Expander.Content>
<ScrollViewer Height="180">
<TextBlock TextWrapping="Wrap">
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
</TextBlock>
</ScrollViewer>
</Expander.Content>
</Expander>
</Grid>
</Window>



DockPanel in WPF

Introduction


The dock panel is a layout panel, that provides an easy docking of elements to the left, right, top, bottom or center of the panel. The dock side of an element is defined by the attached property DockPanel.Dock. To dock an element to the center of the panel, it must be the last child of the panel and the LastChildFill property must be set to true.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="DockPanel Sample">
<DockPanel LastChildFill="True">
<Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
<TextBlock Foreground="Black">Dock = "Top"</TextBlock>
</Border>
<Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
<TextBlock Foreground="Black">Dock = "Top"</TextBlock>
</Border>
<Border Height="25" Background="LemonChiffon" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom">
<TextBlock Foreground="Black">Dock = "Bottom"</TextBlock>
</Border>
<Border Width="200" Background="PaleGreen" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">
<TextBlock Foreground="Black">Dock = "Left"</TextBlock>
</Border>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<TextBlock Foreground="Black">This content will "Fill" the remaining space</TextBlock>
</Border>
</DockPanel>
</Page>


OUTPUT