[英]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的背景。 DoOnTextChanged
的Execute函数中,更改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
的背景越蓝。
第一个TextBox
的Text
属性绑定到视图模型上的Text
属性。 绑定将UpdateSourceTrigger
设置为PropertyChanged
以便绑定在每次属性更改时更新视图模型,而不仅仅是在控件失去焦点时更新。
第二个TextBox
的Background
属性绑定到视图模型上名为BackgroundColor
的SolidColorBrush
属性。
在视图模型上, TextBox
的设置器包含确定第二TextBox
颜色的逻辑。
这或许可以通过使用来实现一个更好一点Color
,而不是一个SolidColorBrush
和IValueConverter
可以改变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.