简体   繁体   中英

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.

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM