WPF笔记汇总之数据绑定及依赖属性

WPF数据绑定

紧接上一篇《WPF笔记汇总之控件进阶与面板布局》,这篇主要汇总一下WPF最重要的特性之一,那就是数据绑定,以及自定义对象的值转换和绑定的调试方法,与以往的WinForm的事件驱动开发方式不同,WPF采用的是数据驱动的方式。

1. 绑定初步

简单的使用绑定,就是采用{Binding Path=Text, ElementName=txtValue}的类似方式,Binding是绑定的标记,Path是绑定的属性,ElementName是绑定的元素,也可以称为数据源。

<StackPanel Margin="10">
	<TextBox Name="txtValue" />
	<WrapPanel Margin="0,10">
		<TextBlock Text="Value: " FontWeight="Bold" />
		<TextBlock Text="{Binding Path=Text, ElementName=txtValue}" />
	</WrapPanel>
</StackPanel>

此外还可以通过后置代码来绑定, SetBinding()方法接受两个参数,一个是要绑定的依赖项属性,另一个是绑定对象。

<StackPanel Margin="10">
	<TextBox Name="txtValue" />
	<WrapPanel Margin="0,10">
		<TextBlock Text="Value: " FontWeight="Bold" />
		<TextBlock Name="lblValue" />
	</WrapPanel>
</StackPanel>
public MainWindow()
{
	InitializeComponent();
	
	Binding binding = new Binding("Text");
	binding.Source = txtValue;
	lblValue.SetBinding(TextBlock.TextProperty, binding);
}

2. 数据上下文

数据上下文就是DataContext对象,它表示要绑定的源对象,本例子中的数据上下文对象是窗体本身。

<StackPanel Margin="15">
	<WrapPanel>
		<TextBlock Text="Window title:  " />
		<TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}" Width="150" />
	</WrapPanel>
	<WrapPanel Margin="0,10,0,0">
		<TextBlock Text="Window dimensions: " />
		<TextBox Text="{Binding Width}" Width="50" />
		<TextBlock Text=" x " />
		<TextBox Text="{Binding Height}" Width="50" />
	</WrapPanel>
</StackPanel>

后置代码

public MainWindow()
{
	InitializeComponent();
	this.DataContext = this;
}

其中UpdateSourceTrigger属性,是用来设置触发数据源用的,Default是UpdateSourceTrigger的默认值。Default是根据不同的控件的默认行为而定的,其他选项是PropertyChanged, LostFocus和Explicit。一个是属性改变时更新,一个是失去焦点时更新,而最后一个Explicit是手动推送更新,使用Binding上的UpdateSource方法调用。用法实例如下:

<StackPanel Margin="15">
	<WrapPanel>
		<TextBlock Text="Window title:  " />
		<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
		<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
	</WrapPanel>
	<WrapPanel Margin="0,10,0,0">
		<TextBlock Text="Window dimensions: " />
		<TextBox Text="{Binding Width, UpdateSourceTrigger=LostFocus}" Width="50" />
		<TextBlock Text=" x " />
		<TextBox Text="{Binding Height, UpdateSourceTrigger=PropertyChanged}" Width="50" />
	</WrapPanel>
</StackPanel>
public partial class MainWindow : Window
{
	public MainWindow()
	{
		InitializeComponent();
		this.DataContext = this;
	}
	private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
	{
		BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
		binding.UpdateSource();
	}
}

3. 自定义绑定属性

如果要让UI中的任何内容得到更新,需要实现ObservableCollection和INotifyPropertyChanged这两个接口,ObservableCollection是用来通知任何目的地其内容的更改的一个列表,INotifyPropertyChanged接口是用来使自定义的对象能够警告UI层对其属性的更改。

<DockPanel Margin="10">
	<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
		<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
		<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
		<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
	</StackPanel>
	<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
</DockPanel>
/// <summary>
	/// MainWindow.xaml 的交互逻辑
	/// </summary>
	public partial class MainWindow : Window
	{
		private ObservableCollection<User> users = new ObservableCollection<User>();
		public MainWindow()
		{
			InitializeComponent();
			
			users.Add(new User() { Name = "John Doe" });
			users.Add(new User() { Name = "Jane Doe" });
			lbUsers.ItemsSource = users;
		}

		private void btnAddUser_Click(object sender, RoutedEventArgs e)
		{
			users.Add(new User() { Name = "New user" });
		}
		private void btnChangeUser_Click(object sender, RoutedEventArgs e)
		{
			if (lbUsers.SelectedItem != null)
				(lbUsers.SelectedItem as User).Name = "Random Name";
		}
		private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
		{
			if (lbUsers.SelectedItem != null)
				users.Remove(lbUsers.SelectedItem as User);
		}
	}

	/// <summary>
	/// User类
	/// </summary>
	public class User : INotifyPropertyChanged
	{
		private string name;
		public string Name
		{
			get { return this.name; }
			set
			{
				if (this.name != value)
				{
					this.name = value;
					this.NotifyPropertyChanged("Name");
				}
			}
		}

		/// <summary>
		/// 实现通知属性改变事件
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;
		public void NotifyPropertyChanged(string propName)
		{
			if (this.PropertyChanged != null)
				this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
		}
	}

4. 自定义对象值转换

要实现自定义的值转换,则需要使用值转换器。这些实现IValueConverter接口的小类将像中间人一样工作,并在源和目标之间转换值。其中有个StringFormat属性是转换显示的文本方式,ConverterCulture是转换为特定的文化输出,有时需要在命名空间中增加 xmlns:system=“clr-namespace:System;assembly=mscorlib” 的系统类库声明。

<Window.Resources>
	<local:YesNoToBooleanConverter x:Key="YesNoToBooleanConverter" />
</Window.Resources>
<StackPanel Margin="10">
	<TextBox Name="txtValue" />
	<WrapPanel Margin="0,10">
		<TextBlock Text="Current value is: " />
		<TextBlock Text="{Binding ElementName=txtValue, Path=Text, Converter={StaticResource YesNoToBooleanConverter}}"></TextBlock>
	</WrapPanel>
	<CheckBox IsChecked="{Binding ElementName=txtValue, Path=Text, Converter={StaticResource YesNoToBooleanConverter}}" Content="Yes" />

	<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='de-DE', StringFormat=German date: {0:D}}" />
	<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='en-US', StringFormat=American date: {0:D}}" />
	<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}, ConverterCulture='ja-JP', StringFormat=Japanese date: {0:D}}" />
	
</StackPanel>
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{

	public MainWindow()
	{
		InitializeComponent();

	}

}
/// <summary>
/// 布尔值转换器
/// </summary>
public class YesNoToBooleanConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		switch (value.ToString().ToLower())
		{
			case "yes":
				return true;
			case "no":
				return false;
		}
		return false;
	}

	public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		if (value is bool)
		{
			if ((bool)value == true)
				return "yes";
			else
				return "no";
		}
		return "no";
	}
}

5. 调试绑定数据

两种方式可以跟踪调试数据绑定,一种是显示的声明跟踪级别,在输出窗口中查看打印的信息,一种是用虚拟转换器进行中断调试。

5.1 调整跟踪级别
<Window x:Class="WpfStudy.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
		xmlns:local="clr-namespace:WpfStudy"
		xmlns:system="clr-namespace:System;assembly=mscorlib"
		 xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
		mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="400"
		Icon="me.png" WindowStartupLocation="CenterScreen">
	<Window.Resources>
	</Window.Resources>

	<Grid Margin="10">
		<TextBlock Text="{Binding Title, diag:PresentationTraceSources.TraceLevel=High}" />
	</Grid>
5.2 虚拟转换器

在后台代码文件中定义一个DebugDummyConverter。 有Convert()和ConvertBack()方法,调用Debugger.Break(),可以在此处做断点调试用。

<Window.Resources>
	<local:DebugDummyConverter x:Key="DebugDummyConverter" />
</Window.Resources>
<Grid Margin="10">
	<TextBlock Text="{Binding Title, ElementName=wnd, Converter={StaticResource DebugDummyConverter}}" />
</Grid>
public class DebugDummyConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		Debugger.Break();
		return value;
	}

	public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		Debugger.Break();
		return value;
	}
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值