你的位置:首页 > ASP.net教程

[ASP.net教程]MVVM实现ViewModel获取View输入验证状态


由于Binding只把Convert成功的值送往Source,当目标中的值Convert失败时Source的值依然是旧值,所以ViewModel必须获取View的输入验证状态,以下是本人的实现。

当“+”号两边输入正确时,“Add”可用,当所有“+”号两边输入正确时,“Add All”可用。

通过Behavior添加Validation.ErrorEvent路由事件的事件处理器,在该事件处理器中把HasError状态写入自定义的附加属性,附加属性可以绑定。

Behavior派生类引用System.Windows.Interactivity.dll,代码如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Windows;using System.Windows.Controls;using System.Windows.Interactivity;namespace Calculater{  public class NotifyErrorBehavior : Behavior<UIElement>  {    protected override void OnAttached()    {      base.OnAttached();      this.AssociatedObject.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(HasErrorChanged));    }    protected override void OnDetaching()    {      base.OnDetaching();      this.AssociatedObject.RemoveHandler(Validation.ErrorEvent, new RoutedEventHandler(HasErrorChanged));    }    private static void HasErrorChanged(object sender, RoutedEventArgs e)    {      DependencyObject d = e.OriginalSource as DependencyObject;      HasErrorHelper.SetHasError(d, Validation.GetHasError(d));    }  }}

附加属性代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;namespace Calculater{  public class HasErrorHelper  {    public static bool GetHasError(DependencyObject obj)    {      return (bool)obj.GetValue(HasErrorProperty);    }    public static void SetHasError(DependencyObject obj, bool value)    {      obj.SetValue(HasErrorProperty, value);    }    public static readonly DependencyProperty HasErrorProperty =      DependencyProperty.RegisterAttached("HasError", typeof(bool), typeof(HasErrorHelper), new PropertyMetadata(false));  }}

View代码:

<UserControl x:Class="Calculater.ChildCalculater"       ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       ="http://schemas.microsoft.com/winfx/2006/xaml"       ="http://schemas.open"       ="http://schemas.microsoft.com/expression/blend/2008"       ="clr-namespace:Calculater"       ="http://schemas.microsoft.com/expression/2010/interactivity"       mc:Ignorable="d"       d:DesignHeight="80" d:DesignWidth="500">  <i:Interaction.Behaviors>    <local:NotifyErrorBehavior></local:NotifyErrorBehavior>  </i:Interaction.Behaviors>  <Grid>    <Grid.ColumnDefinitions>      <ColumnDefinition />      <ColumnDefinition Width="Auto"/>      <ColumnDefinition />      <ColumnDefinition Width="Auto"/>      <ColumnDefinition />      <ColumnDefinition Width="Auto"/>      <ColumnDefinition Width="Auto"/>    </Grid.ColumnDefinitions>    <TextBox Margin="3" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" local:HasErrorHelper.HasError="{Binding Path=HasErrorX,Mode=OneWayToSource}">      <Binding Path="X" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" TargetNullValue="" />    </TextBox>    <Label Margin="3" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Column="1">+</Label>    <TextBox Margin="3" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="2" local:HasErrorHelper.HasError="{Binding Path=HasErrorY,Mode=OneWayToSource}">      <Binding Path="Y" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" TargetNullValue="" />    </TextBox>    <Label Margin="3" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Column="3">=</Label>    <TextBox Margin="3" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Column="4" IsReadOnly="True">      <Binding Path="Sum" TargetNullValue="" />    </TextBox>    <Button Margin="3" Grid.Column="5" Padding="3" Command="{Binding CalculateCommand}">Add</Button>    <Button Margin="3" Grid.Column="6" Padding="3" Command="{Binding ResetCommand}">Reset</Button>  </Grid></UserControl>

ViewModel引用Microsoft.Practices.Prism.dll,代码如下:

using Microsoft.Practices.Prism.Commands;using Microsoft.Practices.Prism.ViewModel;using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Calculater{  class ChildCalculaterViewModel : NotificationObject  {    public ChildCalculaterViewModel()    {      this._calculateCommand = new DelegateCommand(this.Calculate, this.CanCalculate);      this._resetCommand = new DelegateCommand(this.Reset);      CalculaterCommand.CalculateAllCommand.RegisterCommand(this.CalculateCommand);      CalculaterCommand.ResetAllCommand.RegisterCommand(this.ResetCommand);          }    private double? _x;    public double? X    {      get { return _x; }      set      {        if (_x != value)        {          _x = value;          this.RaisePropertyChanged("X");          this.Sum = null;          this.CalculateCommand.RaiseCanExecuteChanged();        }      }    }    private double? _y;    public double? Y    {      get { return _y; }      set      {        if (_y != value)        {          _y = value;          this.RaisePropertyChanged("Y");          this.Sum = null;          this.CalculateCommand.RaiseCanExecuteChanged();        }      }    }    private double? _sum;    public double? Sum    {      get { return _sum; }      set      {        if (_sum != value)        {          _sum = value;          this.RaisePropertyChanged("Sum");        }      }    }    private bool _hasErrorX;    public bool HasErrorX    {      get { return _hasErrorX; }      set      {        if (_hasErrorX != value)        {          _hasErrorX = value;          this.Sum = null;          this.CalculateCommand.RaiseCanExecuteChanged();        }      }    }    private bool _hasErrorY;    public bool HasErrorY    {      get { return _hasErrorY; }      set      {        if (_hasErrorY != value)        {          _hasErrorY = value;          this.Sum = null;          this.CalculateCommand.RaiseCanExecuteChanged();        }      }    }    private DelegateCommand _calculateCommand;    public DelegateCommand CalculateCommand    {      get { return _calculateCommand; }    }    private bool CanCalculate()    {      if (this.HasErrorX || this.HasErrorY || !this.X.HasValue || !this.Y.HasValue)      {        return false;      }      return true;    }    private void Calculate()    {      try      {        double x = this.X.Value;        double y = this.Y.Value;        this.Sum = x + y;      }      catch      {        return;      }    }    private DelegateCommand _resetCommand;    public DelegateCommand ResetCommand    {      get { return _resetCommand; }    }    private void Reset()    {      this.X = null;      this.Y = null;      this.Sum = null;    }  }}

主窗体代码:

<Window x:Class="Calculater.Shell"    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    ="http://schemas.microsoft.com/winfx/2006/xaml"    ="clr-namespace:Calculater"    ="http://www.codeplex.com/prism"    Title="Calculater" Height="300" Width="600">  <Grid>    <Grid.RowDefinitions>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>      <RowDefinition Height="Auto"/>      <RowDefinition />    </Grid.RowDefinitions>    <local:ChildCalculater></local:ChildCalculater>    <local:ChildCalculater Grid.Row="1"></local:ChildCalculater>    <local:ChildCalculater Grid.Row="2"></local:ChildCalculater>    <Grid Grid.Row="3">      <Grid.ColumnDefinitions>        <ColumnDefinition />        <ColumnDefinition />      </Grid.ColumnDefinitions>      <Button Margin="3" HorizontalAlignment="Right" Padding="3" Content="Add All" Command="{x:Static local:CalculaterCommand.CalculateAllCommand}"></Button>      <Button Margin="3" HorizontalAlignment="Left" Padding="3" Grid.Column="1" Content="Reset All" Command="{x:Static local:CalculaterCommand.ResetAllCommand}"></Button>    </Grid>  </Grid></Window>

using Microsoft.Practices.Prism.Commands;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Calculater{  public static class CalculaterCommand  {    private static CompositeCommand _calculateAllCommand = new CompositeCommand();    public static CompositeCommand CalculateAllCommand    {      get { return _calculateAllCommand; }    }    private static CompositeCommand _resetAllCommand = new CompositeCommand();    public static CompositeCommand ResetAllCommand    {      get { return _resetAllCommand; }    }  }}