【wpf】ItemsControl 的Binding 小技巧
ItemsControl 非常常用和好用的控件,我经常将之用于配置界面!
比如这么一个配置界面:
整体是一个ItemsControl,每个子界面就是其中的一个Item。
ItemsControl 的 ItemsSource 绑定到 ParameterInfo 的集合
public ObservableCollection<ParameterInfo> configInfos { get; set; } = new ObservableCollection<ParameterInfo>();
每个Item 绑定的内容都对应集合元素的项 ParameterInfo 。
public class ParameterInfo : BindableBase
{
public const string file_name = "调试参数";
#region 模板匹配相关参数
public MatchInfo matchInfo { get; set; } = new MatchInfo();
private string mdPath;
/// <summary>
/// 模板路径
/// </summary>
public string MdPath
{
get { return mdPath; }
set { SetProperty(ref mdPath, value); }
}
#endregion
#region 瑕疵检测
public DefectInfo defectInfo { get; set; } = new DefectInfo();
#endregion
private string proName = "未命名";
/// <summary>
/// 项目名称
/// </summary>
public string ProName
{
get { return proName; }
set { SetProperty(ref proName, value); }
}
}
这里有个问题,每个子界面都有一个“打开” 按键。而 打开按键的命令绑定不在ParameterInfo 中,而是在ViewModel中,如果直接填写 命令名称,就会绑定失败。
于是就有写法:
<Button MinWidth="50" DockPanel.Dock="Right"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.OpenMdCmd}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}">打开</Button>
首先我们看到:
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.OpenMdCmd}"
就是解决在ItemsControl,无法找到数据源的问题,使用了 RelativeSource,通过 FindAncestor的方式,找到了 UserControl 的 DataContext 中的 命令属性定义。
我在之前的文章中也介绍到了:
此时,OpenMdCmd确实已经绑定成功,但是有个问题,每个子界面的 “打开按钮” 都会调用这个命令,OpenMdCmd对应的方法,如何得知具体是哪个子界面的调用的呢?
这里,就进入今天的主题内容,
CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}"
我们这里的 CommandParameter,就是给CMD传递参数,而且也使用了 RelativeSource 的方式。
这里的 Mode 使用的 TemplatedParent。这种方式,之前的那篇文章也提到过,不过当时只知道会用于 “控件模板”, 想不到在这里,用到“数据模板” 也有它的作用。
于是 OpenMdCmd 里 就接收一下这个参数,看看是个啥?
名称 | 值 | 类型 | |
---|---|---|---|
◢ | item | {"子母勾"} | object {System.Windows.Controls.ContentPresenter} |
◢ Content | "子母勾" | object {VisualMatching.Models.ParameterInfo} | |
proName | "子母勾" | string | |
MdPath | null | string | |
ProName | "子母勾" | string | |
▶ PropertyChanged | {Method = | System.ComponentModel.PropertyChangedEventHandler | |
▶ defectInfo | VisualMatching.Models.DefectInfo | ||
▶ matchInfo | VisualMatching.Models.MatchInfo | ||
mdPath | null | string | |
▶ 静态成员 |
就是 System.Windows.Controls.ContentPresenter,这个就是数据模板对应的玩意儿。
关于 ContentPresenter 我的另一篇文章有详细讲解:
继续往里面看,它其中的一个属性,Content,就是我们自定义的子项类 ParameterInfo!
也就是说Content就是你当前选择的子项Item对应的数据。那么在进入主题时,提出问题也就迎刃而解了!
最近,再通过 Path 属性 过滤一下,就能直接拿到 ParameterInfo 对象:
CommandParameter="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"
小结:
1 xaml写法:
<Button Content="+"
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.AddSelectValueCmd}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
2 后台写法:(这里参数直接用ContentPresenter类型)
AddSelectValueCmd = new DelegateCommand<ContentPresenter>((obj) => {
var a = obj.Content;
});
注意这里的obj是ContentPresenter类型,而obj.Content对应就是binding数据的内容!
如果后台想直接得到Content,我们就可以在前台中添加 Path=Content 进行过滤:
CommandParameter="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"
3 最优写法
最后我觉得最优的写法就是:
<Button Content="+"
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.AddSelectValueCmd}"
CommandParameter="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
不过此时 DelegateCommand 的泛型类型就不是 ContentPresenter,而是实际 Content 对应的类型!
相关文章
- WPF入门教程系列十七——WPF中的数据绑定(三)
- WPF入门教程系列八——布局之Grid与UniformGrid(三)
- 演练:我的第一个 WPF 桌面应用程序 https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/getting-started/walkthrough-my-first-wpf-desktop-application
- 【WPF】 BasedOn的用法
- [WPF 自定义控件]创建包含CheckBox的ListBoxItem
- 用WPF自定义控件实现消息提示
- WPF列表控件第一行子项样式无效问题
- WPF 播放音频:同步、异步、循环、播放与暂停
- 【WPF】获取下拉列表(ComboBox)的选项(ComboBoxItem)的内容