В декабре уже ушедшего 2009 года было много студентов, работы, опять же пожготовка к новому году, поэтому даже на 1 публикацию вемени не было :( А вот сейчас много выходных, сессия и все такой, поэтому буду наверстывать.
Сегодня я хотел бы поговорить о биндинге внешних данных к нашим UserControl (или CustomControl, кому как больше нравится).
Итак стоит достаточно простая задача: разработать WPF UserControl к которому будут адекватно привязываться внешние данные. Для примера рассмотрим тривиальную задачу, компонент для работы с именем, отчеством и фамилией человека. Визуально контрол будет иметь вид:
Биндинг будет осуществляться к полям LastName, FirstName и Patronimyc оответственно.
Реализовать данный UC достаточно просто:
Для тестирования создадим простенькое приложение вида:<UserControl x:Class="ExampleBinding.UC_PersonInfo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinHeight="120" MinWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label Grid.Row="0" VerticalAlignment="Center">Фамилия:</Label>
<Label Grid.Row="1" VerticalAlignment="Center">Имя:</Label>
<Label Grid.Row="2" VerticalAlignment="Center">Отчество:</Label>
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding LastName}" VerticalAlignment="Center"></TextBox>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding FirstName}" VerticalAlignment="Center"></TextBox>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Patronimyc}" VerticalAlignment="Center"></TextBox>
</Grid>
</UserControl>
* This source code was highlighted with Source Code Highlighter.
Напишем вспомогательный класс (обратите внимание, наш UC про него ничего не знает) и три обработчика:
Убедимся чт все работало как ожидалось. По первой кнопке мы создаем обхект, и видим его значения его полей в текстбоксах. По второй изменяется состояние объекта и изменяется содержимое текстбокса. При изменении текста в полях, и нажатии на третью кнопку выдим, что изменения отобразились в объекте.class Person : DependencyObject
{
public string LastName
{
get { return (string)GetValue(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
// Using a DependencyProperty as the backing store for LastName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LastNameProperty =
DependencyProperty.Register("LastName", typeof(string), typeof(Person), new UIPropertyMetadata(""));
public string FirstName
{
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
// Using a DependencyProperty as the backing store for FirstName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FirstNameProperty =
DependencyProperty.Register("FirstName", typeof(string), typeof(Person), new UIPropertyMetadata(""));
public string Patronimyc
{
get { return (string)GetValue(PatronimycProperty); }
set { SetValue(PatronimycProperty, value); }
}
// Using a DependencyProperty as the backing store for Patronimyc. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PatronimycProperty =
DependencyProperty.Register("Patronimyc", typeof(string), typeof(Person), new UIPropertyMetadata(""));
}
Person person = null;
private void button1_Click(object sender, RoutedEventArgs e)
{
person = new Person { FirstName = "Иван", LastName = "Иванов", Patronimyc = "Иванович" };
ucPersonInfo.DataContext = person;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
person.LastName = "Сидоров";
}
private void button3_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(
string.Format(
"{0} {1} {2}",
person.LastName,
person.FirstName,
person.Patronimyc
)
);
}
* This source code was highlighted with Source Code Highlighter.
Как видите, пока ничего интересного.
Усложним задачу, пусть необходимо реализовать выбор должности на которой работает указанный человек. Для простоты в нашем UC добавим ComboBox и набор данных для отображения в нем. Изменим код нашего UC для поддержки всего этого:
Ну и соответственно поменяем код тестового приложения:public class Staff : DependencyObject
{
public int IdStaff
{
get { return (int)GetValue(IdStaffProperty); }
set { SetValue(IdStaffProperty, value); }
}
// Using a DependencyProperty as the backing store for IdStaff. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IdStaffProperty =
DependencyProperty.Register("IdStaff", typeof(int), typeof(Staff), new UIPropertyMetadata(0));
public string StaffName
{
get { return (string)GetValue(StaffNameProperty); }
set { SetValue(StaffNameProperty, value); }
}
// Using a DependencyProperty as the backing store for StaffName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StaffNameProperty =
DependencyProperty.Register("StaffName", typeof(string), typeof(Staff), new UIPropertyMetadata(""));
}
/// <summary>
/// Interaction logic for UC_PersonInfo.xaml
/// </summary>
public partial class UC_PersonInfo : UserControl
{
public UC_PersonInfo()
{
InitializeComponent();
}
List<Staff> _staffList = null;
public List<Staff> StaffList
{
set
{
_staffList = value;
cbStaffs.DisplayMemberPath = "StaffName";
cbStaffs.ItemsSource = _staffList;
}
}
public int IdStaff
{
get { return (int)GetValue(IdStaffProperty); }
set { SetValue(IdStaffProperty, value); }
}
// Using a DependencyProperty as the backing store for IdStaff. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IdStaffProperty =
DependencyProperty.Register("IdStaff", typeof(int), typeof(UC_PersonInfo), new UIPropertyMetadata(0));
}
* This source code was highlighted with Source Code Highlighter.
И вот тут и начинается самое интересное! В нашем UC есть визуальный компонент и свойство IdStaff. И теперь необходимо осуществить правильное отображение должности по иду, и присвоение правильного ида после выбора должности в комбобоксе.private void button1_Click(object sender, RoutedEventArgs e)
{
List<Staff> staffs = new List<Staff>();
staffs.Add(new Staff { IdStaff = 1, StaffName = "Инженер" });
staffs.Add(new Staff { IdStaff = 2, StaffName = "Начальник отдела" });
staffs.Add(new Staff { IdStaff = 3, StaffName = "Технолог" });
ucPersonInfo.StaffList = staffs;
person = new Person { FirstName = "Иван", LastName = "Иванов", Patronimyc = "Иванович", IdStaff = 1};
ucPersonInfo.DataContext = person;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
person.LastName = "Сидоров";
person.IdStaff = 2;
}
* This source code was highlighted with Source Code Highlighter.
Второе проще, для этого необходимо у нашего UC добавить обработчик:
И для ComboBox:private void UserControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Binding bnd = new Binding("IdStaff");
bnd.Mode = BindingMode.TwoWay;
bnd.Source = e.NewValue;
SetBinding(UC_PersonInfo.IdStaffProperty, bnd);
}
* This source code was highlighted with Source Code Highlighter.
private void cbStaffs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
IdStaff = ((Staff)e.AddedItems[0]).IdStaff;
}
А вот с возвратом все намного сложнее. Я это решение подсмотрел в DataTimePicker-е на сайте CodePlex-а.
Необходимо написать вот такой обработчик:
private static void OnIdStaffChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
int value = (int)e.NewValue;
UC_PersonInfo uc = (UC_PersonInfo)d;
Staff newStaff = uc._staffList.FirstOrDefault(s => s.IdStaff == value);
if (newStaff != null)
{
uc.cbStaffs.SelectedItem = newStaff;
}
}
И подписать его для нашего свойства:
// Using a DependencyProperty as the backing store for IdStaff. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IdStaffProperty =
DependencyProperty.Register("IdStaff", typeof(int), typeof(UC_PersonInfo), new UIPropertyMetadata(0, OnIdStaffChanged));
Вот теперь все работает так как ожидалось, осуществляется привязка не только простых полей, но и сложных элементов в которых разорвано свойство и визуальное отображение.
P.s. Сори за последние фрагменты кода, что то сайт для оформления кода шалит...
Комментариев нет:
Отправить комментарий