繁体   English   中英

WPF MVVM:如何基于事件更新UI控制器

[英]WPF MVVM : how to Update UI controllers based on Event

我的UserControl中有2个TextBox控制器,我们称它们为TextBox1和TextBox2。

在我的旧代码中,当激发TextBox2 TextChanged事件时,我更新了TextBox1背景。 在xaml.cs中使用事件处理程序,这非常简单明了。

    private void textBox_TextChanged(object sender, TextChangedEventArgs e) {
     // use controllers Names.
    }

但是,我读到这违反了MVVM标准。 基本上,这不需要在xaml.cs中添加额外的代码!

在寻找答案的过程中,我发现了两种我理解的方法:

1-有人建议我使用PropertyChanged触发另一个事件。 我注意到在TextBox失去焦点之前,不会触发PropertyChanged事件。 这不是我想要的。 我希望TextBox1在用户向TextBox2输入内容后立即更新。 但是,我仍然不确定在哪里告诉代码“如果TextBox TextChanged,请更改TextBox1背景”。

2-另一种方法是使用行为,这对我来说是全新的,我能够立即在TextBox2上触发事件TextChanged,但是我不知道如何访问TextBox1属性!

我的问题:处理MVVM方法中正在寻找的需求的正确方法是什么?

第二种方法是要走的路。 视图模型中 ,添加ICommand DoOnTextChanged和依赖项属性BackgroundColor

  • 使用行为将DoOnTextChanged命令与TextBox1的TextChanged事件绑定
  • 使用转换器将BackgroundColor属性绑定到TextBox2的背景。
  • DoOnTextChangedExecute函数中,更改BackgroundColor属性即可完成操作。

如果使用的是MVVMLight,则绑定到ICommand很容易。 首先添加这两个名称空间xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"并执行以下操作:

<TextBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged" >
            <cmd:EventToCommand Command="{Binding DoOnTextChanged}" PassEventArgsToCommand="False" >
            </cmd:EventToCommand>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

更新资料

由于OP使用的是纯wpf / Xaml,因此我将使用纯wpf的实现来更新答案。

在您的项目中添加以下两个帮助程序类:

public class ExecuteCommand : TriggerAction<DependencyObject>
{
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExecuteCommand));
    public ICommand Command
    {
        get
        {
            return GetValue(CommandProperty) as ICommand;
        }
        set
        {
            SetValue(CommandProperty, value);
        }
    }

    protected override void Invoke(object parameter)
    {
        if (Command != null)
        {
            if (Command.CanExecute(parameter))
            {
                Command.Execute(parameter);
            }
        }
    }
}

public class EventCommand : ICommand
{
    private Action<object> func;

    public EventCommand(Action<object> func)
    {
        this.func = func;
    }

    public bool CanExecute(object parameter)
    {
        //Use your logic here when required
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (func != null)
        {
            func(parameter);
        }
    }
}

在您的ViewModel中,实现INotifyPropertyChanged并添加以下ICommand和Background属性。

public class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public MainViewModel(IDataService dataService)
    {
        BackColor = Brushes.Aqua;
        DoOnTextChanged = new EventCommand((obj => BackColor = BackColor == Brushes.BurlyWood ? Brushes.Chartreuse : Brushes.BurlyWood));
    }

    public ICommand DoOnTextChanged { get; set; }

    private Brush backColor;
    public Brush BackColor
    {
        get
        {
            return backColor;
        }
        set
        {
            backColor = value;
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("BackColor"));
            }
        }

    }
}

最后,在您的ViewName.xaml文件中,添加此命名空间xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 您可能需要添加对System.Windows.Interactivity的引用。 然后添加以下内容以将button事件绑定到命令:

<TextBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="TextChanged" >
            <local:ExecuteCommand Command="{Binding DoOnTextChanged}"></local:ExecuteCommand>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</TextBox>

<TextBox Background="{Binding BackColor}"></TextBox>

尽管完成很多简单的事情需要很多代码,但是在某些情况下确实很有帮助。 最好学习所有方法,并使用完全适合您需要的方法。

我会尽量在xaml中保留特定于UI的内容,并在这种情况下使用触发器。 请查看以下有关触发器和转换器的文章以获取空值。 值不为null的DataTrigger吗?

如Bradly Uffner先前所述,您应该修改您的bindung并添加UpdateSourceTrigger="PropertyChanged"以便立即触发更改。

好吧,我总是喜欢尽可能地遵循MVVM ,并且在这种情况下完全有可能:

您是在考虑View (在TextBox2 TextChanged时更新TextBox1背景),而不是在业务逻辑方面(当业务层中的某事[例如模型]发生[属性更改其值?

您应该有一个带有TextBox1和TextBox2的View以及一个带有某些属性的ViewModel,例如:

/* These properties should implement PropertyChanged, I'm too lazy */
public string WhateverInputsTextBox1 { get; set; }
public string WhateverInputsTextBox2 { get; set; }
public bool WhateverMeansTextBox1HasChanged { get; set; }

然后,当WhatevernInputsTextBox1的内容更改时(在属性set ,应将WhateverMeansTextBox1HasChanged设置为true。

最后,您必须使用转换器将True转换为颜色,将false转换为其他颜色,将TextBox1文本绑定到WhateverInputsTextBox1属性,将TextBox2文本绑定到WhateverInputsTextBox2属性,并将TextBox1背景绑定到WhateverMeansTextBox1HasChanged属性(检查IValueConverter )。 请记住,在需要的地方将绑定设置为UpdateSourceTrigger =“ PropertyChanged”(这将在输入数据时将数据移至ViewModel )。

这样,您就可以将View所有业务逻辑都包含在ViewModel并且可以通过正确分配所有职责来进行测试。

另一个好处是(至少对我来说),当我看到TextBox的背景在“ AccountNumberChanged ”而不是TextBox2进行编辑时发生变化时,更容易理解开发人员的意图。

您可以在视图模型中完成所有这些逻辑。 此特定示例对引发PropertyChanged通知的基本ViewModel类使用AgentOctal.WpfLib NuGet包(免责声明:我是该包的作者),但是只要该属性实现INotifyPropertyChanged ,您就可以使用所需的任何系统。


在此示例中,您在第一个TextBox放置的字母越多,第二个TextBox的背景越蓝。

第一个TextBoxText属性绑定到视图模型上的Text属性。 绑定将UpdateSourceTrigger设置为PropertyChanged以便绑定在每次属性更改时更新视图模型,而不仅仅是在控件失去焦点时更新。

第二个TextBoxBackground属性绑定到视图模型上名为BackgroundColorSolidColorBrush属性。

在视图模型上, TextBox的设置器包含确定第二TextBox颜色的逻辑。

这或许可以通过使用来实现一个更好一点Color ,而不是一个SolidColorBrushIValueConverter可以改变Color到一个Brush ,但它应该服务器作为一个像样的起点。

所有代码都保存在视图模型中,后面的代码为空。


XAML:

<Window
    x:Class="VmBindingExample.MainWindow"
    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:local="clr-namespace:VmBindingExample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="525"
    Height="350"
    mc:Ignorable="d">
    <Window.DataContext>
        <local:MainWindowVm />
    </Window.DataContext>
    <StackPanel Margin="20" Orientation="Vertical">
        <TextBox
            Margin="4"
            MaxLength="10"
            Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Margin="4" Background="{Binding BackgroundColor}">The color of this will reflect the length of the first textbox.</TextBox>
    </StackPanel>
</Window>

视图模型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using AgentOctal.WpfLib;

namespace VmBindingExample
{
    using System.Windows.Media;

    public class MainWindowVm : ViewModel
    {
        private string _text;

        public string Text
        {
            get
            {
                return _text;
            }

            set
            {
                SetValue(ref _text, value);
                byte red = (byte)(255 / 10 * (10 - _text.Length));
                BackgroundColor = new SolidColorBrush(Color.FromArgb(255, red, 255, 255));
            }
        }

        private Brush _backgroundColor;

        public Brush BackgroundColor
        {
            get
            {
                return _backgroundColor;
            }

            set
            {
                SetValue(ref _backgroundColor, value);
            }
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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