2016-09-29 119 views
2

我試圖定義,我可以在我的應用程序中重用一個DataGrid新列的模板,但是當我嘗試使用它,我得到:WPF自定義的DataGridColumn綁定問題

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> 

任何人都可以向我解釋正確的方法嗎?

回答

1

比方說我們有可能讓你感到困惑

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。 輸入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而不是DataContextDataGrid。因此,當您選中/取消選中任何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"/> 

如果我錯過了任何一點,不要讓我知道。