Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wpf Binding Bridge

Tags:

c#

wpf

I have a feeling this is a bug in wpf. Let me know what you guys think of this.

To keep everything simple I made demo example in .net 4.0

I have a ContentControl with Content bound to Data and ContentTemplate which holds a CheckBox bound to Content.

The problem is Ok property is never true no matter how often I click the CheckBox.

As if CheckBox doesn't pass the new value to ViewModel.

Take a look at this:

  <Window.Resources>
    <DataTemplate x:Key="dataTemplate">
      <CheckBox IsChecked="{Binding Path=., Mode=TwoWay}"/>
    </DataTemplate>
  </Window.Resources>

  <Grid>
    <ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
  </Grid>

This is my ViewModel

public MainWindow()
{
    InitializeComponent();

    ViewModel vm = new ViewModel();
    this.DataContext = vm;
}

public class ViewModel : INotifyPropertyChanged
{
    private string txt;

    public string Txt
    {
        get { return txt; }
        set { txt = value; this.OnPropertyChanged("Txt"); }
    }

    private bool ok;

    public bool Ok
    {
        get { return ok; }
        set { ok = value; this.OnPropertyChanged("Ok");}
    }


    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Any ideas how to fix this issue with ContentTemplate?

like image 981
dev hedgehog Avatar asked Sep 26 '26 12:09

dev hedgehog


2 Answers

Your problem is a common problem related to the use of value types. You're data binding your checkbox to a primitive value type (bool) (which is a pretty uncommon thing to do). Since Binding.Source is of type object, your boolean is getting boxed into an object. Any updates on that boxed object has no effect on the original property on ViewModel.

You can test this theory by replacing that boolean with a struct like this:

public struct MyStruct
{
    private bool _isChecked;
    public bool IsChecked
    {
        get { return _isChecked; }
        set { _isChecked = value; }
    }

    public ViewModel Parent { get; set; }
} 

and change your binding:

<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>

If you put breakpoints on the IsChecked getter and setter, you will see that the binding works. However, when you hit one of the break points, try to investigate this value in the Immediate Window:

? this.Parent.Ok.IsChecked

You should see that the MyStruct property on the parent view model is not being affected by the databinding at all.


Full test code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            ViewModel vm = new ViewModel();
            vm.Ok = new MyStruct { Parent = vm, IsChecked = false };
            this.DataContext = vm;
        }


    }

    public class ViewModel : INotifyPropertyChanged
    {
        private string txt;

        public string Txt
        {
            get { return txt; }
            set { txt = value; this.OnPropertyChanged("Txt"); }
        }

        private MyStruct ok;

        public MyStruct Ok
        {
            get { return ok; }
            set { ok = value; this.OnPropertyChanged("Ok"); }
        }


        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public struct MyStruct
    {
        private bool _isChecked;
        public bool IsChecked
        {
            get { return _isChecked; }
            set { _isChecked = value; }
        }

        public ViewModel Parent { get; set; }
    }
}

xaml:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
    <DataTemplate x:Key="dataTemplate">
      <CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
    </DataTemplate>
  </Window.Resources>

  <Grid>
    <ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
  </Grid>
</Window>     
like image 120
Eren Ersönmez Avatar answered Sep 29 '26 00:09

Eren Ersönmez


you can do this way

<Window.Resources>
    <DataTemplate x:Key="dataTemplate">
      <CheckBox IsChecked="{Binding Path=Ok, Mode=TwoWay}"/>
    </DataTemplate>
</Window.Resources>

<Grid>
  <ContentControl Content="{Binding}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>

in above example I bind the Content to the view model itself and then IsChecked of CheckBox to the Ok property.

like image 43
pushpraj Avatar answered Sep 29 '26 00:09

pushpraj