WPF自定义DatagridColumn绑定问题

时间:2016-09-29 09:23:02

标签: c# wpf binding datagrid

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

  

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>

有人能告诉我这样做的正确方法吗?

1 个答案:

答案 0 :(得分:1)

让我们说

public class ViewModel
{
   public bool CanBeUsed {get;set;}
   public List<Employee> Employees{get;set;}
}

可能让你困惑的几点:

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

    但是

    DataGridColumn不是FrameworkElementVisual因此,它不会出现在VisualTree中,因为它不是FrameworkElement所以它没有DataContext属性。没有DataContext您的Binding将如何运作?问你自己。由于此Column无法设置DataContext,因此其ElementName必须为SourceRelativeSourceBinding上班。

    现在,我们知道只有DataGridColumn的一个实例,所以Binding应该(使)使用DataContext(集合属性将是此属性的一部分) )DataGrid

    现在,看看您的BindingSource / 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已经消失了。

  2. 要使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时,所有人都会回复相同的内容。

  3. 但是,我们希望将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"/>
    

    如果我错过任何一点,请告诉我。