gpt4 book ai didi

c# - WPF 自定义 DatagridColumn 绑定(bind)问题

转载 作者:太空宇宙 更新时间:2023-11-03 12:39:02 28 4
gpt4 key购买 nike

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

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();
}
}
}

然后我尝试像这样使用它:

<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。多个记录并不意味着属性的多个列实例。而是为每个 DataGridColumn 创建多个 DataGridCell

    但是

    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 继承

    DataContext 继承仅适用于通过 VisualTree 连接的 FrameworkElement。因此,我们需要一种机制来将此 DataContext 放入我们的 DataGridColumn。输入绑定(bind)代理

    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 绑定(bind)正常工作,您需要处理它的 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 现在都绑定(bind)到 DataContext 属性 CanBeUsed,它将保持不变。您可能认为 CanBeUsed 应该是 Employee 的属性,它是 ItemsSource 而不是 DataContext数据网格。因此,当您选中/取消选中任何 CheckBox 时,所有响应都会相同。

但是,我们想将我们的 isChecked 属性绑定(bind)到 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"/>

如果我遗漏了任何一点,请告诉我。

关于c# - WPF 自定义 DatagridColumn 绑定(bind)问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39766393/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com