WPF DataGridTextColumn下的Visibility绑定

WPF中的DataContext属性非常方便,但在某些情况下,DataContext是不可访问的,比如,当你想绑定的元素不属于其逻辑树或可视树时,想正常使用绑定就可能非常困难……

让我们给一个简单的例子予以说明:我们要在DataGrid中显示产品列表。 在其中,我们希望能够基于ViewModel中公开的ShowPrice属性的值来显示或隐藏Price列。 按照我们正常的做法的话是将列的Visibility绑定到IsHide属性上,有需要的话再通过转化器来将值转换成Visibility枚举:

前台XAML代码如下

<Window
    x:Class="WPFTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WPFTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <local:VisibilityConverter x:Key="visibilityConverter" />
    </Window.Resources>
    <Grid>
        <DataGrid
            x:Name="DataGrid"
            AutoGenerateColumns="False"
            CanUserAddRows="False"
            ItemsSource="{Binding DataList}">
            <DataGrid.Resources>
                <local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn
                    Binding="{Binding Name}"
                    Header="名称"
                    IsReadOnly="True" />
                <DataGridTextColumn
                    Binding="{Binding Content}"
                    Header="备注"
                    IsReadOnly="True" />
                <DataGridTextColumn
                    Binding="{Binding Value}"
                    Header="值"
                    IsReadOnly="False"
                    Visibility="{Binding IsHide, Converter={StaticResource visibilityConverter}}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

后台ViewModel代码如下

using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.ObjectModel;

namespace WPFTest
{
    public class MainViewModel : ObservableRecipient
    {
        private bool isHide = true;

        public bool IsHide
        {
            get => isHide;
            set => SetProperty(ref isHide, value);
        }

        private ObservableCollection<ItemModel> dataList = new ObservableCollection<ItemModel>();

        public ObservableCollection<ItemModel> DataList
        {
            get => dataList;
            set => SetProperty(ref dataList, value);
        }

        public MainViewModel()
        {
            InitData();
        }

        private void InitData()
        {
            for (int i = 0; i < 5; i++)
            {
                ItemModel model = new ItemModel();
                model.Name = $"名称{i + 1}";
                model.Content = $"备注{i + 1}";
                Random random = new Random();
                model.Value = random.Next(0, 100).ToString();
                DataList.Add(model);
            }
        }
    }
}

对应的Model代码如下

namespace WPFTest
{
    public class ItemModel
    {
        public string Name { get; set; } = string.Empty;

        public string Content { get; set; } = string.Empty;

        public string Value { get; set; } = string.Empty;

        public bool IsHide { get; set; } = false;
    }
}

转换器代码如下

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WPFTest
{
    public class VisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (bool)value ? Visibility.Collapsed : Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

以上代码的运行效果如下

但是,我们很快就会发现这样并不起作用,"值"列会一直可见。同时我们可以看到Visual Studio下的XAML绑定失败中可以看到提示

IsHide DataGridTextColumn.Visibility Visibility 找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement

哪怕我们更改绑定成其他的绑定方式

                <DataGridTextColumn
                    Binding="{Binding Value}"
                    Header="值"
                    IsReadOnly="False"
                    Visibility="{Binding IsHide, Converter={StaticResource visibilityConverter},RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />

也是一样不生效的

此处可以采用Freezable类来处理这个问题,首先创建一个BindingProxy类的继承了Freezable的类,以及定义了一个名为Data的依赖项属性:

Freezable 提供 Changed 事件来通知观察者对象发生的任何修改。 冻结的 Freezable 还可以跨线程共享,解冻的 Freezable 则不能。

using System.Windows;

namespace WPFTest
{
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion Overrides of Freezable

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

然后,我们可以在DataGrid的资源中声明此类的实例,并将Data属性绑定到当前的DataContext:

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
</DataGrid.Resources>

最后一步是指定此BindingProxy对象作为绑定的源:

<DataGridTextColumn
                    Binding="{Binding Value}"
                    Header="值"
                    IsReadOnly="False"
                    Visibility="{Binding Data, Converter={StaticResource visibilityConverter}, Source={StaticResource proxy}}" />

此处Data即是上面绑定的IsHide属性值(如果绑定的是诸如Class的话则对应的属性)

完整的XAML代码如下

<Window
    x:Class="WPFTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:System="clr-namespace:System;assembly=mscorlib"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WPFTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.Resources>
        <local:VisibilityConverter x:Key="visibilityConverter" />
    </Window.Resources>
    <Grid>
        <DataGrid
            x:Name="DataGrid"
            AutoGenerateColumns="False"
            CanUserAddRows="False"
            ItemsSource="{Binding DataList}">
            <DataGrid.Resources>
                <local:BindingProxy x:Key="proxy" Data="{Binding IsHide}" />
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn
                    Binding="{Binding Name}"
                    Header="名称"
                    IsReadOnly="True" />
                <DataGridTextColumn
                    Binding="{Binding Content}"
                    Header="备注"
                    IsReadOnly="True" />
                <DataGridTextColumn
                    Binding="{Binding Value}"
                    Header="值"
                    IsReadOnly="False"
                    Visibility="{Binding Data, Converter={StaticResource visibilityConverter}, Source={StaticResource proxy}}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

运行结果如下:

https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/freezable-objects-overview?view=netframeworkdesktop-4.8

posted @   说不出来  阅读(559)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示