【wpf】深度解析,Bingding是如何寻找数据源的
数据源存放位置
目前我用存放数据源的属性有:
- Resources
- ItemsSource
- DataContext
一般控件都有Resources和DataContext属性,列表控件会多一个ItemsSource。
Resources可以放多个资源,但是需要给每一个资源指定一个key。
<Page.Resources>
<local:TestMode2 x:Key="TM2"/>
<local:TestMode x:Key="TM1"/>
</Page.Resources>
而 ItemsSource和DataContext只能放一个对象(不需要指定key)
<Page.DataContext>
<local:TestMode2/>
</Page.DataContext>
Bingding 表达式
Bingding 表达式 有一些常用的方法:
- Source
- RelativeSource
- Path
Path 相当于是一个过滤器,用来选择具体是哪一个属性。
Source 是指定资源的位置,Source 指定的资源是去Resources中找,寻找资源时直接指定key,就能一步到位。
<TextBlock Text="{Binding Source={StaticResource TM2 }, Path=TestDate }"/>
但如果,我们在绑定表达式中,不写Source 指定资源时,绑定表达式就会自动到ItemsSource和DataContext 中去找。
<TextBlock Text="{Binding Path=Value}"/>
而且,默认的寻找方式是一层层的往上找,如果在某一层找到ItemsSource或DataContext 被设置过,不管Path指定的属性是否存在,就会停止继续往上找。
那么是否改变这种默认的查找方式呢?答案是使用RelativeSource 。
你会发现 RelativeSource 和 Source 有着本质的区别, 它并不是指定源的位置,而是用于改变默认的寻找资料的方式。(因为此时 源的位置是不确定的,是需要按照一定的规则去寻找的)
<TextBlock Text="{Binding Path=DataContext.DM1.Value,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Page}}"/>
感悟
在使用数据绑定时,我们更喜欢,使用不指定Source 的方式,此时会自动去ItemsSource或DataContext 中寻找,这种方式更加的灵活。
RelativeSource
接下来,我将介绍 RelativeSource 是如何改变默认的寻找资源的方式。并显示实际的例子。
先看一下它的具体写法
<TextBlock Background="Orange" Text="{Binding Path=DataContext.DM1.Value,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=
{x:Type Page}}}"/>
最主要就是这个Mode,它定义了寻找源的几种规则:
//
// 摘要:
// Describes the location of the binding source relative to the position of the
// binding target.
public enum RelativeSourceMode
{
//
// 摘要:
// Allows you to bind the previous data item (not that control that contains the
// data item) in the list of data items being displayed.
PreviousData = 0,
//
// 摘要:
// Refers to the element to which the template (in which the data-bound element
// exists) is applied. This is similar to setting a System.Windows.TemplateBindingExtension
// and is only applicable if the System.Windows.Data.Binding is within a template.
TemplatedParent = 1,
//
// 摘要:
// Refers to the element on which you are setting the binding and allows you to
// bind one property of that element to another property on the same element.
Self = 2,
//
// 摘要:
// Refers to the ancestor in the parent chain of the data-bound element. You can
// use this to bind to an ancestor of a specific type or its subclasses. This is
// the mode you use if you want to specify System.Windows.Data.RelativeSource.AncestorType
// and/or System.Windows.Data.RelativeSource.AncestorLevel.
FindAncestor = 3
}
FindAncestor
表示找到最顶层,一般配合AncestorType使用,AncestorType指定一种类型,表示遇到这种类型就停止寻找。
Self
表示Bingding的属性就在自身对象上寻找。
TemplatedParent
以下,就是定义了一个控件模板:控件模板中就经常用到 TemplatedParent
这个就是告诉wpf,这个数据源,要在 应用了这个模板的对象中去找!
PreviousData
这个是表示绑定上一个子项,目前感觉只能在数据模板中才会生效。
以下是具体的代码部分:
<Page x:Class="WpfTest.绑定源的寻找"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="AliceBlue"
Title="绑定源的寻找">
<Page.Resources>
<local:TestMode2 x:Key="TM2"/>
<local:TestMode x:Key="TM1"/>
</Page.Resources>
<Page.DataContext>
<local:TestMode2/>
</Page.DataContext>
<UniformGrid Rows="3">
<ItemsControl ItemsSource="{Binding Path=ValueList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<UniformGrid Columns="4" Rows="4">
<TextBlock Text="{Binding Path=Value}"/>
<TextBlock Background="Orange" Text="{Binding Path=DataContext.DM1.Value,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Page}}"/>
<TextBlock Background="Green" Text="{Binding Path=DataContext.TestDate,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Page}}"/>
<TextBlock Text="{Binding }"/>
<TextBlock Text="{Binding Source={StaticResource TM2 }, Path=TestDate }"/>
</UniformGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel>
<TextBlock Background="Orange" Text="{Binding Path=DataContext.DM1.Value,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Page}}"/>
<TextBlock Background="Green" Text="{Binding Path=ActualWidth,
RelativeSource={RelativeSource Mode=Self}}"/>
<ItemsControl ItemsSource="{Binding Path=ValueList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Value}"/>
<TextBlock Text="{Binding Path=Value,
RelativeSource={RelativeSource Mode=PreviousData}}" Foreground="Red" Margin="10,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UniformGrid>
</Page>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace WpfTest
{
public class TestMode2
{
public string TestDate { get; set; } = "test";
public DataModel1 DM1 { get; set; } = new DataModel1();
public DataModel2 DM2 { get; set; } = new DataModel2();
public class DataModel1 : INotifyPropertyChanged
{
private int _value = 1;
public int Value
{
get { return _value; }
set
{
_value = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataModel2 : INotifyPropertyChanged
{
private int _value = 2;
public int Value
{
get { return _value; }
set
{
_value = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public int MyProperty { get; set; } = 3;
}
public List<DataModel2> ValueList { get; set; } = new List<DataModel2>()
{
new DataModel2{ Value=1},
new DataModel2{ Value=2},
new DataModel2{ Value=3},
new DataModel2{ Value=4},
};
}
}
2022年12月2日 修正
AncestorType={x:Type xxx}, 这里的x:Type不能省略。
2022年12月6日
完善对 TemplatedParent 的理解。
作者:宋桓公
出处:http://www.cnblogs.com/douzi2/
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。