[英]WPF Custom DatagridColumn Binding issue
我正在尝试为数据网格定义一个新的列模板,我可以在我的应用程序中重复使用它,但是当我尝试使用它时,我得到:
System.Windows.Data 错误:2:找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement。 BindingExpression:Path=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>
任何人都可以向我解释这样做的正确方法吗?
可以说我们有
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
继承仅适用于通过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"/>
如果我错过了任何一点,请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.