Binding a XamDataGrid Field Property
I recently needed to bind the Visibility of a Field in a XamDataGrid to a property on my ViewModel. I wanted to provide a way for the user to show/hide a column of images in the data grid. My ViewModel object is a POCO (Plain Old Clr Object) that implements INotifyPropertyChanged, and represents the data and UI state of my Window. Unfortunately a Field object is not in the element tree and does not derive from Freezable, which means it does not have an inheritance context, thus its properties cannot participate in normal WPF data binding.
If/when WPF eventually provides a way to give any object an inheritance context, this won't be an issue, but for now there is no clean way to bind a Field's properties to objects inherited down the logical tree, or to the properties of other elements. This blog post shows a workaround that I came up with, allowing me to easily bind the Visibility of a Field to any object.
Here is the basic idea of what I'm trying to accomplish. First the grid displays a column of images:
If you then click the "Show Photos" CheckBox in the ToolBar, the column of photos is hidden, as seen below:
Here is the code-behind of the Window:
public partial class Window1 : Window
{
public Window1()
{
Person[] people = new Person[]
{
new Person("Boss Hogg", 42, "hogg.jpg"),
new Person("Johann Bach", 50, "bach.jpg"),
new Person("Mugatu", 39, "mugatu.gif"),
new Person("Simon Wolcott", 24, "wolcott.jpg")
};
base.DataContext = new CommunityViewModel(people);
Application.Current.Resources.Add("DATA_CommunityViewModel", base.DataContext);
InitializeComponent();
}
}
Notice that I'm adding the CommunityViewModel class to both the DataContext of the Window and the Resources of the App. I set the DataContext to the ViewModel so that the XamDataGrid and CheckBox controls can bind to its properties. I add it to the App's Resources so that the Field's Visibility binding can access it. Here is the CommunityViewModel class:
class CommunityViewModel : INotifyPropertyChanged
{
bool _showPhotos;
public CommunityViewModel(IList<Person> constituents)
{
this.Constituents = new ReadOnlyCollection<Person>(constituents);
_showPhotos = true;
}
public ReadOnlyCollection<Person> Constituents { get; set; }
public bool ShowPhotos
{
get { return _showPhotos; }
set
{
if (value == _showPhotos)
return;
_showPhotos = value;
this.OnPropertyChanged("ShowPhotos");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
The magic happens in the XAML. Pay close attention to the 'Photo' Field declaration:
<Window
x:Class="XamDataGridWithBoundField.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:igDP="http://infragistics.com/DataPresenter"
Title="Window1"
Width="400" Height="400"
>
<Window.Resources>
<Style
x:Key="PhotoCellStyle"
TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}">
<Image
Source="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=Content}"
Width="60" Height="60"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<DockPanel>
<ToolBar DockPanel.Dock="Top">
<CheckBox
Content="Show Photos"
IsChecked="{Binding Path=ShowPhotos}"
/>
</ToolBar>
<igDP:XamDataGrid
AutoFit="True"
DataSource="{Binding Constituents}"
>
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:FieldLayout.Fields>
<igDP:Field Label="Name" Name="Name" />
<igDP:Field Label="Photo" Name="ImageUri">
<igDP:Field.Settings>
<igDP:FieldSettings
CellMaxWidth="70" LabelMaxWidth="70"
CellValuePresenterStyle="{StaticResource PhotoCellStyle}"
/>
</igDP:Field.Settings>
<igDP:Field.Visibility>
<Binding
Path="ShowPhotos"
Source="{StaticResource DATA_CommunityViewModel}"
>
<Binding.Converter>
<BooleanToVisibilityConverter />
</Binding.Converter>
</Binding>
</igDP:Field.Visibility>
</igDP:Field>
<igDP:Field Label="Age" Name="Age" />
</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
</DockPanel>
</Window>
You can download the demo source code here. You will need to have the Infragistics NetAdvantage for WPF installed to run the application.