WPF 几种绑定 (笔记)
资源与绑定DataContext(绑定到我们定义的属性)
xmlns:local="clr-namespace:模板"<Grid>
<Grid.Resources><local:MyViewModel x:Key="SharedViewModel"/>
</Grid.Resources></Grid>
以上仅仅是代表放了一个 "ViewModel 字典"
完整引用 是"模板\MyViewModel\SharedViewModel" 然后并没有去使用它
<Grid.DataContext><Binding Source="{StaticResource SharedViewModel}"/>
</Grid.DataContext>
然后要想使用它就得通过指定" Source="{StaticResource SharedViewModel}" 这样就表示Grid绑定上下文对象是我资源定义的"SharedViewModel",这样一来Grid里面的子控件都能去访问到资源"SharedViewModel"里面的内容。
而"StaticResource "标注这个是一个静态绑定 加载后不会再次去加载了也就是上下文都用的同一个"SharedViewModel",在grid里面所有的控件共享一个 "SharedViewModel"资源.
单独互相不干扰的绑定(DataContext)
<Grid><Grid.ColumnDefinitions><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><TextBlock x:Name="tex" Text="{Binding DateTimeProperty, StringFormat=yyyy-MM-dd}" ><TextBlock.DataContext><local:MyViewModel/></TextBlock.DataContext></TextBlock><DatePicker Grid.Row="1"SelectedDate="{Binding DateTimeProperty, Mode=TwoWay}" ><DatePicker.DataContext><local:MyViewModel/></DatePicker.DataContext></DatePicker></Grid>
上面实在Grid里面放了一个 "DatePicker"用于选择日期,"TextBlock "显示选择的内容,而且两个控件都绑定了"<local:MyViewModel/>" 这样就代表每次都是绑定一个新的"MyViewModel"对象,就像每次都去new一个对象。所有虽然他们两个直接是双向绑定但是 由于绑定的"MyViewModel"不是同一个所有运行后修改日期 上边的TextBlock 内容并不会变化。
<Grid.DataContext><local:MyViewModel/></Grid.DataContext><TextBlock x:Name="tex" Text="{Binding DateTimeProperty, StringFormat=yyyy-MM-dd}" ></TextBlock><DatePicker Grid.Row="1"SelectedDate="{Binding DateTimeProperty, Mode=TwoWay}" ></DatePicker>
但是只需要小修改一下让Grid去绑定一个新的"MyViewModel"这样每次就能实现"Grid"子控件和Grid绑定的对象都是同一个"MyViewModel"。虽然这样也能绑定并能每次显示值变化,但是这样每次绑定都会重新去NEW一个"MyViewModel"对象。可以在"MyViewModel"构造函数打上断点就可以看出。
Text="{Binding DateTimeProperty, StringFormat=yyyy-MM-dd}" >里面的DateTimeProperty是一个MyViewModel类里面的一个属性,类型是日期时间类型,后边跟着的StringFormat=yyyy-MM-dd就表示格式化
要想把控件属性绑定到我们自定义属性就直接用Binding(前提是我们要提前引用资源)
绑定到控件属性(重写类型转换)(ElementName)
<TextBox Grid.Row="1"FontSize="22"Text="{Binding ElementName=slider,Path=Value,Mode=OneWayToSource}" ></TextBox>
<Sliderx:Name="slider"Grid.Row="0"Maximum="10"Minimum="0"SmallChange="1" />
表达方式 | 实际含义 |
---|---|
TextBox → Slider | TextBox 是 数据源(Source),Slider 是 目标(Target) |
上面我定义两个控件,滑块的value绑定到 txtbox的text上面,
Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay}"
ElementName=slider:类型
Path=Value:要绑定到什么属性
Mode=TwoWay: 绑定模式
Mode | 说明 |
---|---|
OneWay | Source → Target(单向) |
TwoWay | Source ⇄ Target(双向) |
OneWayToSource | Target → Source(反向单向) |
OneTime | Source → Target(只在加载时绑定一次) |
总的来说我的理解是ElementName 里面可以去按照x:name定义的名称拿到对应的控件,再通过path属性指定控件的属性,这样来完成控件属性到控件属性的绑定(要把控件的属性帮到拎一个控件上面用它就对了)
我们来试一下OneWayToSource
<TextBoxGrid.Row="1"FontSize="22"Text="{Binding ElementName=slider, Path=Value, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"/><Sliderx:Name="slider"Grid.Row="0"Maximum="10"Minimum="0"SmallChange="1" />
咋一看好像就是我们拖动滑块 数字不变化,而我们文本框输入后滑块变化这个功能,确实也能运行,但是会出现报错,因为我们滑块的value可以是int也可以是小数类型。而text的值是字符串没有进行转换会出现类型转换错误。
这个时候可以自己重写转换器来实现
class StringToDoubleConverter : IValueConverter{// TextBox → Sliderpublic object Convert(object value, Type targetType, object parameter, CultureInfo culture){return value?.ToString();}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){if (value is string str && double.TryParse(str, out double result)){return result;}return 0.0; // 默认值}}
我们做了一个简单判断如果是字符串并且能够转换成double我们把字符串转换位double,接下来看我们如何使用它。
<Grid.Resources><local:StringToDoubleConverter x:Key="StrToDoubleConverter2"/><local:MyViewModel x:Key="MyViewModel"/></Grid.Resources><TextBox Grid.Row="1"FontSize="22"Text="{Binding ElementName=slider,Path=Value,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource StrToDoubleConverter2}}" ></TextBox><Sliderx:Name="slider"Grid.Row="0"Maximum="10"Minimum="0"SmallChange="1" />
我先是定义两个资源
<local:StringToDoubleConverter x:Key="StrToDoubleConverter2"/>
<local:MyViewModel x:Key="MyViewModel"/>
接着用 Converter={StaticResource StrToDoubleConverter2}}" 去绑定我写的类型转换,这样就达到了想要的效果
UpdateSourceTrigger=PropertyChanged:比较特殊,可以把这条删除再看看是不是输入字符串后滑块不改变了,当你离开文本框后滑块值才发送改变。(当然也可以 UpdateSourceTrigger=PropertyChanged,Delay=1000,加点延迟)
触发时机 | 描述 | 适用场景 |
---|---|---|
PropertyChanged | 属性值每变化一次就更新源 | 实时联动、搜索、滑块绑定 |
LostFocus (默认) | 只有失去焦点才更新 | 一般表单输入、性能优化 |
Explicit | 需要代码显式调用更新 | 貌似我用不上 |
绑定当中的 (Source )属性介绍
<TextBlock Text="{Binding DateTimeProperty, Source={StaticResource MyViewModel}}" /><TextBlock Text="{Binding DateTimeProperty}" DataContext="{Binding }"/>
这里需要特别注意 第一节绑定是
{Binding DateTimeProperty, Source={StaticResource MyViewModel}}" 多个参数绑定 逗号隔开
第二节 绑定是"{Binding DateTimeProperty}" DataContext="{Binding }" 不同属性或绑定 空格隔开
用Source 可以不依靠DataContext,跳过它并绑定到任意Source 指定的对象属性上,准确说就是忽略 DataContext
,直接绑定到资源名为 MyViewModel
的对象的 DateTimeProperty 属性。
总结 当你的上下文绑定不再满足你要求 ,想绑定,非DataContext里面的属性就用 Source去指定来绑定。
绑定到上级属性(RelativeSource)
<TextBlock Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}" Text="1213" />
这个绑定和之前控件属性与控件属性绑定又点类似
1.指定绑定的属性"Binding Path=Background"
2.RelativeSource源设定:RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}//是从什么类型绑定过来,通过查找祖先元素
Mode=FindAncestor:向上查找祖先元素
AncestorType=StackPanel:查找的类型是StackPanel
这样就会一级一级往上找找到最近的一个满足要求的绑定上去
自己的另一个属性 | RelativeSource Self |
上层容器(如 Grid, Window) | RelativeSource AncestorType=XXX |
模板中绑定外部控件的属性 | RelativeSource TemplatedParent |
上一个数据项(ItemsControl 场景) | RelativeSource PreviousData |
最后两种貌似现在我还没学到控件模板看视频应该后边控件模板绑定会用到先记录下来把!