user3161050
user3161050

Reputation: 121

Binding visibility to bool value in WPF dataGrid

I am aware that as DataGrid columns aren't part of the Visual Tree, you can't bind the visibility property of the columns directly to a boolean property in your VM. You have to do it another way. Below is the way I have done it:

public class LocalVm
{
    public static ObservableCollection<Item> Items
    {
        get
        {
            return new ObservableCollection<Item>
            {
                new Item{Name="Test1", ShortDescription = "Short1"}
            };
        }
    }

    public static bool IsDetailsModeEnabled
    {
        get { return true; }
    }
}

public class Item : INotifyPropertyChanged
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }


    private string _shortDescription;

    public string ShortDescription
    {
        get
        {
            return _shortDescription;
        }
        set
        {
            _shortDescription = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML:

<Window x:Class="Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Wpf"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        d:DataContext="{d:DesignInstance Type=local:LocalVm}"
        Title="MainWindow" Height="350" Width="525" 
        Name="MyWindow">

    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVis" />

        <DataGridTextColumn x:Key="ThatPeskyColumn"
                            Binding="{Binding Size}" 
                            Visibility="{Binding DataContext.IsDetailsModeEnabled, 
                            Source={x:Reference MyWindow}, 
                            Converter={StaticResource BoolToVis}}"/>

    </Window.Resources>

    <Grid>
        <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" />
                <StaticResource ResourceKey="ThatPeskyColumn"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

However, in my xaml window, there is an error on the "visibility" property: "Object reference not set to an instance of an object". If I remove the source and converter part, the error goes but it doesn't bind properly. What I am doing wrong?

thanks in advance

Upvotes: 0

Views: 2708

Answers (1)

Anton Danylov
Anton Danylov

Reputation: 1491

As I can see from the code you've provided, IsDetailsModeEnabled property is static. To bind to static property in that case, you may just create your VM as a static resource, bind to its static property and set it to Window DataContext later:

<Window.Resources>
    <local:LocalVm x:Key="vm" />
    <BooleanToVisibilityConverter x:Key="BoolToVis" />

    <DataGridTextColumn x:Key="ThatPeskyColumn"
                        Binding="{Binding ShortDescription}" 
                        Visibility="{Binding Path = IsDetailsModeEnabled, 
                        Source={StaticResource vm}, 
                        Converter={StaticResource BoolToVis}}"/>
</Window.Resources>
<Window.DataContext>
    <StaticResource ResourceKey="vm"/> 
</Window.DataContext>

From the other hand, more classical approach in that case would be with a proxy Freezable object, described here:

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

XAML

<Window.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
    <BooleanToVisibilityConverter x:Key="BoolToVis" />

    <DataGridTextColumn x:Key="ThatPeskyColumn"
                        Binding="{Binding ShortDescription}" 
                        Visibility="{Binding Path=Data.IsDetailsModeEnabled, 
                        Source={StaticResource proxy}, 
                        Converter={StaticResource BoolToVis}}"/>
</Window.Resources>

Upvotes: 2

Related Questions