WPF使用MVVM时,表单验证

来源:互联网 发布:南京浦口行知基地图片 编辑:程序博客网 时间:2024/06/07 03:48

<StackPanel  Orientation="Horizontal" Height="30">    <TextBlock TextWrapping="Wrap" Text="底板" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVT}" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding H_H, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="60" VerticalAlignment="Center"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVW}" VerticalAlignment="Center" Margin="30,0,3,0" />    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, Path=H_B}" Width="60" VerticalAlignment="Center"/></StackPanel><StackPanel  Orientation="Horizontal" Height="30">      <TextBlock TextWrapping="Wrap" Text="腹板" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVT}" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding H_T2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="60" VerticalAlignment="Center"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVW}" VerticalAlignment="Center" Margin="30,0,3,0" />    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding H_D, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="60" VerticalAlignment="Center"/></StackPanel> <StackPanel  Orientation="Horizontal" Height="30">    <TextBlock TextWrapping="Wrap" Text="面板" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVT}" VerticalAlignment="Center" Margin="20,0,3,0"/>    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding B_H1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="60" VerticalAlignment="Center"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVW}" VerticalAlignment="Center" Margin="30,0,3,0" Visibility="{Binding Visib}" />    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding B_H2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="60" Visibility="{Binding Visib}" VerticalAlignment="Center"/>    <TextBlock TextWrapping="Wrap" Text="{StaticResource X_TbVL}" VerticalAlignment="Center" Margin="30,0,3,0" />    <TextBox TextAlignment="Right" Margin="5,0,3,0" TextWrapping="Wrap" Text="{Binding H_T1, UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True}" Width="60" VerticalAlignment="Center"/>    <TextBlock TextWrapping="Wrap" Text="TYPE" VerticalAlignment="Center" Margin="30,0,3,0"/>    <ComboBox Height="25" Margin="5,0,3,0" SelectedIndex="{Binding CoverPlateType}">        <ComboBoxItem Content="覆盖"/>        <ComboBoxItem Content="嵌入"/>    </ComboBox></StackPanel><Button Content="保存" HorizontalAlignment="Left" VerticalAlignment="Center" Width="80" Margin="120,0,0,0" Command="{Binding WorksSaveManage}" Click="saveButton_Click" /><Button Content="删除" HorizontalAlignment="Right" VerticalAlignment="Center" Width="75" Margin="60,0,0,0" Command="{Binding WorksDeleteManage}" Click="Button2_Click"/><Button Content="关闭" HorizontalAlignment="Left" VerticalAlignment="Center" Width="80" Margin="60,0,0,0" Click="Button_Click" />

错误提示模板:

<Window.Resources><ControlTemplate x:Key="ErrorTemplate">            <DockPanel LastChildFill="true">                <Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"                            ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">                    <TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">                    </TextBlock>                </Border>                <AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >                    <Border BorderBrush="red" BorderThickness="1" />                </AdornedElementPlaceholder>            </DockPanel>        </ControlTemplate>        <Style TargetType="TextBox">            <Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}">            </Setter>            <Style.Triggers>                <Trigger Property="Validation.HasError" Value="True">                    <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>                </Trigger>            </Style.Triggers>        </Style>    </Window.Resources>


应用场景描述:表单中TextBox数据验证条件依赖其他TextBox数据(例:腹板T<0.5*底板W)。

问题1:先录入腹板T数据,后录入底板W数据,不能触发腹板T 数据验证。

解决方法1:底板W加入和腹板T相关的验证,实现双向关联验证。优点,解决方式简单;缺点,用户体验差,增加表验证逻辑复杂性(整个表单校验时,校验失败,2选1提示数据内容)。

方法2:终端校验,保存按钮触发整个表单校验。优点:减少表单验证条件。缺点:验证条件依赖其他TextBox数据的校验,需要保存按钮触发校验提示。

问题2:表单验证失败,保存按钮不提交数据。(能力原因,没有找到ViewModel中校验整个表单的方式(╯︵╰),有大神请赐教。

方法1:cs代码中添加保存按钮Click事件,返回整个表单验证结果给ViewModel。(关键内容校验控件的 ((TextBox)controlName).GetBindingExpression(TextBox.TextProperty).UpdateTarget()

ViewModel代码:

public class WorksManageViewModel : NotificationObject, IDataErrorInfo{        /// <summary>        /// 底板厚        /// </summary>        public decimal H_H        {            get { return MainSelect.H_H; }            set { MainSelect.H_H = value; this.RaisePropertyChanged("H_H"); }        }        /// <summary>        /// 底板宽        /// </summary>        public decimal H_B        {            get { return MainSelect.H_B; }            set { MainSelect.H_B = value; this.RaisePropertyChanged("H_B");}        }        /// <summary>        /// 腹板厚        /// </summary>        public decimal H_T2        {            get { return MainSelect.H_T2; }            set { MainSelect.H_T2 = value; this.RaisePropertyChanged("H_T2"); }        }        /// <summary>        /// 腹板宽        /// </summary>        public decimal H_D        {            get { return MainSelect.H_D; }            set { MainSelect.H_D = value; this.RaisePropertyChanged("H_D"); }        }        /// <summary>        /// 面板厚        /// </summary>        public decimal B_H1        {            get { return MainSelect.B_H1; }            set { MainSelect.B_H1 = value; this.RaisePropertyChanged("B_H1");}        }        /// <summary>        /// 面板宽        /// </summary>        public decimal B_H2        {            get { return MainSelect.B_H2; }            set { MainSelect.B_H2 = value; this.RaisePropertyChanged("B_H2"); }        }        /// <summary>        /// 箱体长度        /// </summary>        public decimal H_T1        {            get { return MainSelect.H_T1; }            set { MainSelect.H_T1 = value; this.RaisePropertyChanged("H_T1"); }        }        public string this[string columnName]        {            get { return GetErrorFor(columnName); }        }        private string _error;        public string Error        {            get { return _error; }            set { _error = value; }        }        /// <summary>        /// 校验方式        /// </summary>        /// <param name="columnName"></param>        /// <returns></returns>        public string GetErrorFor(string columnName)        {            switch (columnName)            {                case "H_H":                    if (0 >= H_H) return columnName + " 必须大于0"; break;                case "H_B":                    if (0 >= H_B) return columnName + " 必须大于0"; break;                case "H_T2":                    if (0 >= H_T2) return columnName + " 必须大于0";                    if (2 * H_T2 >= H_B) return columnName + string.Format(" 必须小于(0.5*底板W){0}", (H_B / 2).ToString());                    break;                case "H_D":                    if (0 >= H_D) return columnName + " 必须大于0"; break;                case "B_H1":                    if (0 >= B_H1) return columnName + " 必须大于0";                    else                    {                        switch (CoverPlateType)                        {                            case 1:                                if (B_H1 >= H_D)                                {                                    return columnName + string.Format(" 必须小于(腹板W){0}", H_D.ToString());                                }                                break;                        }                    }                    break;                case "B_H2":                    switch (CoverPlateType)                    {                        case 0:                            if (2 * H_T2 >= B_H2)                                return columnName + string.Format(" 必须大于(2*腹板T){0}", (2 * H_T2).ToString());                            break;                        case 1:                            if (B_H2 >= H_B - 2 * H_T2)                            {                                return columnName + string.Format(" 必须大于(底板W-2*腹板T){0}", (H_B - 2 * H_T2).ToString());                            }                            break;                    }                    break;                case "H_T1":                    if (0 >= H_T1)                        return columnName + " 必须大于0";                    break;            }            return "";        }}

cs文件代码:

        private void saveButton_Click(object sender, RoutedEventArgs e)        {            ((WorksManageViewModel)this.DataContext).IsValid = IsValid(this);        }        // Validate all dependency objects in a window        bool IsValid(DependencyObject node)        {            // Check if dependency object was passed            if (node != null)            {                //Check DependencyObject is TextBox                 //NOTE:Update DataSource Binding                if (node is TextBox)                {                    BindingExpression binding = ((TextBox)node).GetBindingExpression(TextBox.TextProperty);                    binding.UpdateTarget();                }                // Check if dependency object is valid.                // NOTE: Validation.GetHasError works for controls that have validation rules attached                 bool isValid = !Validation.GetHasError(node);                if (!isValid)                {                    // If the dependency object is invalid, and it can receive the focus,                    // set the focus                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);                    return false;                }            }            // If this dependency object is valid, check all child dependency objects            foreach (object subnode in LogicalTreeHelper.GetChildren(node))            {                if (subnode is DependencyObject)                {                    // If a child dependency object is invalid, return false immediately,                    // otherwise keep checking                    if (IsValid((DependencyObject)subnode) == false) return false;                }            }            // All dependency objects are valid            return true;        }