【问题标题】:DataGridTextColumn Visibility BindingDataGridTextColumn 可见性绑定
【发布时间】:2012-02-09 11:37:59
【问题描述】:

我正在尝试将列可见性绑定到另一个元素的可见性,如下所示:

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

但我在 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')

有没有一种纯粹的 XAML 方法来实现这一点?

【问题讨论】:

    标签: c# wpf xaml wpfdatagrid


    【解决方案1】:

    DataGrid 的列是未出现在可视化或逻辑树中的抽象对象。您不能使用ElementName 和RelativeSource。 Source 与 x:Reference 结合应该可以工作:

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

    【讨论】:

    • 谢谢,它有效!但是这样我就可以睡觉了:)...我完全可以理解为什么 RelativeSource 不起作用,因为它是相对于目标的。但是ElementName到底有什么问题?我以为我通过使用 ElementName 为绑定提供了一个绝对源(显然我错了!)所以目标是否在可视树或逻辑树上都没有关系。
    • @ErenErsonmez:ElementName 使用当前的名称范围来解析名称,据我所知,名称范围依赖于树。
    • @ErenErsonmez: MSDN: You can refer to elements in code only if they are registered to the appropriate NameScope through RegisterName. For more information, see WPF XAML Namescopes -> The names in a XAML namescope can be used to establish relationships between the XAML-defined names of objects and their instance equivalents in an object tree.
    • 谢谢!我知道除了使用代理元素之外,必须有更好的答案。
    【解决方案2】:

    我为它写了一个标记扩展:

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

    它可以绑定到当前根对象 {Window, UserControl, ...}

    的 DataContext

    示例用法(可见性和可见性是 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>
    

    【讨论】:

    • 由于某种原因对我不起作用...BindingExpression path error: 'ValidationColumnVisibility' property not found on 'object' ''NameFixupToken' (HashCode=55620207)'. BindingExpression:Path=ValidationColumnVisibility; DataItem='NameFixupToken' (HashCode=55620207); target element is 'DataGridTextColumn' (HashCode=62066456); target property is 'Visibility' (type 'Visibility')
    • 在我的应用程序中完美地使用了 DataTemplateColumn 和 DataGrid。
    【解决方案3】:

    Johan Larsson 的解决方案完美运行,只有来自 Binding 的 FallbackValue 没有被转发,所以我将其更改为:

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

    所以它可以像这样使用,这里以绑定到 Header 为例:

    <DataGridTemplateColumn Header="{dataGridBox:NinjaBinding Binding={Binding MyHeaderName1, FallbackValue=HeadingNr1}}" />
    

    【讨论】:

      猜你喜欢
      • 2010-11-02
      • 2011-12-21
      • 2020-10-14
      • 1970-01-01
      • 2013-12-13
      • 2014-08-20
      • 1970-01-01
      • 1970-01-01
      • 2011-12-18
      相关资源
      最近更新 更多