WPF自定义DatagridColumn绑定问题 [英] WPF Custom DatagridColumn Binding issue

查看:111
本文介绍了WPF自定义DatagridColumn绑定问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为一个数据网格定义一个新的列模板,以便可以在我的应用程序中重复使用,但是当我尝试使用它时,我得到了:

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:

System.Windows.Data错误:2:找不到管理FrameworkElement 或FrameworkContentElement作为目标元素. BindingExpression:Path = CanLogin; DataItem = null;目标元素是 'DataGridBetterCheckBoxColumn'(HashCode = 56040243);目标属性是 'isChecked'(类型为'Object')

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=CanLogin; DataItem=null; target element is 'DataGridBetterCheckBoxColumn' (HashCode=56040243); target property is 'isChecked' (type 'Object')

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 :

  1. 只有一个实例化的DataGridBetterCheckBoxColumn属性.多个记录并不意味着一个属性有多个列实例.而是为每个DataGridColumn创建多个DataGridCell.

  1. There will be only one DataGridBetterCheckBoxColumn instantiated for a property. Multiple records doesn't mean multiple column instance for a property. Instead multiple DataGridCell are created for every DataGridColumn.

但是

DataGridColumn不是FrameworkElementVisual,因此它不会出现在VisualTree中,并且由于它不是FrameworkElement,因此它没有DataContext属性.没有DataContext,您的Binding将如何工作?问你自己.由于此Column不能设置其DataContext,因此它必须具有ElementNameSourceRelativeSource才能使其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应该(使)使用DataGridDataContext(集合属性将是其中的一部分).

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而不是DataGridDataContext.因此,当您选中/取消选中任何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绑定问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆