WPF 自定义 DatagridColumn 绑定问题
Posted
技术标签:
【中文标题】WPF 自定义 DatagridColumn 绑定问题【英文标题】:WPF Custom DatagridColumn Binding issue 【发布时间】:2017-02-07 13:10:12 【问题描述】:我正在尝试为我可以在我的应用程序中重复使用的数据网格定义一个新的列模板,但是当我尝试使用它时,我得到:
System.Windows.Data 错误:2:找不到管理 FrameworkElement 或 FrameworkContentElement 为目标元素。 绑定表达式:路径=CanLogin;数据项=空;目标元素是 'DataGridBetterCheckBoxColumn' (HashCode=56040243);目标属性是 “isChecked”(类型“对象”)
列的 XAML:
<DataGridTemplateColumn x:Class="BACSFileGenerator.UserControls.DataGridBetterCheckBoxColumn"
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:BACSFileGenerator.UserControls"
mc:Ignorable="d"
x:Name="ColumnRoot"
>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="Binding isChecked, Source=x:Reference Name=ColumnRoot"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
代码背后:
using System.Windows;
using System.Windows.Controls;
namespace BACSFileGenerator.UserControls
public partial class DataGridBetterCheckBoxColumn : DataGridTemplateColumn
public object isChecked
get return (object)GetValue(isCheckedProperty);
set SetValue(isCheckedProperty, value);
public static readonly DependencyProperty isCheckedProperty =
DependencyProperty.Register("isChecked", typeof(object),
typeof(DataGridBetterCheckBoxColumn), new PropertyMetadata(null));
public DataGridBetterCheckBoxColumn()
InitializeComponent();
然后我尝试像这样使用它:
<DataGrid Margin="0,0,0,10" ItemsSource="Binding UserAccessGrid" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="User" Binding="Binding User" IsReadOnly="True"/>
<uc:DataGridBetterCheckBoxColumn Header="Login" isChecked="Binding CanLogin"/>
<uc:DataGridBetterCheckBoxColumn Header="Export Payments" isChecked="Binding canExportPayments"/>
<uc:DataGridBetterCheckBoxColumn Header="Create File Layouts" isChecked="Binding canCreateFileLayouts"/>
<uc:DataGridBetterCheckBoxColumn Header="Change User Access" isChecked="Binding canChangeUserAccess"/>
</DataGrid.Columns>
</DataGrid>
谁能向我解释一下正确的做法?
【问题讨论】:
【参考方案1】:假设我们有
public class ViewModel
public bool CanBeUsed get;set;
public List<Employee> Employeesget;set;
可能让您感到困惑的几点:
一个属性只会有一个DataGridBetterCheckBoxColumn
实例化。多个记录并不意味着属性的多个列实例。而是为每个DataGridColumn
创建多个DataGridCell
。
但是
DataGridColumn
不是FrameworkElement
或Visual
,所以它不会出现在VisualTree
中,因为它不是FrameworkElement
,所以它没有DataContext
属性。没有DataContext
你的Binding
将如何工作?问你自己。由于此Column
不能设置其DataContext
,因此它必须具有ElementName
、Source
或RelativeSource
才能使其Binding
工作。
现在,我们知道DataGridColumn
将只有一个实例,因此它的Binding
应该(被制作为)使用DataGrid
的DataContext
(集合属性将是其中的一部分)。
现在,看看你的Binding
,它的Source
/ RelativeSource
在哪里?没有。现在,RelativeSource
在这里有意义吗?由于DataGridColumn
没有出现在VisualTree
中,所以RelativeSource
不会在这里应用。我们留下了Source
财产。我们现在应该为 Source
设置什么?输入DataContext Inheritance
。
DataContext 继承
DataContext
继承仅适用于通过VisualTree
连接的FrameworkElement
。因此,我们需要一种机制,通过它我们可以将这个DataContext
降低到我们的DataGridColumn
。
输入Binding Proxy
。
public class BindingProxy : Freezable
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
return new BindingProxy();
#endregion
public object Data
get return (object)GetValue(DataProperty);
set SetValue(DataProperty, value);
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
如果我们将这个BindingProxy
的一个实例声明为Resource
,我们可以得到我们的Source
。
<DataGrid Margin="0,52,0,10" ItemsSource="Binding Records" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False">
<DataGrid.Resources>
<uc:BindingProxy x:Key="FE" Data="Binding"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn x:Name="dgt" Header="User" Binding="Binding User" IsReadOnly="True"/>
<uc:DataGridBetterCheckBoxColumn isChecked="Binding Data.CanBeUsed, Source=StaticResource FE" Header="CanLogin"/>
</DataGrid.Columns>
</DataGrid>
现在,你会看到你讨厌的Binding Error
不见了。
要使您的CheckBox
绑定正常工作,您需要处理其Loaded
事件。
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Loaded="CheckBox_Loaded"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
代码:
void CheckBox_Loaded(object sender, RoutedEventArgs e)
Binding b = new Binding();
b.Path = new PropertyPath("isChecked");
b.Mode = BindingMode.TwoWay;
b.Source = this;
CheckBox cb = sender as CheckBox;
BindingOperations.SetBinding(cb , CheckBox.IsCheckedProperty, b);
但是现在,我们这里有一个逻辑问题。我们所有的CheckBox
现在都绑定到DataContext
属性CanBeUsed
,这将保持不变。您可能会认为CanBeUsed
应该是Employee
的属性,它是ItemsSource
而不是DataGrid
的DataContext
。因此,当您选中/取消选中任何CheckBox
时,都会做出相同的响应。
但是,我们希望将我们的isChecked
属性绑定到Employee
记录的某个属性,该记录对于每个DataGridRow
都将保持差异。因此,我们现在需要更改isChecked
的定义,之后整个代码将如下所示:
public partial class DataGridBetterCheckBoxColumn : DataGridTemplateColumn
public BindingBase isChecked get; set;
public DataGridBetterCheckBoxColumn()
InitializeComponent();
void CheckBox_Loaded(object sender, RoutedEventArgs e)
CheckBox cb = sender as CheckBox;
BindingOperations.SetBinding(cb , CheckBox.IsCheckedProperty, isChecked);
用法:
<uc:DataGridBetterCheckBoxColumn isChecked="Binding CanLogin, Mode=TwoWay" Header="CanLogin"/>
如果我遗漏了任何一点,请告诉我。
【讨论】:
以上是关于WPF 自定义 DatagridColumn 绑定问题的主要内容,如果未能解决你的问题,请参考以下文章
仅当对象属性为 true 时才绑定 DataGridColumn
WPF - 如何绑定 DataGridTemplateColumn
标头为“*”的 DataGridColumn 已存在于 DataGrid 的 Columns 集合中