C# 用户输入确认
在尝试使用更通用的WPF方法(沿着MVVM的方式)之后,我遇到了一个问题 假设我有一个经典的Person类,它有三个字符串属性——FirstName、LastName、Fullname。前两个由用户通过两个文本框输入,全名为只读,以文本块打印 我有一个带有两个文本框的stackpanel。我将Person实例绑定到stackpanel的DataContext。它看起来像这样:C# 用户输入确认,c#,wpf,mvvm,C#,Wpf,Mvvm,在尝试使用更通用的WPF方法(沿着MVVM的方式)之后,我遇到了一个问题 假设我有一个经典的Person类,它有三个字符串属性——FirstName、LastName、Fullname。前两个由用户通过两个文本框输入,全名为只读,以文本块打印 我有一个带有两个文本框的stackpanel。我将Person实例绑定到stackpanel的DataContext。它看起来像这样: <StackPanel x:Name="InputData" DataContext="{Binding Pers
<StackPanel x:Name="InputData" DataContext="{Binding Person}">
<Text="{Binding FirstName, Mode=TwoWay}"/>
<Text="{Binding LastName, Mode=TwoWay}"/>
</StackPanel>
<TextBlock Text="{Binding FullName}"/>
现在我想避免对pp的意外更改。以前我使用事件和messagebox进行管理-在StackPanel失去焦点或用户按下Enter键后,出现了一个messagebox,询问是否更改pp
若用户选择“否”,那个么所有内容都将恢复到编辑前的原始状态
否则,pp和所有文本框将保存新值
现在,在尝试了绑定和MVVM之后,我真的不想回到所有这些事件,那么正确的方法是什么呢?有多种方法可以实现这一点。
你以前处理事件的方法(可能?)还不错 我只需在程序/产品的MVVM库中创建一个类,其中包含要从UI库订阅的事件和方法 这里有一个例子来说明这一点 MVVM: 用户界面: 您只需在UI助手类中打开一个对话框。您可以将其更改为
EventArgs
[…],但您知道了
基本上你是在指导助手做某事。UI级别负责执行正确的操作(例如,显示对话框或窗口)。下面是一个非常基本和详细的示例,说明如何执行您正在寻找的操作。实现这一点的组件可以也应该被分解成进一步的处理组,但你会明白这一点 我将在这里包括3个类:ViewModel、PreviousState和RelayCommand。然后我将在视图中使用纯绑定。按原样运行此代码,它将通过纯MVVM以您希望的方式工作。然而;花点时间来解读正在发生的事情,因为有更深入更简单的方法来实现这个结果。我相信这是一个受过良好教育的例子,使用纯MVVM可以帮助您理解如何实现这一点。注意:这里使用了一些流行的技术,为了帮助您更好地理解这些技术,需要一个关于MVVM和XAML的短期课程。我的希望是,您可以将其分离开来,以便更好地理解它,并对其进行修改以满足您的需要 我把所有文件放在同一个名称空间中,他把ViewModel、RelayCommand和PreviousState类型都放在一个文件中,只是为了简化发布这个答案
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace Question_Answer_WPF_App
{
public class PersonViewModel : INotifyPropertyChanged
{
string firstName = "Bob";
string lastName = "Smith";
string fullName;
public PersonViewModel()
{
CommitStateCommand = new RelayCommand((obj) => CommitChanges(),(obj) => CanExecuteCommands());
RevertStateCommand = new RelayCommand((obj) => RevertChanges(),(obj) => CanExecuteCommands());
CommitChanges();
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public string FirstName
{
get => firstName;
set
{
firstName = value;
Notify();
UpdateChanges();
}
}
public string LastName
{
get => lastName;
set
{
lastName = value;
Notify();
UpdateChanges();
}
}
public string FullName
{
get => fullName;
private set
{
fullName = value;
Notify();
}
}
private PersonState PreviousState { get; } = new PersonState();
public RelayCommand CommitStateCommand { get; }
public RelayCommand RevertStateCommand { get; }
private void UpdateChanges()
{
FullName = $"{FirstName} {LastName}";
CommitStateCommand.UpdateCanExecute();
RevertStateCommand.UpdateCanExecute();
}
private void CommitChanges()
{
PreviousState.FirstName = FirstName;
PreviousState.LastName = LastName;
UpdateChanges();
}
private void RevertChanges()
{
FirstName = PreviousState?.FirstName;
LastName = PreviousState?.LastName;
CommitChanges();
}
private bool CanExecuteCommands() => PreviousState?.FirstName != FirstName || PreviousState?.LastName != LastName;
}
internal class PersonState
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class RelayCommand : ICommand
{
public Action<object> ExecuteFunction { get; }
public Predicate<object> CanExecutePredicate { get; }
public event EventHandler CanExecuteChanged;
public void UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public RelayCommand(Action<object> executeFunction) : this(executeFunction, (obj) => true) { }
public RelayCommand(Action<object> executeFunction, Predicate<object> canExecutePredicate)
{
ExecuteFunction = executeFunction;
CanExecutePredicate = canExecutePredicate;
}
public bool CanExecute(object parameter) => CanExecutePredicate?.Invoke(parameter) ?? true;
public void Execute(object parameter) => ExecuteFunction.Invoke(parameter);
}
}
使用系统;
使用系统组件模型;
使用System.Runtime.CompilerServices;
使用System.Windows.Input;
名称空间问题\答案\ WPF\应用程序
{
公共类PersonViewModel:INotifyPropertyChanged
{
string firstName=“Bob”;
字符串lastName=“Smith”;
字符串全名;
公共PersonViewModel()
{
CommitStateCommand=new RelayCommand((obj)=>CommitChanges(),(obj)=>CanExecuteCommands());
RevertStateCommand=new RelayCommand((obj)=>RevertChanges(),(obj)=>CanExecuteCommands());
提交更改();
}
公共事件属性更改事件处理程序属性更改;
私有void Notify([CallerMemberName]string property=”“)=>PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(property));
公共字符串名
{
get=>firstName;
设置
{
firstName=值;
通知();
UpdateChanges();
}
}
公共字符串姓氏
{
get=>lastName;
设置
{
lastName=值;
通知();
UpdateChanges();
}
}
公共字符串全名
{
get=>fullName;
专用设备
{
全名=值;
通知();
}
}
private PersonState PreviousState{get;}=new PersonState();
public RelayCommand CommitState命令{get;}
public RelayCommand RevertStateCommand{get;}
私有void UpdateChanges()
{
全名=$“{FirstName}{LastName}”;
CommitStateCommand.UpdateCanExecute();
RevertStateCommand.UpdateCanExecute();
}
私人无效佣金()
{
PreviousState.FirstName=FirstName;
PreviousState.LastName=LastName;
UpdateChanges();
}
私有void RevertChanges()
{
FirstName=以前的状态?.FirstName;
LastName=以前的状态?.LastName;
提交更改();
}
private bool CanExecuteCommands()=>PreviousState?.FirstName!=FirstName | | PreviousState?.LastName!=LastName;
}
内部类个人状态
{
公共字符串名{get;set;}
公共字符串LastName{get;set;}
}
公共类中继命令:ICommand
{
公共操作执行函数{get;}
公共谓词CanExecutePredicate{get;}
公共事件处理程序CanExecuteChanged;
public void UpdateCanExecute()=>CanExecuteChanged?.Invoke(此为EventArgs.Empty);
public RelayCommand(Action executeFunction):this(executeFunction,(obj)=>true){
公共RelayCommand(Action executeFunction,谓词canExecutePredicate)
{
ExecuteFunction=ExecuteFunction;
CanExecutePredicate=CanExecutePredicate;
}
public bool CanExecute(对象参数)=>CanExecutePredicate?.Invoke(参数)??true;
public void Execute(对象参数)=>ExecuteFunction.Invoke(参数);
}
}
main window.xaml
<Window x:Class="Question_Answer_WPF_App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Question_Answer_WPF_App"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="150" />
<Setter Property="Margin" Value="4" />
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="First Name"/>
<TextBox Text="{Binding FirstName, Mode=TwoWay}"/>
<TextBlock Text="Last Name"/>
<TextBox Text="{Binding LastName, Mode=TwoWay}"/>
<TextBlock Text="Full Name"/>
<TextBlock Text="{Binding FullName}" />
<StackPanel Orientation="Horizontal">
<Button Content="Commit"
Command="{Binding CommitStateCommand}" />
<Button Content="Revert"
Command="{Binding RevertStateCommand}" />
</StackPanel>
</StackPanel>
</Window>
<
static class MessageResolver
{
#region Constructor
static MessageResolver()
{
MessageHelper.Exception += OnException;
}
#endregion
#region Events
static void OnException(Exception e)
{
new ExceptionDialog(e).ShowDialog();
}
#endregion
}
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace Question_Answer_WPF_App
{
public class PersonViewModel : INotifyPropertyChanged
{
string firstName = "Bob";
string lastName = "Smith";
string fullName;
public PersonViewModel()
{
CommitStateCommand = new RelayCommand((obj) => CommitChanges(),(obj) => CanExecuteCommands());
RevertStateCommand = new RelayCommand((obj) => RevertChanges(),(obj) => CanExecuteCommands());
CommitChanges();
}
public event PropertyChangedEventHandler PropertyChanged;
private void Notify([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
public string FirstName
{
get => firstName;
set
{
firstName = value;
Notify();
UpdateChanges();
}
}
public string LastName
{
get => lastName;
set
{
lastName = value;
Notify();
UpdateChanges();
}
}
public string FullName
{
get => fullName;
private set
{
fullName = value;
Notify();
}
}
private PersonState PreviousState { get; } = new PersonState();
public RelayCommand CommitStateCommand { get; }
public RelayCommand RevertStateCommand { get; }
private void UpdateChanges()
{
FullName = $"{FirstName} {LastName}";
CommitStateCommand.UpdateCanExecute();
RevertStateCommand.UpdateCanExecute();
}
private void CommitChanges()
{
PreviousState.FirstName = FirstName;
PreviousState.LastName = LastName;
UpdateChanges();
}
private void RevertChanges()
{
FirstName = PreviousState?.FirstName;
LastName = PreviousState?.LastName;
CommitChanges();
}
private bool CanExecuteCommands() => PreviousState?.FirstName != FirstName || PreviousState?.LastName != LastName;
}
internal class PersonState
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class RelayCommand : ICommand
{
public Action<object> ExecuteFunction { get; }
public Predicate<object> CanExecutePredicate { get; }
public event EventHandler CanExecuteChanged;
public void UpdateCanExecute() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public RelayCommand(Action<object> executeFunction) : this(executeFunction, (obj) => true) { }
public RelayCommand(Action<object> executeFunction, Predicate<object> canExecutePredicate)
{
ExecuteFunction = executeFunction;
CanExecutePredicate = canExecutePredicate;
}
public bool CanExecute(object parameter) => CanExecutePredicate?.Invoke(parameter) ?? true;
public void Execute(object parameter) => ExecuteFunction.Invoke(parameter);
}
}
<Window x:Class="Question_Answer_WPF_App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Question_Answer_WPF_App"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="150" />
<Setter Property="Margin" Value="4" />
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="First Name"/>
<TextBox Text="{Binding FirstName, Mode=TwoWay}"/>
<TextBlock Text="Last Name"/>
<TextBox Text="{Binding LastName, Mode=TwoWay}"/>
<TextBlock Text="Full Name"/>
<TextBlock Text="{Binding FullName}" />
<StackPanel Orientation="Horizontal">
<Button Content="Commit"
Command="{Binding CommitStateCommand}" />
<Button Content="Revert"
Command="{Binding RevertStateCommand}" />
</StackPanel>
</StackPanel>
</Window>