WPF 教程 - 概念绑定






4.84/5 (46投票s)
绑定是WPF编程中最重要的话题。在本文中,我将演示如何使用数据绑定来确保表示逻辑与视图分离,并简单演示数据绑定概念的工作原理。
目录
引言
在此文之前,我已讨论过WPF的架构、标记扩展、依赖属性、逻辑树和视觉树、布局、变换等。今天,我将讨论我们称之为任何WPF应用程序中最重要部分的“绑定”。WPF具有卓越的DataBinding
(数据绑定)功能,它使用户能够绑定对象,以便当其他对象发生变化时,主对象能够反映这些变化。DataBinding
的主要目的是确保UI始终自动与内部对象结构同步。
在深入探讨之前,让我们回顾一下我们已经讨论过的内容。如果您是本文的新读者,您可以从下面的列表中我的其他文章开始阅读:
- WPF 教程:入门 [^]
- WPF 教程:布局面板、容器和布局变换 [^]
- WPF 教程:边框和画笔的乐趣 [^]
- WPF 教程 - 类型转换器和标记扩展 [^]
- WPF 教程 - 依赖属性 [^]
- WPF 教程 - 概念绑定 [^]
- WPF 教程 - 样式、触发器和动画 [^]

DataBinding
(数据绑定)在WPF出现之前就已存在。在ASP.NET中,我们绑定数据元素以从控件中呈现正确的数据。我们通常传递一个DataTable
并绑定模板以从各个DataRows
获取数据。另一方面,在传统的Windows Forms应用程序中,我们也可以将属性绑定到数据元素。可以将绑定添加到对象的属性中,以确保每当属性值更改时,数据会在内部得到反映。所以,一言以蔽之,DataBinding
对系统来说并不是什么新鲜事。DataBinding
的主要目标是向应用程序显示数据,从而减少应用程序开发人员为使应用程序正确显示数据而需要编写的工作量。在本文中,我将讨论如何在WPF应用程序中使用Databinding
,并创建一个示例应用程序来深入演示该功能。
WPF 中的绑定
WPF将Binding
(绑定)的概念进一步发展,并引入了新功能,以便我们可以广泛地使用绑定功能。绑定建立了应用程序和业务层之间的连接。如果您希望您的应用程序遵循严格的设计模式规则,DataBinding
(数据绑定)概念将帮助您实现这一点。我们将在稍后更详细地探讨如何实现这一点。
在WPF中,我们可以绑定两个属性、一个属性和一个DependencyProperty
(依赖属性)、两个DependencyProperties
等。WPF还支持Command Binding
(命令绑定)。让我们详细讨论如何实现它们。
绑定可分为几种类型。
数据绑定 / 对象绑定
最重要和最主要的绑定是Databinding
(数据绑定)。WPF引入了ObjectDataProvider
和XMLDataProvider
等对象,可在XAML中声明,以增强对象绑定的能力。DataBinding
(数据绑定)可以通过多种方式实现。如Adnan在他的博客中所述[^],我们可以通过使用XAML、XAML和C#,或者仅使用C#来利用绑定功能。因此,WPF足够灵活,可以处理任何情况。
<TextBox x:Name="txtName" />
<TextBlock Text="{Binding ElementName=txtName, Path=Text.Length}" />
在上述情况中,我展示了绑定的最基本用法。TextBlock
的Text
属性绑定到TextBox txtName
,因此当您在运行时在TextBox
中输入任何内容时,TextBlock
将显示该string
的长度。
作为一种标记扩展,绑定实际上是一个带属性的类。在此,我们指定了ElementName
和Path
属性的值。ElementName
确保了属性所属的对象。Path
定义了对象需要查找的属性路径。
您可以使用ObjectDataProvider
轻松地在XAML中处理数据。ObjectDataProvider
可以添加为资源,然后使用StaticResource
进行引用。让我们看看下面的代码:
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<ObjectDataProvider ObjectType="{x:Type m:StringData}"
x:Key="objStrings" MethodName="GetStrings"/>
</StackPanel.Resources>
<ListBox Name="lstStrings" Width="200" Height="300"
ItemsSource="{Binding Source={StaticResource objStrings}}" />
正如上面所示,ObjectType
将获得一个类型,该类型是调用GetStrings
方法的内部类结构。从ListBox
中,我使用StaticResource
引用了对象。现在在代码中,您可以声明一个类:
public class StringData
{
ObservableCollection<String> lst= new ObservableCollection<String>();
public StringData()
{
lst.Add("Abhishek");
lst.Add("Abhijit");
lst.Add("Kunal");
lst.Add("Sheo");
}
public ObservableCollection<String> GetStrings()
{
return lst;
}
}
这样,您可以看到列表已填充了string
。
为什么使用 ObservableCollection、INotifyPropertyChanged、INotifyCollectionChanged?
现在您可以看到,我使用了ObvervableCollection
。这很重要。ObservableCollection
在插入新项时发送自动通知。因此,它会通知ListBox
更新列表。所以,如果您放置一个插入数据的按钮到ObservableCollection
中,绑定将自动由集合通知,从而自动更新集合。您无需手动将相同的数据插入ListBox
。
WPF绑定通常需要被通知何时修改。INotifyPropertyChanged
和INotifyCollectionChanged
接口需要更新与数据绑定的UIElement
。因此,如果您正在创建一个属性,并且该属性需要在其值修改时更新UI,那么最低要求是实现INotifyPropertyChanged
,而对于集合(如ItemsSource
),它需要实现INotifyCollectionChanged
。ObservableCollection
本身实现了INotifyCollectionChanged
,因此它支持在列表插入新项或从string
中移除旧项时更新控件。
我在一篇关于对象和集合的更改通知的文章中详细讨论了这两个接口。 [^]
相反,Sacha提出了一个通过Aspect Examples (INotifyPropertyChanged via aspects)来摆脱INotifyPropertyChanged
接口的好方法。
XML 绑定
与对象绑定类似,XAML也支持XML绑定。您可以使用Binding
类定义中的内置属性,如XPath
,轻松绑定来自XMLDataProvider
的数据。让我们看看代码:
<TextBlock Text="{Binding XPath=@description}"/>
<TextBlock Text="{Binding XPath=text()}"/>
因此,如果您在XYZ
节点中,可以使用text()
属性获取InnerText
。@
符号用于属性。所以,使用XPath
,您可以轻松处理您的XML。
如果您想了解更多关于XML绑定的信息,请查看:WPF中的XML绑定 [^]。
DataContext 的重要性
您可能会想,为什么我在讨论WPF绑定时提到了DataContext
的上下文。DataContext
实际上是一个依赖属性。它指向原始数据,因此我们作为DataContext
传递的对象将继承给其所有子控件。我的意思是,如果您为Grid
定义了DataContext
,那么Grid
内的所有元素都将获得相同的DataContext
。
<Grid DataContext="{StaticResource dtItem}">
<TextBox Text="{Binding MyProperty}" />
</Grid>
在这里,正如我为Grid
定义了DataContext
一样,网格内的TextBox
可以将其MyProperty
属性引用为dtItem
对象将自动继承给其所有子元素。在使用绑定时,DataContext
是您必须使用的最重要部分。
绑定成员
正如大家所知,绑定是一种标记扩展。它是一个带有几个属性的Binding
类。让我们讨论一下Binding
的成员:
- Source:源属性保存
DataSource
。默认情况下,它引用控件的DataContext
。如果您为绑定设置了Source属性,它将代替原始DataContext
元素。 - ElementName:在与另一个
Element
进行Binding
时,ElementName
用于获取XAML中定义的Element
的名称以引用对象。ElementName
充当Source
的替代。如果未为绑定指定Path,它将使用ToString
从作为Source传递的对象中获取数据。 - Path:
Path
定义了获取字符串数据的实际属性路径。如果最终结果不是string
,它还将调用ToString
来获取数据。 - Mode:它定义数据如何流动。
OneWay
表示仅当源更新时对象才会被更新,反之,OneWayToSource
则相反。TwoWay
定义数据在两个方向上流动。 - UpdateSourceTrigger:这是任何
Binding
的另一个重要部分。它定义了何时更新源。UpdateSourceTrigger
的值可以是:- PropertyChanged:这是默认值。结果是,当控件中的任何内容更新时,其他绑定的元素都会反映相同的内容。
- LostFocus:这意味着当属性失去焦点时,属性将被更新。
- Explicit:如果选择此选项,您需要明确设置何时更新源。您需要使用
BindingExpression
的UpdateSource
来更新控件。BindingExpression bexp = mytextbox.GetBindingExpression(TextBox.TextProperty); bexp.UpdateSource();
通过这种方式,源会得到更新。
- Converter:
Converter
为您提供一个接口,可以插入一个对象,该对象将在绑定对象更新时被调用。任何实现IValueConverter
的对象都可以代替Converter
使用。您可以从绑定中的转换器了解更多关于它的信息。 [^] - ConverterParameter:它与
Converter
一起使用,将参数发送给Converter
。 - FallbackValue:定义在
Binding
无法返回任何值时将显示的值。默认情况下,它是空白的。 - StringFormat:一个格式化
string
,指示数据将遵循的Format
。 - ValidatesOnDataErrors:当指定此项时,将验证
DataErrors
。您可以使用IDataErrorInfo
在数据对象更新时运行自定义验证块。您可以从:使用IDataErrorInfo验证您的应用程序了解更多关于IDataErrorInfo
的信息。 [^]。
代码隐藏中的绑定
与您在XAML中所做的类似,您也可以在代码隐藏中定义绑定。要做到这一点,您需要使用
Binding myBinding = new Binding("DataObject");
myBinding.Source = myDataObject;
myTextBlock.SetBinding(TextBlock.TextProperty, myBinding);
您也可以这样指定Binding
属性。
命令绑定
WPF支持CommandBinding
(命令绑定)。每个命令对象(如Button
)都公开一个名为Command
的属性,该属性接受一个实现ICommand
接口的对象,并在对象命令触发时执行Execute
方法。
例如,当窗口输入被调用时,您希望您的命令被执行。
<Window.InputBindings>
<KeyBinding Command="{Binding CreateNewStudent}" Key="N" Modifiers="Ctrl" />
<MouseBinding Command="{Binding CreateNewStudent}"
MouseAction="LeftDoubleClick" />
</Window.InputBindings>
在上面的代码中,CreateNewStudent
是一个属性,它公开实现ICommand
接口的对象,并且当窗口的Ctrl + N键或LeftDoubleClick
被调用时,将调用Execute
方法。
注意:在VS 2008中,InputBindings
仅接受Static
命令对象。 有一个关于此的bug报告 [^],将在后续版本中修复。
您可以使用CommandParameter
将参数传递给构成ICommand
接口的方法。
<Button Content="CreateNew" Command="{Binding CreateNewStudent}" />
与InputBindings
类似,您可以使用Button
的Command
。要执行,您需要创建一个实现ICommand
的对象,如下所示:
public class CommandBase : ICommand
{
private Func<object, bool> _canExecute;
private Action<object> _executeAction;
private bool canExecuteCache;
public CommandBase(Action<object>executeAction, Func<object, bool> canExecute)
{
this._executeAction = executeAction;
this._canExecute = canExecute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
bool tempCanExecute = _canExecute(parameter);
canExecuteCache = tempCanExecute;
return canExecuteCache;
}
private event EventHandler _canExecuteChanged;
public event EventHandler CanExecuteChanged
{
add { this._canExecuteChanged += value; }
remove { this._canExecuteChanged -= value; }
}
protected virtual void OnCanExecuteChanged()
{
if (this._canExecuteChanged != null)
this._canExecuteChanged(this, EventArgs.Empty);
}
public void Execute(object parameter)
{
_executeAction(parameter);
}
#endregion
}
我使用了一个CommandBase
类来使对象看起来不那么笨拙。实际的对象类如下所示:
private CommandBase createNewstudent;
public CommandBase CreateNewStudent
{
get
{
this.createNewstudent = this.createNewstudent ??
new CommandBase(param => this.CreateStudent(), param => this.CanCreateStudent);
return this.createNewstudent;
}
}
private object CreateStudent()
{
this.CurrentStudent = new StudentItem();
return this.CurrentStudent;
}
public bool CanCreateStudent
{
get { return true; }
}
这样,您可以看到createNewCommand
传递了CreateStudent
lambda表达式,该表达式在对象更新时被调用。CanCreateStudent
属性也将被调用,根据true
或false
,WPF将允许命令执行。

PropertyBinding
(属性绑定)和CommandBinding
(命令绑定)共同提供了一个完整的解决方案,用于将表示逻辑与表示层分离。这提供了将所有逻辑分离的架构。Microsoft创建了整个Expression blend,使用了MVVM模式,将视图与ViewModel
分离,从而为表示层提供了轻松进行单元测试的机会。我们将在系列文章的后续内容中进一步讨论这个主题。
MultiBinding(多重绑定)
与单个Binding
类似,WPF还引入了MultiBinding
(多重绑定)的概念。在MultiBinding
的情况下,绑定数据依赖于多个源。您可以指定多个绑定表达式,并且实际输出取决于每个绑定表达式。
<TextBlock DockPanel.Dock="Top" >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource mbindingconv}">
<Binding ElementName="lst" Path="Items.Count" />
<Binding ElementName="txtName" Path="Text" />
<Binding ElementName="txtAge" Path="Text" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
这里,TextBlock
的值依赖于3个元素:第一个是ListBox
计数,然后是txtName
和txtAge
。我使用了Converter
来确保我们在IMultiValueConverter
块中找到所有单独的元素,并单独处理每个值。IMultiValueConverter
与IValueConverter
类似,可以接受值并返回绑定到Text
属性的对象。
public class MyMultiBindingConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
string returnval = "Total no of Data {0}, NewData : ";
if (values.Count() <= 0) return string.Empty;
returnval = string.Format(returnval, values[0]);
for (int i = 1; i < values.Count(); i++)
returnval += "- " + values[i];
return returnval;
}
public object[] ConvertBack(object value, Type[]
targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
为简单起见,我只是连接了传递的每个值,并返回输出。
在示例应用程序中,我使用了最简单的绑定,以确保所有内容都来自模型。您可以在本文顶部找到示例应用程序的链接。
结论
我认为您一定很享受这个系列。也欢迎您发表评论。感谢您的阅读。