Mechanizm jest znacznie lepiej dopracowany niż ten znany z WindowsForms. Umożliwia tworzenie źródeł danych jak i wzorców dla wyświetlanych danych. Wielu ekspertów uważa, że przesiadając się z WindowsForms warto właśnie ten mechanizm poznać jako pierwszy i najważniejszy, gdyż z niego będziemy korzystali najczęściej w naszych aplikacjach.
Tak samo jak w WindowsForms mamy proste wiązanie. Jednak oprócz wiązania w jedną i dwie strony mamy także inne możliwości. Zaczniemy jednak od bardzo prostego przykładu kolejno idąc w stronę coraz to bardziej złożonych.
A więc zaczniemy od bardzo prostego bindowania w jedną stronę. Na początek stworzymy formatkę z odpowiednimi kontrolkami:
<Window x:Class="WpfApplication4.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="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Name="label" Grid.Column="0" Grid.Row="0" Content="Ala ma kota" />
<TextBox Name="textBox" Grid.Column="1" Grid.Row="0" Width="100" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Text="10" />
</Grid>
</Window>
Teraz w pliku źródłowym wprowadzamy kod:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Binding binding = new Binding();
binding.Source = textBox;
binding.Path = new PropertyPath("Text");
label.SetBinding(Label.FontSizeProperty, binding);
}
}
Uruchamiamy program i zobaczmy jak działa. Po wprowadzeniu liczby do TextBoxa rozmiar czcionki Labela odpowiednio się zwiększa lub zmniejsza. Zobaczmy jak zostało to osiągnięte. Na początku został stworzony obiekt typu Binding, który reprezentuje wiązanie z danymi. Następnie ustawiamy parametr Source, który odpowiada za źródło danych. Właściwość Path mówi do jakiej właściwości będziemy się odnosili (w naszym wypadku jest to po prostu Text). Na końcu bindujemy label z naszym obiektem Binding. Bindingowi podlega każda DependencyProperty.
Zaprezentowano tutaj sposób bindowania za pomocą kodu. W większości przypadków stosuje się metodę poprzez bindowanie bezpośrednio w XAMLu. Dlaczego? Przede wszystkim jest to dużo łatwiejsze i przyjemniejsze. Wiązanie w kodzie przypadje się w kilku sytuacjach:
- kiedy chcemy dynamicznie bindować kontrolki z różnymi źródłami danych
- jeżeli chcemy usunąć bindowanie (za pomocą metod ClearBinding() lub ClearAllBindings()
- kiedy tworzymy własne kontrolki
Zobaczmy teraz na poprzedni przykład w wersji XAML (kod C# dla bindowania usuwamy lub komentujemy):
<Window x:Class="WpfApplication4.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="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Name="label" Grid.Column="0" Grid.Row="0" Content="Ala ma kota" FontSize="{Binding ElementName=textBox, Path=Text}"/>
<TextBox Name="textBox" Grid.Column="1" Grid.Row="0" Width="100" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Text="10" />
</Grid>
</Window>
Jak widać z kilku linijek, które musialiśmy poprzednio napisać zrobiło się 5 wyrazów:
FontSize="{Binding ElementName=textBox, Path=Text}"
Powyższy przykład pokazuje bindowanie w jedną stronę (OneWay). Zobaczmy teraz jak działa bindowanie w dwie strony. Tworzymy nowy przykład, który bardzo ładnie pokaże zastosowanie tego rodzaju bindowania:
<Window x:Class="WpfApplication4.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="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition />
</Grid.RowDefinitions>
<Slider Name="slider" Grid.Column="0" Grid.Row="0" Minimum="0" Maximum="100" Value="{Binding ElementName=textBox, Path=Text, Mode=TwoWay}" />
<TextBox Name="textBox" Grid.Column="1" Grid.Row="0" Width="100" Height="25" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" />
</Grid>
</Window>
Po uruchomieniu aplikacji zobaczymy:
Zmieniając pozycję na suwaku zmieniamy wartość wyświetlaną w TextBoxie, ale równocześnie zmieniając wartość w TextBoxie zmieniamy pozycję suwaka Slidera. Tak więc jest to klasyczny przykład bindowania w dwie strony. Tak więc podsumowując mamy do dyspozycji następujące tryby bindowania:
- TwoWay - uaktualnia zarówno wartość właściwości kontrolki celowej jak i źródłowej
- OneWay - uaktualnia wartość kontrolki docelowej tylko w przypadku zmiany wartości w źródle
- OneTime - dokonuje zmiany w kontrolce docelowej tylko raz (np. w momencie uruchomienia programu)
- OneWayToSource - uaktualnia tylko wartość źródłową
- Default - zależy od konkretnej sytuacji i właściwości na którą się ją nakłada
Podczas pracy z ostatnim przykładem, podczas jakichkolwiek zmian położenia suwaka czy wprowadzania danych do TextBoxa, zmiany następują natychmiastowo. Można to zmienić za pomocą typu wyliczeniowego UpdateSourceTrigger. Do wyboru mamy takie tryby jak:
- LostFocus - czyli kiedy kontrolka straci aktywność
- PropertyChanged - kiedy właściwość się zmieni
- Default - dla większości właściwości jest to PropertyChanged
- Explicit - czyli na wyraźne życzenie użytkownika
W przypadku ostatniej możliwości chodzi o to, że np. użytkownik wprowadza jakieś dane, a następnie klika w przycisk OK który dokonuje uaktualnienia. Wymaga to napisania następującego kodu (na formatkę dokładamy przycisk):
private void button_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = slider.GetBindingExpression(Slider.ValueProperty);
be.UpdateSource();
}
Do tej pory widzieliśmy w jaki sposób wiązać proste właściwości obiektów. Teraz zobaczymy w jaki sposób można związać kontrolkę z obiektem. Na potrzeby przykładu stworzymy prostą klasę reprezentującą osobę:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int YearOfBirth { get; set; }
}
Ważne jest tutaj, że właściwości do których bindujemy są publiczne. Należy podkreślić słowo właściwości. Do zwykłych pól klasy (chociaż byłyby publiczne) bindowanie nie działa. Tak więc najpierw w Resourcach okna tworzymy obiekt typu Person, inicjujemy jego pola, a następnie kolejnym Labelom przypisujemy odpowiednie właściwości:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfApplication4"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:Person x:Key="Marek" FirstName="Marek" LastName="Kowalski" YearOfBirth="1980" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="{Binding Source={StaticResource ResourceKey=Marek}, Path=FirstName}" Grid.Column="0" Grid.Row="0" />
<Label Content="{Binding Source={StaticResource ResourceKey=Marek}, Path=LastName}" Grid.Column="0" Grid.Row="1" />
<Label Content="{Binding Source={StaticResource ResourceKey=Marek}, Path=YearOfBirth}" Grid.Column="0" Grid.Row="2" />
</Grid>
</Window>
Kilka linijek powyższego kodu należy wyjaśnić:
xmlns:c="clr-namespace:WpfApplication4"
<Window.Resources>
<c:Person x:Key="Marek" FirstName="Marek" LastName="Kowalski" YearOfBirth="1980" />
</Window.Resources>
{Binding Source={StaticResource ResourceKey=Marek}, Path=FirstName}
Zbindowaliśmy jeden obiekt? To teraz czas na całą listę obiektów. Stworzymy listę obiektów klasy Person a następnie wyświetlimy ją w ListBoxie. Na początek kod a później wyjaśnienia:
public class PersonList : ObservableCollection<Person>
{
}
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfApplication4"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:PersonList x:Key="PersonList">
<c:Person FirstName="Ala" LastName="Kowalska" YearOfBirth="1920" />
<c:Person FirstName="Marek" LastName="Kowalski" YearOfBirth="1980" />
</c:PersonList>
<DataTemplate x:Key="listBoxItem">
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="5" >
<StackPanel Name="itemPanel" Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding Path=FirstName}" Foreground="Blue" />
<TextBlock Text="{Binding Path=LastName}" Foreground="Green" />
<TextBlock Text="{Binding Path=YearOfBirth}" Foreground="Red" />
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Name="lb" Width="100" Height="250" ItemsSource="{StaticResource ResourceKey=PersonList}" ItemTemplate="{StaticResource ResourceKey=listBoxItem}" >
</ListBox>
</Grid>
</Window>
Po uruchomieniu programu zobaczymy następujący efekt na ekranie:
Jak widać składowe właściwości klasy Person występują tutaj jako jeden item ListBoxa. A więc teraz po kolei co zrobiliśmy aby osiągnąć taki efekt. Na początek stworzyliśmy klasę PersonList która dziedziczy po ObservableCollection
<c:PersonList x:Key="PersonList">
<c:Person FirstName="Ala" LastName="Kowalska" YearOfBirth="1920" />
<c:Person FirstName="Marek" LastName="Kowalski" YearOfBirth="1980" />
</c:PersonList>
<ListBox Name="lb" Width="100" Height="250" ItemsSource="{StaticResource ResourceKey=PersonList}" ItemTemplate="{StaticResource ResourceKey=listBoxItem}" >
<DataTemplate x:Key="listBoxItem">
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="5" >
<StackPanel Name="itemPanel" Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding Path=FirstName}" Foreground="Blue" />
<TextBlock Text="{Binding Path=LastName}" Foreground="Green" />
<TextBlock Text="{Binding Path=YearOfBirth}" Foreground="Red" />
</StackPanel>
</Border>
</DataTemplate>
Tak więc bardzo małym nakładem pracy udało się osiągnąć ciekawe rezultaty w sposobie wyświetlania danych.
Teraz można jeszcze dodać możliwość edytowania danych zawatych w ListBoxie. Na formatkę dokładamy TextBoxy i bindujemy je z itemem ListBoxa:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WpfApplication4"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<c:PersonList x:Key="PersonList">
<c:Person FirstName="Ala" LastName="Kowalska" YearOfBirth="1920" />
<c:Person FirstName="Marek" LastName="Kowalski" YearOfBirth="1980" />
</c:PersonList>
<DataTemplate x:Key="listBoxItem">
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="5" >
<StackPanel Name="itemPanel" Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding Path=FirstName}" Foreground="Blue" />
<TextBlock Text="{Binding Path=LastName}" Foreground="Green" />
<TextBlock Text="{Binding Path=YearOfBirth}" Foreground="Red" />
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox HorizontalAlignment="Left" Name="lb" Width="100" Height="250" ItemsSource="{StaticResource ResourceKey=PersonList}" ItemTemplate="{StaticResource ResourceKey=listBoxItem}" SelectedIndex="0" />
<StackPanel Width="100" Orientation="Vertical">
<TextBox Width="100" Text="{Binding ElementName=lb, Path=SelectedItem.FirstName}" />
<TextBox Width="100" Text="{Binding ElementName=lb, Path=SelectedItem.LastName}" />
<TextBox Width="100" Text="{Binding ElementName=lb, Path=SelectedItem.YearOfBirth}" />
</StackPanel>
</Grid>
</Window>
Text="{Binding ElementName=lb, Path=SelectedItem.FirstName}"
Ponieważ wiązanie tutaj jest domyślnie TwoWay zmieniając dane w TextBoxach zmieniają się także w źródle danych.
Tyle na temat bindowania do obiektów. W następnej części pokażę w jaki sposób wykorzystać poznane tutaj narzędzia w pracy z bazą danych.
Źródło:
http://msdn.microsoft.com/en-us/library/ms752347.aspx
http://msdn.microsoft.com/en-us/magazine/cc163299.aspx
http://coredotnet.blogspot.com/2006/05/wpf-data-binding-tutorial.html
http://msdn.microsoft.com/en-us/library/ms617928.aspx
http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.aspx
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger.aspx
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx
http://msdn.microsoft.com/en-us/library/ms613642.aspx
Świetny artykuł, na pewno jeszcze nie raz mi się przyda ;)
OdpowiedzUsuń