简体   繁体   English

整个控件形式的验证顺序

[英]Validation order for an entire form of controls

I'm learning this wpf stuff and trying to get my head around validation of controls.我正在学习这个 wpf 的东西,并试图让我的头脑围绕控件的验证。 Specifically what I'm looking for is this...具体来说,我正在寻找的是这个......

  1. A form can have 100 controls on it (exaggerating, but possible).一个表单可以有 100 个控件(夸张,但可能)。 The form's layout and flow are a specific order (via tabbed sequence for user).表单的布局和流程是特定的顺序(通过用户的选项卡式顺序)。 A user may never get to some "required" fields and click on a "Save" button.用户可能永远不会到达某些“必填”字段并单击“保存”按钮。 How can I trigger it so all the controls force triggering their own respective "Validation" events.我如何触发它以便所有控件强制触发它们各自的“验证”事件。

  2. Based on above, does the WPF framework process the validation rules in the tab order the user is looking at.基于上述,WPF 框架是否按照用户查看的 Tab 键顺序处理验证规则。 If not, how can that be controlled to match the data entry flow instead of bouncing around in the sequential order the application happens to create objects and their respective validation rules.如果没有,如何控制它以匹配数据输入流,而不是按应用程序碰巧创建对象及其各自验证规则的顺序来回跳动。

  3. Is there a way to have ALL failed controls triggered for the default behavior of putting a red border box around the failed control instead of only one at a time.有没有办法让所有失败的控件触发默认行为,即在失败的控件周围放置一个红色边框框,而不是一次只放置一个。

Thanks谢谢

Typically, to accomplish what you are looking for you use an MVVM type pattern.通常,要完成您要查找的内容,请使用 MVVM 类型模式。 This means that you bind each control that collects data in your WPF form to a backing field or property.这意味着您将在 WPF 表单中收集数据的每个控件绑定到支持字段或属性。 You add validation to the binding, with a style that will cause the red border box.您向绑定添加验证,其样式将导致红色边框框。 For controls with required data, part of the validation is that they are filled in. You could define a single validation rule for this called "ValidWhenHasData" or some such.对于具有必需数据的控件,验证的一部分是填充它们。您可以为此定义一个称为“ValidWhenHasData”或类似规则的验证规则。

To cause the validations to trigger only when you press "save" or the like, there are a number of ways you can do this.要使验证仅在您按“保存”等时触发,您可以通过多种方式执行此操作。 I typically make a property in each validation rule called "IsEnabled" and set it to false by default;我通常在每个验证规则中创建一个名为“IsEnabled”的属性,并将其默认设置为 false; if set to false, the validation rule always returns valid.如果设置为 false,则验证规则始终返回有效。 I then add a list in the code-behind of the controls that I want to validate.然后,我在要验证的控件的代码隐藏中添加一个列表。 When "save" is clicked, I go through the list and set all the validation rules' IsEnabled to true, clear all errors on the controls in the list, and then refresh the binding on each.单击“保存”时,我浏览列表并将所有验证规则的 IsEnabled 设置为 true,清除列表中控件上的所有错误,然后刷新每个控件上的绑定。 This will display the red rectangles on any that are not filled in or whatever else you have defined as an error condition.这将在任何未填充或您定义为错误条件的任何其他内容上显示红色矩形。 You can also use this list to set focus to the first control that failed validation, in the order you choose.您还可以使用此列表按照您选择的顺序将焦点设置到验证失败的第一个控件。

Example validation control template, which includes placeholder for validation error tooltip:示例验证控件模板,其中包括验证错误工具提示的占位符:

<ControlTemplate x:Key="errorTemplate">
    <Canvas Width="{Binding Path=AdornedElement.ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}" Height="{Binding Path=AdornedElement.ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}">
        <Border BorderBrush="Red" BorderThickness="1">
            <AdornedElementPlaceholder/>
        </Border>
        <Border Canvas.Top="-5" Canvas.Right="-5" BorderBrush="Gray" BorderThickness="1" >
            <TextBlock x:Name="errorBlock" TextAlignment="Center" Background="Red" Foreground="White" Width="10" Height="10" FontSize="9" ctl:valTooltip.MessageBody="{Binding Path=AdornedElement.(Validation.Errors)[0].ErrorContent,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Adorner}}}">*</TextBlock>
        </Border>
    </Canvas>
</ControlTemplate>

Example validation binding:示例验证绑定:

<TextBox x:Name="TBNumItems" Margin="2,2,2,2" MinWidth="40" HorizontalAlignment="Left" Validation.ErrorTemplate="{StaticResource errorTemplate}">
    <TextBox.Text>
        <Binding x:Name="NumItemsBinding" Path="NumItems" NotifyOnSourceUpdated="True" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <cal:UIntValidationRule x:Name="NumItemsValidationRule" MinValue="1" MaxValue="99999" IsEnabled="False"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Example code behind for validation:后面用于验证的示例代码:

/// <summary>
/// Clears all validation errors
/// </summary>
void ClearAllValidationErrors()
{
    Validation.ClearInvalid(TBNumItems.GetBindingExpression(TextBox.TextProperty));
}


/// <summary>
///  Revalidates everything
/// </summary>
void RevalidateAll()
{
    ClearAllValidationErrors();

    TBNumItems.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

Make your data object implement IDataErrorInfo , which will perform a validation check on a property when the user changes it, then use the following style to apply the red border to controls that have a validation error:使您的数据对象实现IDataErrorInfo ,它将在用户更改属性时对其执行验证检查,然后使用以下样式将红色边框应用于具有验证错误的控件:

<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="ToolTip" Value="{Binding 
                Path=(Validation.Errors)[0].ErrorContent, 
                RelativeSource={x:Static RelativeSource.Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

This will这会

  1. Only perform a validation check for a single property when that property gets changed仅在该属性更改时对单个属性执行验证检查
  2. Only (and always) show the red validation error border on controls that are bound to an invalid property仅(并且始终)在绑定到无效属性的控件上显示红色验证错误边框

Edit编辑

Here's a sample of a how I would implement validation on an object:这是我如何在对象上实现验证的示例:

public class MyObject : ValidatingObject
{
    public MyObject()
    {
        // Add Properties to Validate here
        this.ValidatedProperties.Add("SomeNumber");
    }

    // Implement validation rules here
    public override string GetValidationError(string propertyName)
    {
        if (ValidatedProperties.IndexOf(propertyName) < 0)
        {
            return null;
        }

        string s = null;

        switch (propertyName)
        {
            case "SomeNumber":
                if (SomeNumber <= 0)
                    s = "SomeNumber must be greater than 0";
                break;
        }

        return s;
    }

}

And my ValidatingObject base class which implements IDataErrorInfo usually contains the following:我实现IDataErrorInfo ValidatingObject基类通常包含以下内容:

#region IDataErrorInfo & Validation Members

/// <summary>
/// List of Property Names that should be validated
/// </summary>
protected List<string> ValidatedProperties = new List<string>();

public abstract string GetValidationError(string propertyName);

string IDataErrorInfo.Error { get { return null; } }

string IDataErrorInfo.this[string propertyName]
{
    get { return this.GetValidationError(propertyName); }
}

public bool IsValid
{
    get
    {
        return (GetValidationError() == null);
    }
}

public string GetValidationError()
{
    string error = null;

    if (ValidatedProperties != null)
    {
        foreach (string s in ValidatedProperties)
        {
            error = GetValidationError(s);
            if (error != null)
            {
                return error;
            }
        }
    }

    return error;
}

#endregion // IDataErrorInfo & Validation Members

I faced the same problem.我遇到了同样的问题。 I wanted controls who know if they are required and report automatically any change to the hosting Window.我想要知道是否需要它们并自动报告对托管窗口的任何更改的控件。 I didn't want to have to write complicated XAML or other code, just placing the control and setting a property to indicate if user input is required.我不想编写复杂的 XAML 或其他代码,只需放置控件并设置一个属性来指示是否需要用户输入。 The control searches then automatically the host window and informs it when the user keys in required data or deletes it, so the window can change the state of the Save button.控件然后自动搜索主机窗口并在用户键入所需数据或删除它时通知它,因此窗口可以更改保存按钮的状态。 The final solution looks like this:最终的解决方案如下所示:

<wwl:CheckedWindow x:Class="Samples.SampleWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wwl="clr-namespace:WpfWindowsLib;assembly=WpfWindowsLib">
  <StackPanel>
    <wwl:CheckedTextBox x:Name="TestCheckedTextBox" MinWidth="100" MaxLength="20" IsRequired="True"/>
    <Button x:Name="SaveButton" Content="_Save"/>
  </StackPanel>
</wwl:CheckedWindow>

For this to work added to the most common WPF controls an IChecker, which does the following:为此,向最常见的 WPF 控件添加了一个 IChecker,它执行以下操作:

  • know when the data has changed知道数据何时发生变化
  • know when the data has been unchanged (user undid his change)知道数据何时未更改(用户撤消更改)
  • know when a "required" control is lacking data知道“必需”控件何时缺少数据
  • know when a "required" control has data知道“必需”控件何时有数据
  • find automatically the window the control is in and inform the Window about each state change自动查找控件所在的窗口并通知窗口每个状态变化

If the window gets informed by any control that the control's state has changed, the window then queries automatically all other controls about their state.如果任何控件通知窗口控件的状态已更改,则窗口会自动查询所有其他控件的状态。 If all required controls have data, the Save Button gets enabled.如果所有必需的控件都有数据,则启用保存按钮。

Knowing that the user has changed some data it is useful, when the user tries to close the window without saving .当用户尝试关闭窗口而不保存 . He gets then automatically a warning and gives him the choice if he wants to save or discard the data.然后他会自动收到警告,并让他选择是否要保存或丢弃数据。

I had to write too much code to be posted here, but now life is very easy.我不得不写太多的代码来张贴在这里,但现在生活很容易。 Just place the control into the XAML and add few lines of code to the Window for the Save Button, that's all.只需将控件放入 XAML 并将几行代码添加到保存按钮的窗口,就可以了。 Here is an detailed explanation: https://www.codeproject.com/Articles/5257393/Base-WPF-Window-functionality-for-data-entry The code is on GitHub: https://github.com/PeterHuberSg/WpfWindowsLib这里有详细解释: https : //www.codeproject.com/Articles/5257393/Base-WPF-Window-functionality-for-data-entry代码在GitHub上: https : //github.com/PeterHuberSg/WpfWindowsLib

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM