C# WPF MVVM:如何基于事件更新UI控制器
我的UserControl中有两个TextBox控制器,我们称它们为TextBox1和TextBox2 在我的旧代码中,当触发TextBox2TextChanged事件时,我会更新TextBox1背景。在xaml.cs中使用事件处理程序,这很简单C# WPF MVVM:如何基于事件更新UI控制器,c#,wpf,mvvm,C#,Wpf,Mvvm,我的UserControl中有两个TextBox控制器,我们称它们为TextBox1和TextBox2 在我的旧代码中,当触发TextBox2TextChanged事件时,我会更新TextBox1背景。在xaml.cs中使用事件处理程序,这很简单 private void textBox_TextChanged(object sender, TextChangedEventArgs e) { // use controllers Names. } 然而,我读到这违反了
private void textBox_TextChanged(object sender, TextChangedEventArgs e) {
// use controllers Names.
}
然而,我读到这违反了MVVM标准。基本上就是不要在xaml.cs中添加额外的代码
在我寻找答案的过程中,我发现了两种我比较理解的方法:
1-有些人建议我使用PropertyChanged来触发另一个事件。我注意到,直到文本框失去焦点,PropertyChanged事件才会触发。这不是我要找的。我希望TextBox1在用户向TextBox2输入内容后立即更新。然而,我仍然不知道该在哪里告诉代码
“如果文本框文本已更改,则更改文本框1背景”
2-另一种方法是使用行为,这对我来说是全新的,我能够立即在TextBox2上启动事件TextChanged,但我不知道如何访问TextBox1属性
我的问题是:在MVVM方法中,处理我正在寻找的需求的正确方法是什么 第二种方法是要走的路。在viewmodel中,添加ICommand
DoOnTextChanged
和依赖项属性BackgroundColor
- 使用行为将
命令与TextBox1的TextChanged事件绑定DoOnTextChanged
- 使用转换器将
属性绑定到TextBox2的背景BackgroundColor
- 在
的Execute函数中,更改DoOnTextChanged
属性,即可完成操作BackgroundColor
xmlns:i=”http://schemas.microsoft.com/expression/2010/interactivity“
和xmlns:cmd=“clr命名空间: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>
最后,在ViewName.xaml文件中,添加这个名称空间xmlns:i=”http://schemas.microsoft.com/expression/2010/interactivity“
。您可能需要添加对System.Windows.Interactive的引用。然后添加以下内容以将按钮事件绑定到命令:
<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的内容,并在这种情况下使用触发器。 检查以下关于触发器和空值转换器的文章
正如Bradly Uffner前面提到的,您应该修改bindung并添加
UpdateSourceTrigger=“PropertyChanged”
,以便立即触发更改。好吧,如果可能并且在这种情况下完全可能的话,我总是喜欢遵循MVVM
:
您考虑的是视图
(当TextBox2文本发生变化时更新TextBox1背景),而不是业务逻辑(当业务层[例如模型]中发生某种情况时更新TextBox1背景)[属性改变其值
您应该有一个具有TextBox1和TextBox2的视图,以及一个具有某些属性的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的内容更改时(在属性的集合中),您应该将WhateverMeansTextBox1HasChanged设置为true
最后,必须使用转换器将TextBox1文本绑定到WhateverInputsTextBox1属性,将TextBox2文本绑定到WhateverInputsTextBox2属性,将TextBox1背景绑定到WhateverMeansTextBox1HasChanged属性,以将真值转换为颜色,将假值转换为其他颜色(选中)。请记住在需要时将绑定设置为UpdateSourceTracger=“PropertyChanged”(输入数据时会将数据移动到ViewModel
)
通过这种方式,您可以将视图的所有业务逻辑
放入视图模型
中,如果您想测试它,那么您就可以测试它,因为所有职责都已正确分配
另一个好处是(至少对我来说)当我看到“accountnumberchange
”时TextBox
的背景发生变化时,更容易理解开发人员的意图而不是编辑TextBox2
时。您可以在视图模型中执行所有逻辑。此特定示例使用NuGet软件包(免责声明:我是此软件包的作者)对于引发PropertyChanged
通知的基本ViewModel
类,但您可以使用您想要的任何系统,只要它的属性实现INotifyPropertyChanged
在本例中,您在第一个文本框中输入的字母越多,第二个文本框的背景越蓝
第一个TextBox
将其Text
属性绑定到视图模型上的Text
属性。该绑定将UpdateSourceTrigger
设置为PropertyChanged
,以便绑定在每次属性更改时更新视图模型,而不仅仅是在控件失去焦点时
第二个文本框
将其背景
属性绑定到视图模型上名为背景色
的SolidColorBrush
属性
在视图模型上,文本框的setter
包含确定第二个文本框的颜色的逻辑
使用Color
而不是solidcolorbush
,以及IValueConverter
可以将Color
更改为Brush
,这可能会实现得更好一些,但它应该是一个不错的起点<
<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>
/* These properties should implement PropertyChanged, I'm too lazy */
public string WhateverInputsTextBox1 { get; set; }
public string WhateverInputsTextBox2 { get; set; }
public bool WhateverMeansTextBox1HasChanged { get; set; }
<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);
}
}
}
}