WPF自定义DatagridColumn绑定问题
本文关键字:问题 绑定 DatagridColumn 自定义 WPF | 更新日期: 2023-09-27 18:09:19
我试图为数据网格定义一个新的列模板,我可以在我的应用程序中重用,但是当我尝试使用它时,我得到:
XAML for Column:System.Windows。数据错误:2:无法找到治理框架元素或FrameworkContentElement作为目标元素。BindingExpression:路径= CanLogin;DataItem =零;目标元素是"DataGridBetterCheckBoxColumn"(HashCode = 56040243);目标属性为'isChecked' (type 'Object')
<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>
谁能给我解释一下做这件事的正确方法?
假设我们有
public class ViewModel
{
public bool CanBeUsed {get;set;}
public List<Employee> Employees{get;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
上,CC_46将保持不变。你可能会认为CanBeUsed
应该是Employee
的属性,而不是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"/>
如果我遗漏了什么,请告诉我。