繁体   English   中英

WPF 自定义 DatagridColumn 绑定问题

[英]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;}
}

可能让您感到困惑的几点:

  1. 对于一个属性,只会实例化一个DataGridBetterCheckBoxColumn 多个记录并不意味着一个属性的多个列实例。 相反,多DataGridCell是针对每个创建DataGridColumn

    DataGridColumn不是FrameworkElementVisual因此,它不会出现在VisualTree ,并且由于它不是FrameworkElement所以它没有DataContext属性。 如果没有DataContext ,您的Binding将如何工作? 问你自己。 由于此Column无法设置其DataContext ,因此它必须具有ElementNameSourceRelativeSource才能使其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消失了。

  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而不是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM