2012-01-13 12 views
13

Sto cercando di legare la visibilità della colonna a quella di un altro elemento come questo:DataGridTextColumn Visibilità Binding

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<Window.Resources> 
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 
</Window.Resources> 
<StackPanel> 
    <CheckBox x:Name="chkColumnVisible" Content="Show column" /> 
    <DataGrid x:Name="MyDataGrid" AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
      <DataGridTextColumn Header="Column1" Visibility="{Binding ElementName=chkColumnVisible, Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"/> 
     </DataGrid.Columns> 
    </DataGrid> 
</StackPanel> 

ma ottengo questo errore in uscita VS:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsChecked; DataItem=null; target element is 'DataGridTextColumn' (HashCode=48860040); target property is 'Visibility' (type 'Visibility') 

Is c'è un puro modo XAML per realizzare questo?

risposta

35

le colonne di una DataGrid sono oggetti astratti che non figurano nella struttura ad albero visuale o logica. Non è possibile utilizzare ElementName e RelativeSource. Source in combinazione con x:Reference dovrebbe funzionare però:

Visibility="{Binding Source={x:Reference chkColumnVisible}, 
        Path=IsChecked, 
        Converter={StaticResource BooleanToVisibilityConverter}}" 
+0

+1 per il collegamento a x: Reference! –

+0

Grazie, funziona! Ma così posso dormire:) ... Posso sicuramente capire perché RelativeSource non funzionerebbe, dal momento che è relativo all'obiettivo. Ma cosa diavolo è il problema con ElementName ?? Ho pensato di dare al binding un'assoluta fonte usando ElementName (e ovviamente ho sbagliato!), Quindi non importa se il target si trova nell'albero visivo o logico oppure no. –

+0

@ErenErsonmez: 'ElementName' utilizza il NameScope corrente per risolvere il nome, e namescopes dipendono gli alberi per quanto ne so. –

10

ho scritto un MarkupExtension per esso:

using System; 
using System.ComponentModel; 
using System.Linq; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Data; 
using System.Windows.Markup; 
using System.Xaml; 

/// <summary> 
/// Binds to the datacontext of the current root object or ElementName 
/// </summary> 
[MarkupExtensionReturnType(typeof(object))] 
public class NinjaBinding : MarkupExtension 
{ 
    private static readonly DependencyObject DependencyObject = new DependencyObject(); 
    private static readonly string[] DoNotCopy = { "Path", "Source", "ElementName", "RelativeSource", "ValidationRules" }; 
    private static readonly PropertyInfo[] CopyProperties = typeof(Binding).GetProperties().Where(x => !DoNotCopy.Contains(x.Name)).ToArray(); 
    public NinjaBinding() 
    { 
    } 

    public NinjaBinding(Binding binding) 
    { 
     Binding = binding; 
    } 

    public Binding Binding { get; set; } 

    private bool IsInDesignMode 
    { 
     get { return DesignerProperties.GetIsInDesignMode(DependencyObject); } 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     if (Binding == null) 
     { 
      throw new ArgumentException("Binding == null"); 
     } 
     if (IsInDesignMode) 
     { 
      return DefaultValue(serviceProvider); 
     } 
     Binding binding = null; 
     if (Binding.ElementName != null) 
     { 
      var reference = new Reference(Binding.ElementName); 
      var source = reference.ProvideValue(serviceProvider); 
      if (source == null) 
      { 
       throw new ArgumentException("Could not resolve element"); 
      } 
      binding = CreateElementNameBinding(Binding, source); 
     } 
     else if (Binding.RelativeSource !=null) 
     { 
      throw new ArgumentException("RelativeSource not supported"); 
     } 
     else 
     { 
      var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider)); 
      if (rootObjectProvider == null) 
      { 
       throw new ArgumentException("rootObjectProvider == null"); 
      } 
      binding = CreateDataContextBinding((FrameworkElement) rootObjectProvider.RootObject, Binding); 
     } 

     var provideValue = binding.ProvideValue(serviceProvider); 
     return provideValue; 
    } 

    private static Binding CreateElementNameBinding(Binding original, object source) 
    { 
     var binding = new Binding() 
     { 
      Path = original.Path, 
      Source = source, 
     }; 
     SyncProperties(original, binding); 
     return binding; 
    } 

    private static Binding CreateDataContextBinding(FrameworkElement rootObject, Binding original) 
    { 
     string path = string.Format("{0}.{1}", FrameworkElement.DataContextProperty.Name, original.Path.Path); 
     var binding = new Binding(path) 
     { 
      Source = rootObject, 
     }; 
     SyncProperties(original, binding); 
     return binding; 
    } 

    private static void SyncProperties(Binding source, Binding target) 
    { 
     foreach (var copyProperty in CopyProperties) 
     { 
      var value = copyProperty.GetValue(source); 
      copyProperty.SetValue(target, value); 
     } 
     foreach (var rule in source.ValidationRules) 
     { 
      target.ValidationRules.Add(rule); 
     } 
    } 

    private static object DefaultValue(IServiceProvider serviceProvider) 
    { 
     var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); 
     if (provideValueTarget == null) 
     { 
      throw new ArgumentException("provideValueTarget == null"); 
     } 
     var dependencyProperty = (DependencyProperty)provideValueTarget.TargetProperty; 
     return dependencyProperty.DefaultMetadata.DefaultValue; 
    } 
} 

Essa consente il legame con il DataContext del objet radice corrente {Finestra, UserControl, ... }

utilizzo

Esempio (visibile & visibilità appartengono a viewmodel):

<DataGrid> 
    <DataGrid.Columns> 
     <DataGridTextColumn Header="DataContext" Visibility="{dataGridBox:NinjaBinding Binding={Binding Visibility}}" /> 
     <DataGridTextColumn Header="Converter" Visibility="{dataGridBox:NinjaBinding Binding={Binding Visible, Converter={StaticResource BooleanToVisibilityConverter}}}" /> 
     <DataGridTextColumn Header="ElementName" Visibility="{dataGridBox:NinjaBinding Binding={Binding IsChecked, ElementName=CheckBox, Converter={StaticResource BooleanToVisibilityConverter}}}" /> 
    </DataGrid.Columns> 
</DataGrid> 
+0

non funziona per me, per qualche motivo ... 'Errore percorso BindingExpression: proprietà 'ValidationColumnVisibility' non trovato sul 'oggetto' '' NameFixupToken '(HashCode = 55.620.207)'. BindingExpression: Path = ValidationColumnVisibility; DataItem = 'NameFixupToken' (HashCode = 55620207); l'elemento di destinazione è 'DataGridTextColumn' (HashCode = 62066456); la proprietà target è 'Visibility' (tipo 'Visibility') ' – Bartosz

Problemi correlati