WPF One Way绑定已损坏

时间:2011-12-30 14:41:26

标签: wpf binding mvvm

我试图将2个不同的WPF控件绑定到ViewModel中的同一属性,一个CheckBox.IsChecked和一个Expander.IsExpanded。我想要实现的行为是让CheckBox影响ViewModel(因此也影响Expander),但不影响其他方式。 类似的东西:

Checkbox Checked -> ViewModel property set to frue -> Expander.Expand
Checkbox Unchecked -> ViewModel property set to false -> Expander.Collapse
Expander Expanded -> Nothing else affected
Expander Collapsed -> Nothing else affected

这是XAML:

<Window x:Class="WpfApplication9.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">
    <Expander IsExpanded="{Binding IsChecked, Mode=OneWay}">
        <Expander.Header>
            <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
        </Expander.Header>
        <TextBlock Text="Expanded!"/>
    </Expander>
</Window>

和代码:

using System.ComponentModel;
using System.Windows;

namespace WpfApplication9
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel: INotifyPropertyChanged
    {
        private bool _isChecked;
        public bool IsChecked
        {
            get { return _isChecked; }
            set
            {
                _isChecked = value;
                NotifyPropertyChange("IsChecked");
            }
        }

        protected void NotifyPropertyChange(string PropertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
}

现在我的问题是,只要我点击Expander展开/折叠它,Binding就会停止工作。任何人都可以向我解释为什么会发生这种情况,我该如何实现这一目标?提前谢谢!

3 个答案:

答案 0 :(得分:12)

新答案

发现您可以通过在扩展程序中将UpdateSourceTrigger设置为Explicit来执行此操作。这使绑定保持双向,但永远不会更新源,因为你告诉它不要更新源,除非你明确告诉它。

<Expander IsExpanded="{Binding IsChecked, UpdateSourceTrigger=Explicit}">
    <Expander.Header>
        <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked"/>
    </Expander.Header>
    <TextBlock Text="Expanded!"/>
</Expander>

在下面留下我的旧答案,以便评论有意义,因为我仍然觉得视图特定代码在视图的代码隐藏中没有问题:)


旧答案

就个人而言,由于这是特定于视图的代码,我认为使用CheckBox点击事件设置Expander的IsExpanded值没有问题。

private void MyCheckBox_Click(object sender, RoutedEventArgs e)
{
    MyExpander.IsExpanded = ((CheckBox)sender).IsChecked.GetValueOrDefault();
}

通过删除名称并导航Visual Tree以查找与CheckBox关联的Expander,可以使其更加通用。以下是使用Visual Tree Helpers I built

的示例
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
    var chk = (CheckBox)sender;
    var expander = VisualTreeHelpers.FindAncestor<Expander>(chk);

    if (expander != null)
        expander.IsExpanded = chk.IsChecked.GetValueOrDefault();
}

答案 1 :(得分:1)

如果您希望复选框影响扩展器(但反之亦然),请正常绑定扩展器并在复选框上使用OneWayToSource

<Window x:Class="WpfApplication9.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">
   <Expander IsExpanded="{Binding IsChecked}">
      <Expander.Header>
         <CheckBox IsChecked="{Binding IsChecked, Mode=OneWayToSource}" Content="Is Checked"/>
      </Expander.Header>
      <TextBlock Text="Expanded!"/>
   </Expander>
</Window>

在复选框上使用OneWayToSource将允许它:

  • 修改基础属性(因此影响扩展器,它也绑定到该属性)
  • 不受其他对基础财产进行更改的组件的影响

答案 2 :(得分:1)

如果您想避免任何代码隐藏,可以在ViewModel中的ExpanderCheckBox状态之间添加一定程度的分隔:

            private bool _isChecked;
            public bool IsChecked
            {
                get { return _isChecked; }
                set
                {
                    _isChecked = value;
                    NotifyPropertyChange("IsChecked");
                    IsExpanded = value;
                }
            }

            private bool _isExpanded;
            public bool IsExpanded
            {
                get { return _isExpanded; }
                set
                {
                    _isExpanded = value;
                    NotifyPropertyChange("IsExpanded");
                }
            }

    <Expander IsExpanded="{Binding IsExpanded}">
        <Expander.Header>
            <CheckBox IsChecked="{Binding IsChecked}" Content="Is Checked" x:Name="cb"/>
        </Expander.Header>
        <TextBlock Text="Expanded!"/>
    </Expander>