问题描述
我正在尝试为一个数据网格定义一个新的列模板,以便可以在我的应用程序中重复使用,但是当我尝试使用它时,我得到了:
I am trying to define a new column template for a datagrid that I can reuse across my application, but when I try and use it, I get:
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();
}
}
}
然后我要像这样使用它:
I am then trying to use it like this:
<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>
任何人都可以向我解释执行此操作的正确方法吗?
Can anyone explain to me the proper way to do this?
推荐答案
让我们说
public class ViewModel
{
public bool CanBeUsed {get;set;}
public List<Employee> Employees{get;set;}
}
可能使您感到困惑的几点:
Few points which might have confused you :
-
只有一个实例化的
DataGridBetterCheckBoxColumn
属性.多个记录并不意味着一个属性有多个列实例.而是为每个DataGridColumn
创建多个DataGridCell
.
There will be only one
DataGridBetterCheckBoxColumn
instantiated for a property. Multiple records doesn't mean multiple column instance for a property. Instead multipleDataGridCell
are created for everyDataGridColumn
.
但是
DataGridColumn
不是FrameworkElement
或Visual
,因此它不会出现在VisualTree
中,并且由于它不是FrameworkElement
,因此它没有DataContext
属性.没有DataContext
,您的Binding
将如何工作?问你自己.由于此Column
不能设置其DataContext
,因此它必须具有ElementName
或Source
或RelativeSource
才能使其Binding
正常工作.
DataGridColumn
is not a FrameworkElement
or a Visual
so, it won't appear in the VisualTree
, and since it is not FrameworkElement
so it doesn't have a DataContext
property. Without DataContext
how your Binding
will work ? Ask your self. Since this Column
cant have its DataContext
set, so it must have either a ElementName
, or a Source
or a RelativeSource
for its Binding
to work.
现在,我们知道DataGridColumn
只会有一个实例,因此自然地,它的Binding
应该(使)使用DataGrid
的DataContext
(集合属性将是其中的一部分).
Now, we know that there will be only one instance of a DataGridColumn
, so naturally its Binding
should (made to) use the DataContext
(collection property will be part of this) of DataGrid
.
现在,查看您的Binding
,它的Source
/RelativeSource
在哪里?没有任何东西.现在,RelativeSource
在这里有意义吗?由于DataGridColumn
没有出现在VisualTree
中,因此RelativeSource
不适用于此.我们剩下Source
属性.现在我们应该为Source
设置什么?输入DataContext Inheritance
.
Now, see your Binding
, where is its Source
/ RelativeSource
? There isnt any. Now, will RelativeSource
make any sense here ? As DataGridColumn
does not appear in the VisualTree
, so RelativeSource
wont apply here. We are left with Source
property. What we should set now for the Source
? Enter DataContext Inheritance
.
DataContext继承
DataContext Inheritance
DataContext
继承仅对通过VisualTree
连接的FrameworkElement
有效.因此,我们需要一种机制,可以将DataContext
分解为DataGridColumn
.输入Binding Proxy
.
DataContext
inheritance will only work for FrameworkElement
connected via VisualTree
. So, we need a mechanism with which we can bring down this DataContext
to our DataGridColumn
.Enter 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
.
If we declare an instance of this BindingProxy
as a Resource
, we can get our 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
不见了.
Now, you will see that your nasty Binding Error
is gone.
要使您的CheckBox
绑定正常工作,您需要处理其Loaded
事件.
To make your CheckBox
binding to work properly, you need to handle its Loaded
event.
<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
时,所有响应都会相同.
But now, we have one logical problem here. All our CheckBox
are now bounded to the DataContext
property CanBeUsed
which will remain same. You might be thinking that CanBeUsed
should be a property of Employee
which is ItemsSource
and not DataContext
of DataGrid
. So, when you check / uncheck any CheckBox
, all will respond same.
但是,我们想将我们的isChecked
属性绑定到Employee
记录的某些属性,该属性对于每个DataGridRow
都将保持差异.因此,我们现在需要更改isChecked
的定义,此后,整个代码将如下所示:
But, we want to bind our isChecked
property to some property of Employee
record which will remain diff for every DataGridRow
. So, we need to now change our definition of isChecked
, after which entire code will look like below :
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"/>
如果我错过任何一点,请告诉我.
If I missed any point, do let me know.
这篇关于WPF自定义DatagridColumn绑定问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!