我感觉这是wpf中的错误。让我知道你们的想法。
为使一切简单,我在.net 4.0中制作了演示示例
我有一个ContentControl,它的内容绑定到Data,而ContentTemplate则拥有一个绑定到Content的CheckBox。
问题是,无论我多久单击一次CheckBox,Ok属性永远都不是正确的。
好像CheckBox不会将新值传递给ViewModel。
看看这个:
<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>
这是我的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;
}
有任何想法如何使用ContentTemplate解决此问题?
最佳答案
您的问题是与使用值类型有关的常见问题。您正在将复选框数据绑定到原始值类型(布尔)(这是一件很不常见的事情)。由于Binding.Source
的类型为object
,因此您的布尔值将被装箱到object
中。该装箱的对象的任何更新都不会影响ViewModel
上的原始属性。
您可以通过使用以下结构替换该布尔值来测试该理论:
public struct MyStruct
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public ViewModel Parent { get; set; }
}
并更改您的绑定:
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
如果将断点放在
IsChecked
getter和setter上,您将看到绑定有效。但是,当您遇到一个断点时,请尝试在立即窗口中调查此值:? this.Parent.Ok.IsChecked
您应该看到父视图模型上的
MyStruct
属性完全不受数据绑定的影响。完整的测试代码:
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>