Whether it is defining a control or a user control, a function will be used - binding. The written name: element binding. This means that the bound elements can achieve data synchronization. In the author's opinion, the introduction of this feature into WPF is really perfect. Programming is more concrete. Especially when combined with the MVVM model, it is perfect. The author is not an academic. It would be unrealistic to tell it comprehensively. Let’s talk about Binding from the author’s experience.
The most common way to use it is that its target element is the DataContext object on the control. As follows:
<TextBlock Grid.Column="0" Text="{Binding DishName}" Style="{StaticResource TakingDishDishNameTextStyle}" />
DataContext This attribute is on the FrameworkElement class. In other words, most controls will have their own DataContext. Then we generally only set the DataContext property at the outermost layer. In order to understand DataContext binding more clearly. The author made a simple example. The author sets the DataContext value for the outermost Window. At the same time, the DataContext value is also set for his internal Grid. But they are not the same object type, they just have the same attributes. The following
<Window x:Class="Wpf.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:aomi="http://aomiwpf.com/ModernUI"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local ="clr-namespace:Wpf"Title="MainWindow" Height="350" Width="525"><Window.DataContext><local:WindDataContext /></Window.DataContext><Grid><Grid.DataContext><local:GridDataContext /></Grid.DataContext><TextBlock Text="{Binding TestName}"></TextBlock></Grid></Window>
execution results:
The experiment can prove that the target element of the standard binding method is DataContext. It will find the DataContext closest to the currently bound element. Let's make a hypothesis - what would it look like if the property TestName in the GridDataContext class was replaced with TestName1? As follows
1 public class GridDataContext : NotifyPropertyChanged 2 { 3 private string _testName1 ="GridDataContext"; 4 5 public string TestName1 6 { 7 set 8 { 9 10 if (this._testName1 != value)11 {12 this._testName1 = value;13 OnPropertyChanged("TestName1");14 }15 }16 get { return this._testName1; }17 }18 }
Execution results:
Sorry! The author thought he would go to the property TestName of Window's DataContext. Apparently he doesn't. It also explains that he will only search in the closest DataContext. I won't go straight up to find them one by one.
It is worth noting that if you just write {Binding} above, it will bind the current DataContext. rather than his attributes.
During the development process, we often hope that an element can be bound to an attribute on another element. As long as the attribute of another element changes, an element will be notified to change together. At this time, you have to use the following method.
{Binding ElementName=SomeThingName, Path=Text}
ElementName: Indicates the name of the element.
Path: Represents the attributes of the element object.
In fact we can think of a problem. Does binding only affect one party? This is the mode used in binding. As follows
{Binding ElementName=SomeThindName, Path=Text,Mode=TwoWay}
TwoWay: Causes changes to the source attribute or the target attribute to automatically update the other.
OneWay: When the binding source (source) changes, update the binding target (target) property.
OneTime: Update the binding target when the application starts or the data context changes.
OneWayToSource: Updates the source property when the target property changes.
The above usage is relatively common. It is also relatively simple. Why not let us take a look at a binding expression in an open source project. As follows
<Button Command="{Binding Source={x:Static SystemCommands.MinimizeWindowCommand}}" ToolTip="{x:Static modernui:Resources.Minimize}" Style="{StaticResource SystemButton}"> <Button.Content> <Grid Width="13" Height="12" RenderTransform="1,0,0,1,0,1"> <Path Data="M0,6 L8,6 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center" Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2" /> </Grid> </Button.Content></Button>
I don’t know if you can understand it clearly. The above means that the Foreground of the parent node Button and the Stroke of the current Path are bound together. The main key is AncestorType. Used to specify the type of father. Mode is a RelativeSourceMode type. He has four values. as follows.
PreviousData: Used for data lists, meaning previous data items. That is the display above the data collection. Controls not included.
TemplatedParent: used for binding on templates.
Self: Bind to the attributes on the element itself.
FindAncestor: used to find parent elements.
As long as you explain it like this, you can understand that RelativeSource is used to specify relative source elements. That is the target element.
In fact, there is another possible way to write the above expression. That is, there is one more depth (AncestorLevel) used to limit the parent. As follows
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}, AncestorLevel=2}, Path=Name}
Note: If you want to modify the bound value, you must use a converter. As follows
{Binding ElementName=SomeThindName, Path=Text,Mode=TwoWay,Converter=XXXConverter}
When developing custom controls, we often use an expression. As follows
Width="{TemplateBinding Width}"
The above writing is just an abbreviation. The complete list is as follows
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}"
It can be said that the above content is what the author uses most. Next let's look at some binding other content points. That is, content that is not common.
1.StringFormat function. Equivalent to the string.format function. Give an example. If we want to add "¥" in front of the amount, we can use it. As follows
<TextBlock Text="{Binding MoneyText, StringFormat=¥{0}}" />
如果不是这样子做的话,你就不得不给“¥”一个TextBlock来显示,或是MoneyText变成string类型,然后设置值里面加上¥。但是笔者这里却是double类型的。所以用StringFormat的功能有就可以完美的决解了显示“¥”的问题。
执行结果:
2.TargetNullValue的功能,用于绑定目标是一个null值的时候,要显示的内容。如下笔者给NullName赋null。
<TextBlock Text="{Binding NullName, TargetNullValue=aomi}" />
执行结果:
3.FallbackValue的功能,用于绑定目标是发生错误的时候,要显示的内容。如下
<TextBlock Text="{Binding NullName, FallbackValue=aomi}" />
执行结果笔者就不贴了。
文章最后。在来说明一个不常用的功能——PriorityBinding。这个功能笔者不好说。只能让读者们自行体会吧。他主要用于在加载时间比较多的时候,要显示的信息。比如显示“正在加载中...”。笔者做了例子吧。
Xaml:
<Window x:Class="Wpf.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:aomi="http://aomiwpf.com/ModernUI"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local ="clr-namespace:Wpf"Title="MainWindow" Height="350" Width="525"><Window.DataContext><local:MainViewModel /></Window.DataContext><Grid><TextBlock><TextBlock.Text><PriorityBinding><Binding Path="UserName" IsAsync="True"></Binding><Binding Path="LoadingName"></Binding></PriorityBinding></TextBlock.Text></TextBlock></Grid></Window>
ViewModel:
public class MainViewModel : NotifyPropertyChanged {private string _userName ="Haya";private string _loadingName = "正在加载中...";public string UserName {set{if (this._userName != value) {this._userName = value; OnPropertyChanged("UserName"); } }get { Thread.Sleep(7000);return this._userName; } }public string LoadingName {set{if (this._loadingName != value) {this._loadingName = value; OnPropertyChanged("LoadingName"); } }get { return this._loadingName; } } }
执行结果:
七秒后:
本章的内容比较简单。笔者只是讲述了常用的一些知识点。但是必不是说就这些了。例如Binding还关系到Xml的绑定和集合的绑定功能。读者们可以自行去找一下资料。
The above is the detailed content of A brief discussion on Binding expressions in WPF. For more information, please follow other related articles on the PHP Chinese website!