2012-01-13 17 views
13

Estoy tratando de unir la visibilidad de la columna a la de otro elemento de la siguiente manera:DataGridTextColumn Visibilidad Encuadernación

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

pero me sale este error en la salida 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') 

Es hay una forma pura de XAML para lograr esto?

Respuesta

35

Las columnas de un DataGrid son objetos abstractos que no aparecen en el árbol visual o lógico. No puede usar ElementName y RelativeSource. Source en combinación con x:Reference debería funcionar sin embargo:

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

+1 para el enlace a x: ¡Referencia! –

+0

¡Gracias, funciona! Pero para que pueda dormir:) ... definitivamente puedo entender por qué RelativeSource no funcionaría, ya que es relativo al objetivo. Pero, ¿qué demonios es el problema con ElementName? Pensé que estaba dando al enlace una fuente absoluta al usar ElementName (¡y obviamente estoy equivocado!) Así que no importaría si el objetivo está en el árbol visual o lógico o no. –

+0

@ErenErsonmez: 'ElementName' usa el nombre del campo actual para resolver el nombre, y los namescopes dependen de los árboles, hasta donde yo sé. –

10

me escribió una markupextension para ello:

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

Permite la unión a la DataContext del objet raíz actual {Ventana, control de usuario, ... }

uso

Muestra (Visible & visibilidad son propiedades en el modelo de vista):

<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

No funciona para mí por alguna razón ... 'Error de ruta BindingExpression: la propiedad 'ValidationColumnVisibility' no se encuentra en 'object' '' NameFixupToken '(HashCode = 55620207)'. BindingExpression: Path = ValidationColumnVisibility; DataItem = 'NameFixupToken' (HashCode = 55620207); el elemento de destino es 'DataGridTextColumn' (HashCode = 62066456); la propiedad de destino es 'Visibilidad' (tipo 'Visibilidad') ' – Bartosz

Cuestiones relacionadas