Blog Archives
Data Binding (part 2, advanced), WPF
The new C++ posts will take a lot time. The C# posts are shorter for the next three weeks. Today I created a DataGrid and three TextBoxes. They are all linked together with pretty much no code. You can even add new items to the list. Check it out!
<Window x:Class="WpfDatabinding2.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> <Grid.ColumnDefinitions> <ColumnDefinition Width="26*" /> <ColumnDefinition Width="192*" /> <ColumnDefinition Width="285*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="10*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <DataGrid AutoGenerateColumns="True" Grid.ColumnSpan="3" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ColumnHeaderHeight="30"> </DataGrid> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name}" /> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Owner}" /> <TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Age}" /> </Grid> </Window>
using System.Collections.Generic; using System.Windows; using System.Windows.Documents; namespace WpfDatabinding2 { public partial class MainWindow : Window { public class Dog { public string Name { get; set; } public double Age { get; set; } public string Owner { get; set; } } // class public MainWindow() { InitializeComponent(); List<Dog> lDogs = new List<Dog>(); lDogs.Add(new Dog() { Name = "Spike", Owner = "Granny", Age = 12.6 }); lDogs.Add(new Dog() { Name = "Pluto", Owner = "Mickey Mouse", Age = 7.0 }); lDogs.Add(new Dog() { Name = "Snoopy", Owner = "Charlie Brown", Age = 5.3 }); lDogs.Add(new Dog() { Name = "Lassie", Owner = "Rudd Weatherwax", Age = 8.5 }); this.DataContext = lDogs; } // } // class } // namespace
Hard to believe, but this is it. Code does not have to be long to be powerful. The magic mostly comes from these lines:
<DataGrid AutoGenerateColumns="True" Grid.ColumnSpan="3" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" ColumnHeaderHeight="30"> </DataGrid> <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Name}" /> <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Owner}" /> <TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Age}" />
Data Binding (part 1, advanced), WPF
I was studying the “Google Authentication” as I plan to write a post about it. But then I came across the WPF data binding, which I was avoiding so far, because code pieces are not easy to display in posts due to their complexity. The “Google Authentication” source code will use WPF data binding. It therefore makes sense to post about WPF data binding first. Newbies have problems with WPF. It can be quite overwhelming at the beginning. This post is not covering the basics. I expect them to be known already. I will only concentrate on some data binding with TextBoxes today.
Have a look at the XAML:
<Window x:Class="WpfDatabinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="194" Width="374"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Label Grid.Column="0" Grid.Row="0" Content="Value1, Init only" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="0" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneTime}" /> <Label Grid.Column="0" Grid.Row="1" Content="Value1, OneWay" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="1" VerticalAlignment="Center" Text="{Binding Value1, Mode=OneWay}" /> <Label Grid.Column="0" Grid.Row="2" Content="Value2, OneWayToSource" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="2" Text="{Binding Value2, Mode=OneWayToSource}" VerticalAlignment="Center" /> <Label Grid.Column="0" Grid.Row="3" Content="Value3, OneWay" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="3" Text="{Binding Value3, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" /> <Label Grid.Column="0" Grid.Row="4" Content="Value3, TwoWay" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="4" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" /> <Label Grid.Column="0" Grid.Row="5" Content="Value3, TwoWay" VerticalAlignment="Center" /> <TextBox Grid.Column=" 1" Grid.Row="5" Text="{Binding Value3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" /> </Grid> </Window>
Instead of assigning simple text literals to the TextBoxes like: Text=”Hello World!”, we assign binding definitions in curly brackets. For instance: Text=”{Binding Value2, Mode=OneWayToSource}”
In this case we connect the property “Value2” with the TextBox text. The Mode tells the binding direction:
The UpdateSourceTrigger=PropertyChanged implies that the binding source class has implemented the interface INotifyPropertyChanged, which requires the event implementation called PropertyChanged. When the program raises this event with the correct property name, which is also defined in the XAML file, then the binding will refresh the GUI element.
And here is the program. Create a BindingClass instance and assign it to the current window DataContext. There is nothing else to do. The rest is taken care of by the .Net Framework and compiler. WPF is quite abstract. A lot of code is generated out of the XAML files. You cannot see the generated code without pressing the icon “Show All Files” in the Solution Explorer. What is important to know is that we are mainly looking at partial files (here: “public partial class MainWindow : Window”). The rest is auto-generated source code.
using System.Windows; namespace WpfDatabinding { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new BindingClass(); } // } // class } // namespace
using System; using System.ComponentModel; using System.Timers; namespace WpfDatabinding { public class BindingClass : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _Value1 = "Value1 XX:XX:XX"; private string _Value2 = "Value2 XX:XX:XX"; private string _Value3 = "Value3 XX:XX:XX"; public string Value1 { get { return _Value1; } set { _Value1 = value; RaiseEvent("Value1"); } } public string Value2 { get { return _Value2; } set { _Value2 = value; RaiseEvent("Value2"); Console.WriteLine(_Value2); } } // event has no effect public string Value3 { get { return _Value3; } set { _Value3 = value; RaiseEvent("Value3"); } } // event has effect on TwoWay public BindingClass() { Timer lTimer = new Timer(1000.0); lTimer.Elapsed += new ElapsedEventHandler(Timer_Elapsed); lTimer.Start(); } // constructor void Timer_Elapsed(object sender, ElapsedEventArgs e) { string lNow = DateTime.Now.ToString("HH:mm:ss"); Value1 = "Value1 " + lNow; } // private void RaiseEvent(string xPropertyName) { var lEvent = PropertyChanged; if (lEvent == null) return; lEvent(this, new PropertyChangedEventArgs(xPropertyName)); } // } // class } // namespace
The BindingClass is straight forward. There are no events that are triggered by the GUI. The separation of XAML and program code is a pretty cool approach.
There are three properties: Value1, Value2 and Value3.
Value1 is constantly written to by a timer event that is raised each second.
Value2 is only receiving values from the GUI, because the corresponding TextBox is using Mode=OneWayToSource. This does not print anything in the GUI, hence I added the Console.WriteLine(_Value2) statement. Check the output there. The statement RaiseEvent("Value2") has no impact, writing to a TextBox would be the wrong direction for this mode.
And RaiseEvent("Value3") only impacts TwoWay TextBoxes, but not on the OneWay TextBox.
Play with the textboxes and see what reacts on what. My hint is that the last three TextBoxes are the most interesting ones.
example output on the console window:
What happens with Value 2 ?