WPF使用MVVM時,表單驗證
阿新 • • 發佈:2019-02-12
<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;
}