我正在尝试为我可以在我的应用程序中重用的数据网格定义一个新的列模板,但是当我尝试使用它时,我得到:
System.Windows.Data错误:2:找不到管理FrameworkElement 或目标元素的FrameworkContentElement。 BindingExpression:路径= CanLogin;的DataItem = NULL;目标元素是 ' DataGridBetterCheckBoxColumn' (的HashCode = 56040243);目标属性是 '&器isChecked#39; (键入'对象')
XAML for Column:
<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>
有人能告诉我这样做的正确方法吗?
答案 0 :(得分:1)
让我们说
public class ViewModel
{
public bool CanBeUsed {get;set;}
public List<Employee> Employees{get;set;}
}
可能让你困惑的几点:
只有一个DataGridBetterCheckBoxColumn
实例化了一个属性。多条记录并不意味着属性的多列实例。而是为每个DataGridCell
创建多个DataGridColumn
。
但是
DataGridColumn
不是FrameworkElement
或Visual
因此,它不会出现在VisualTree
中,因为它不是FrameworkElement
所以它没有DataContext
属性。没有DataContext
您的Binding
将如何运作?问你自己。由于此Column
无法设置DataContext
,因此其ElementName
必须为Source
或RelativeSource
或Binding
上班。
现在,我们知道只有DataGridColumn
的一个实例,所以Binding
应该(使)使用DataContext
(集合属性将是此属性的一部分) )DataGrid
。
现在,看看您的Binding
,Source
/ RelativeSource
在哪里?没有。现在,RelativeSource
会在这里有意义吗?由于DataGridColumn
未显示VisualTree
,因此RelativeSource
不适用于此处。我们留有Source
财产。我们现在应该为Source
设置什么?输入DataContext Inheritance
。
DataContext继承
DataContext
继承仅适用于通过FrameworkElement
连接的VisualTree
。因此,我们需要一种机制,我们可以将此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
而非DataContext
DataGrid
。因此,当您选中/取消选中任何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"/>
如果我错过任何一点,请告诉我。