Binding to a viewmodel property in a DataTemplate
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item
. Thus, the DataContext of the CheckBox in the DataTemplate is an Item
, not your ItemViewModel
. You can see this by your <TextBlock Text="{Binding ItemName}"/>
, which binds to a property on the Item
class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item
.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel
, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.
Admin
Updated on July 05, 2022Comments
-
Admin almost 2 years
I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a
DataTemplate
.I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the
Visibility
property of aCheckBox
in aDataTemplate
to a Property in my viewmodel. (Using this scenario purely for learning/demo.)I have a simple DataModel named
Item
, but is of little relevance in this example.class Item : INotifyPropertyChanged { // Fields... private bool _IsRequired; private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged { private ObservableCollection<Item> _Items; private bool _IsCheckBoxChecked; private bool _IsCheckBoxVisible; public ObservableCollection<Item> Items { get { return _Items; } set { _Items = value; } } public bool IsCheckBoxChecked { get { return _IsCheckBoxChecked; } set { if (_IsCheckBoxChecked == value) return; _IsCheckBoxChecked = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked")); PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible")); } } } public bool IsCheckBoxVisible { get { return !_IsCheckBoxChecked; } set { if (_IsCheckBoxVisible == value) return; _IsCheckBoxVisible = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible")); }
(Constructors and
INotifyPropertyChanged
implementation omitted for brevity.)Controls laid out in MainPage.xaml as follows.
<Window.Resources> <local:VisibilityConverter x:Key="VisibilityConverter"/> </Window.Resources> <Window.DataContext> <local:ItemViewModel/> </Window.DataContext> <Grid> <StackPanel> <CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" /> <ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" > <ListView.ItemTemplate > <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding ItemName}"/> <CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" > <CheckBox.DataContext> <local:ItemViewModel/> </CheckBox.DataContext> </CheckBox> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> <StackPanel Orientation="Horizontal" Margin="4,4,0,0"> <TextBlock Text="IsCheckBoxVisible:"/> <TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" /> </StackPanel > <Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/> </StackPanel> </Grid>
The 'Hide CheckBoxes' checkbox is bound to
IsCheckBoxChecked
and is used to updateIsCheckBoxVisible
. I've also added a couple of extra controls below theDataTemplate
to prove, (to myself,) the everything works.)I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the
DataTemplate
function as expected but, alas, theCheckbox
inside the data template remains unchanged.I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
-
Admin about 11 yearsDuncan. Thank you for your prompt reply. I've tried your suggestion and it worked like a charm. I thought adding a separate <DataContext> tag for the checkbox would work but alas, no. Once again thank you. Will give Dan Wahlin's atricle a read in the link you provided as well. (I tried voting up your answer but my lowly reputation prevents me from doing so.)
-
Duncan Matheson about 11 yearsGlad I could help! :-) It's not completely relevant, but just as an aside, from your comment: manually setting the DataContext of an element (except the Window or another root) is something you want to be very careful with, and is often a sign that you're doing something wrong. I'm imagining your attempt was along the lines of
<CheckBox.DataContext><local:ItemViewModel/></...>
: if so, the reason it wouldn't work is when you define the ItemViewModel in xaml like that you are creating one, so you'd have a different instance. You can get around this, but the solution I described is better.