In WPF, using the MVVM model, I am struggling with what I believe is a binding issue. I have a ListView designed to display a collection of objects. The ItemsSource is bound to the collection and each GridViewColumn to seperate properties of that object. This view (a UserControl) is generic across several tabs in a TabControl on the master window. Each tab requires certain columns to be hidden (logic happens in the ViewModel).
I have created a Behaviors class to attach DependecyProperties for several things, including a property IsHidden for the GridViewColumn. With this in mind, here's an example setup:
Behavior Classes - minimized examples
public static class LayoutColumn
{
public static readonly DependencyProperty HiddenProperty = DependencyProperty.RegisterAttached(
"IsHidden",
typeof(bool),
typeof(LayoutColumn));
public static bool GetIsHidden(DependencyObject obj)
{
return (bool)obj.GetValue(HiddenProperty);
}
public static void SetIsHidden(DependencyObject obj, bool isHidden)
{
obj.SetValue(HiddenProperty, isHidden);
}
public static bool IsHidden(this GridViewColumn column)
{
bool? isHidden = column.GetProperty<bool>(HiddenProperty);
// Debug
string format = "{0}.IsHidden = {1}";
if (isHidden.HasValue)
{
Console.WriteLine(format, column.Header, isHidden.Value);
}
else
{
Console.WriteLine(format, column.Header, "Null");
}
return isHidden.HasValue && isHidden.Value;
}
private static T? GetProperty<T>(this GridViewColumn column, DependencyProperty dp) where T : struct
{
if (column == null)
{
throw new ArgumentNullException("column");
}
object value = column.ReadLocalValue(dp);
if (value != null && value.GetType() == dp.PropertyType)
{
return (T)value;
}
return null;
}
} // end LayoutColumn class
public class LayoutManager
{
// Methods and logic to enforce column min/max width, hidden, fixed, etc by attaching to the ListView and GridViewColumn event handlers.
}
An example object class
public class Example
{
public string Foo { get; set; }
public string Bar { get; set; }
public string Baz { get; set; }
}
ViewModel
public class MyDisplayViewModel : INotifyPropertyChanged
{
private bool hidden;
private ObservableCollection<Example> examples;
...
public bool HideColumn
{
get
{
return this.hidden;
}
set
{
this.hidden = value;
this.OnPropertyChanged("HideColumn");
}
}
public ObservableCollection<Example> ExampleCollection
{
get
{
return this.examples;
}
set
{
this.examples = value;
this.OnPropertyChanged("ExampleCollection");
}
}
}
Some XAML
<ListView
Name="LogListView"
ItemsSource="{Binding ExampleCollection}"
ListViewBehaviors:LayoutManager.Enabled="{Binding AttachProperty}">
<ListView.View>
<GridView>
<GridViewColumn
Width="Auto"
ListViewBehaviors:LayoutColumn.MinWidth="40"
ListViewBehaviors:LayoutColumn.MaxWidth="200"
ListViewBehaviors:LayoutColumn.IsHidden="True"
DisplayMemberBinding="{Binding Foo, Mode=OneWay}"
Header="Hidden Column"/>
<GridViewColumn
Width="Auto"
ListViewBehaviors:LayoutColumn.MinWidth="74"
ListViewBehaviors:LayoutColumn.MaxWidth="200"
ListViewBehaviors:LayoutColumn.IsHidden="False"
DisplayMemberBinding="{Binding Bar, Mode=OneWay}"
Header="Visible Column"/>
<GridViewColumn
Width="Auto"
ListViewBehaviors:LayoutColumn.IsFixed="True"
ListViewBehaviors:LayoutColumn.IsHidden="{Binding Path=DataContext.HideColumn, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}},
Mode=OneWay}"
DisplayMemberBinding="{Binding Baz, Mode=OneWay}"
Header="Dynamic Visibility">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Demonstrated here, manually setting the attached property IsHidden to true or false works correctly to hide/show the associated column. When IsHidden is null (the column doesn't have the attached behavior set) the expected behavior is for the column to show like normal.
Problem
The bound column always shows by default, which to me means IsHidden is not being set. I feel like I'm close, but all my research results in examples doing what I've done here. I'm fairly new to WPF, so I don't know what else to search for.
Edit
I've tried the following, but all failed:
AncestorType={x:Type ListView}
AncestorType={x:Type UserControl}
AncestorType={x:Type Window}
What do I need to do to bind the GridViewColumn attached property to the ViewModel HideColumn property?