How can I bind visibility of a datagrid column on a custom wpf control?

3.7k views Asked by At

I've spent the morning looking at related posts, NONE of them I've found address the exact issue I'm experiencing, although I've learned a bit more along the way.

(Using MVVM with user controls in WPF)

Scenario: I need to create a re-usable control which is a datagrid that shows two or three columns depending on the form requirements. I have a custom control I've already created, as well as a dependency property for hiding / showing this third column option:

*Note: This visibility is dependent entirely on what I set the property to, I never need it to change based off of selection in other areas.

public class MyCustomControl: Control
{
    public static readonly DependencyProperty DisplayThirdColumnProperty = DependencyProperty.Register(
                                                                                        "DisplayThirdColumn",
                                                                                        typeof(bool),
                                                                                        typeof(MyCustomControl),
                                                                                        new FrameworkPropertyMetadata(false));

    static MyCustomControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }

    /// <summary>
    /// Gets or sets a value indicating whether the the third column should display.
    /// </summary>
    public bool DisplayThirdColumn
    {
        get
        {
            return (bool)this.GetValue(DisplayThirdColumnProperty);
        }
        set
        {
            this.SetValue(DisplayThirdColumnProperty, value);
        }
    }
}

Here is the xaml.Generic:

<CheckBoxColumn Binding="{Binding StuffInThirdColumn}"
                Header="ThirdColumn" 
                Visibility="{Binding DisplayThirdColumn, 
                Converter={StaticResource BooleanToVisibilityConverter},RelativeSource={RelativeSource TemplatedParent}}"/>

Now when I consume the control:

<MyControls:MyCustomControl DisplayThirdColumn="False"/>

My apologies if my 'newbieness' is showing, but am I missing something obvious here? When I set the Visiblity property to collapsed explicitly on the control xaml.Generic, it correctly hides the column:

<CheckBoxColumn Visibility="Collapsed"..../>

Output window seems to indicate that it cant find the element to apply it to.

If I can't use relative source, do you know another way I can accomplish this?

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

3

There are 3 answers

1
K.DW On BEST ANSWER

Thank you all for your comments and input, and for taking a minute (I always appreciate any time you take!)

Here is the end result, what ended up working in case someone else runs in to this:

This post helped out a lot, but the syntax I needed was missing the relative source for TemplatedParent:

(1) I am using a consumable control, and wanted to be able to set this visibility when the control is implemented. You can get to the ViewModel context using the steps in the post mentioned above.

(2) You need to put the binding Relative source to TemplatedParent on either the proxy or the dummy element (that was the part I was missing).

... In a ControlTemplate:

    <FrameworkElement x:Name="dummyElementToGetDataContext"
                        DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                        Visibility="Collapsed" />
        <DataGrid>
           <DataGrid.Columns>
            ......
           <CheckBoxColumn Binding="{Binding SecondColumnStuff}"
                            Visibility="{Binding DataContext.ShouldDisplaySecondColumn,
                                         Converter={StaticResource BooleanToVisibilityConverter},
                                         Source={x:Reference dummyElementToGetDataContext}}"



 .............

OR

Create the proxy and when declaring this as resource, set binding relative source to templated parent:

<DataGrid.Resources>
     <controls:ControlProxy x:Key="ControlProxy" Control="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</DataGrid.Resources>
1
Maxime Tremblay-Savard On

The Visibility property doesn't take "False" as a possible value. If you want to hide your control, you either need to write:

<CheckBoxColumn Visibility="Collapsed"/>

or

<CheckBoxColumn Visibility="Hidden"/>

If you want to set the Visibility in c# code, write:

yourObject.Visibility = Visibility.Collapsed;

If you need more information about the visibility property and all its possible values, you should go here: https://msdn.microsoft.com/en-us/library/system.windows.visibility(v=vs.110).aspx

0
Contango On

I would bind the visibility property to a boolean in the ViewModel, and use a VisibilityConverter, see http://www.codeproject.com/Tips/285358/All-purpose-Boolean-to-Visibility-Converter.

This means that if the boolean property we are binding to is true, it would be converted to Visibility.Visible, and if false, Visibility.Collapsed.