Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to a viewmodel property in a DataTemplate

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 a CheckBox in a DataTemplate 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 update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate 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, the Checkbox 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.

like image 334
Dowse Avatar asked Apr 01 '13 17:04

Dowse


1 Answers

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.

like image 59
Duncan Matheson Avatar answered Sep 20 '22 17:09

Duncan Matheson