【问题标题】:MVVM Binding not working when change from event firing从事件触发更改时,MVVM 绑定不起作用
【发布时间】:2017-10-30 11:25:18
【问题描述】:

我制作了一个简单的 MVVM 示例。我有两个用户控制页面的主窗口。主窗口有两个事件将视图更改为两个用户控件。

这是我的主窗口 XAML

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVC_Binding"
        xmlns:views="clr-namespace:MVVC_Binding.Views"
        xmlns:viewModel="clr-namespace:MVVC_Binding.ViewModels"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" x:Class="MVVC_Binding.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewModel:Page1ViewModel}">
            <views:Page1 />
        </DataTemplate>

        <DataTemplate DataType="{x:Type viewModel:Page2ViewModel}">
            <views:Page2/>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0">
            <TextBlock Text="Page1">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonUp">
                        <i:InvokeCommandAction Command="{Binding NavCommand}" CommandParameter="page1"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBlock>
            <TextBlock Text="Page2">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonUp">
                        <i:InvokeCommandAction Command="{Binding NavCommand}" CommandParameter="page2"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBlock>
        </StackPanel>
        <DockPanel Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Gainsboro">
            <Grid x:Name="container" Background="Gainsboro" VerticalAlignment="Top">
                <ContentControl Content="{Binding CurrentViewModel}"/>
            </Grid>
        </DockPanel>
    </Grid>
</Window>

我正在为我的视图模型使用 BindableBase 类。这是我的 BindableBase 类

namespace MVVC_Binding.Utilities
{
    public class BindableBase : INotifyPropertyChanged
    {
        /// <summary>
        /// Interface implementation
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged = delegate { };

        protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null)
        {
            // Check for current set member and the new value
            // If they are the same, do nothing
            if (object.Equals(member, val)) return;

            member = val;
            // Invoke the property change event
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(string propName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

在我的主视图模型中,只需简单的单击事件即可更改视图绑定

private BindableBase _CurrentViewModel;

        public BindableBase CurrentViewModel
        {
            get { return _CurrentViewModel; }
            set
            {
                SetProperty(ref _CurrentViewModel, value);
            }
        }

        private void OnNav(string destination)
        {
            switch (destination)
            {
                case "page2":
                    CurrentViewModel = page2;
                    break;
                default:
                    CurrentViewModel = page1;
                    break;
            }
        }

问题出在用户控件第2页,当它显示时,它旁边的事件并没有改变TextBlock绑定值,但是在视图模型构造函数事件期间文本可以改变。

这是我的第 2 页 XAML

<UserControl x:Class="MVVC_Binding.Views.Page2"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:MVVC_Binding.Views"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <TextBlock Name="txtPage2" Text="{Binding Page2Text}"></TextBlock>
        <TextBlock Name="btn" Text="Click Button">
            <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseLeftButtonUp">
                        <i:InvokeCommandAction Command="{Binding BtnCommand}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
        </TextBlock>
    </StackPanel>
</UserControl>

这里是 Page2ViewModel

namespace MVVC_Binding.ViewModels
{
    public class Page2ViewModel : BindableBase
    {
        public MyICommand<string> BtnCommand { get; private set; }

        public Page2ViewModel()
        {
            BtnCommand = new MyICommand<string>(OnBtnClick);
            Page2Text = "Just a test";
        }

        private void OnBtnClick(string obj)
        {
            Page2Text = "Changing by button click";
        }

        private string _page2Text;

        public string Page2Text
        {
            get { return _page2Text; }
            set
            {
                _page2Text = value;
                SetProperty(ref _page2Text, value);
            }
        }
    }
}

你能看看我做错了什么吗?非常感谢

【问题讨论】:

  • @Aaron 的回答应该可以解决您的问题,但我想为您的BindableBase 添加一些有用的提示。如果您将SetProperty 更改为返回bool,其中true 表示该值已更新,而false 如果未更新,则如果您需要在setter 中添加额外的逻辑,您可以进行一些非常有效的属性更改。我使用相同的模式,非常有帮助。
  • 感谢@BradleyUffner,我更改了 Araon 提到的代码,但没有帮助。该值不会更新到视图。但是当我放置break时,当视图中的值发生变化时,我可以看到代码正确触发。并触发了 INotify 事件
  • PropertyChanged 事件的设置和触发方式对我来说有点奇怪,但我不确定这是否真的是一个问题,或者只是与我习惯的不同。您之前是否使用过此 BindableBase 类并成功进行 2 向绑定,或者这是您第一次尝试使用它?
  • 这是我第一次使用它,我是从PluralSight课程中学习的,所以我尝试应用它。但奇怪的是,如果我从 Viewmodel 构造函数中更改文本,它将正确更新。它只会因命令事件而失败
  • 那是因为构造函数在绑定之前运行,属性值在评估时已经到位,它只是拾取绑定时已经存在的内容,它不依赖在PropertyChanged 事件上通知它新值。尝试更改您的 BindableBase 使其看起来像 this 并让我知道它是否有任何不同。如果可行,我会为您输入答案。

标签: c# wpf mvvm


【解决方案1】:

如果我理解正确,您是在问为什么此函数中的代码似乎对视图没有影响:

private void OnBtnClick(string obj)
{
    _page2Text = "Changing by button click";
}

问题是您正在更改底层 _page2Text 成员,但为了让 WPF 检测到此更改,您必须使用 Page2Text 属性,如下所示:

private void OnBtnClick(string obj)
{
    Page2Text = "Changing by button click";
}

指示 WPF 属性更改的代码的特定部分是 BindableBase 类中的 OnPropertyChanged 方法。

【讨论】:

  • 另外注意到他不需要both_page2Text = value;设置支持字段,在@987654329中调用SetProperty(ref _page2Text, value); @二传手。对SetProperty 的调用将为他处理支持字段。
  • 我将这一行改为 Page2Text = "Changing by button click";但这并没有什么不同。
【解决方案2】:

谢谢大家,我设法通过更新类 BindableBase 来解决它。更新了

namespace SilentUpdate.Utilities
{
    public class BindableBase : INotifyPropertyChanged
    {
        /// <summary>
        /// Interface implementation
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null)
        {
            // Check for current set member and the new value
            // If they are the same, do nothing
            // if (object.Equals(member, val)) return;

            member = val;
            // Invoke the property change event
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void OnPropertyChanged(string propName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }
}

所以我注释掉 Object.Equals 的检查并强制它为每个值运行

【讨论】:

  • 如果你仍然想检查是否相等,在引发事件之前,这是我在ViewModel 基类中使用的。 if (EqualityComparer&lt;T&gt;.Default.Equals(member, val)),我没有遇到任何问题。
  • 感谢@BradleyUffner,我用您的更改进行了测试,但问题是一样的,代码应该调用属性更改方法来让视图更新。我将代码更改为始终调用 PropertyChange 事件并且它可以工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
  • 2016-08-11
  • 2011-11-10
  • 2012-10-12
  • 1970-01-01
  • 2017-04-28
相关资源
最近更新 更多