65.9K
CodeProject 正在变化。 阅读更多。
Home

用于数据输入的基类 WPF 窗口功能

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.08/5 (7投票s)

2020年3月25日

公共领域

4分钟阅读

viewsIcon

33063

控件验证用户输入,并自动向宿主窗口报告数据是否已更改以及是否输入了必需的数据。

目录

引言

在大多数程序中,有些窗口需要用户输入一些数据,这些数据需要控件进行验证。只有当所有必填数据都输入完成后,用户才能保存。如果用户修改了一些数据但尚未保存,当他关闭窗口时,会收到可能丢失数据的警告。如果所有这些功能都能自动添加到您的所有窗口而无需您编写太多代码,那不是很棒吗?

WpfWindowsLib 提供了此功能。本文描述了它的功能以及如何使用它。WpfWindowsLib 是为 .NET Core 3.1 及更高版本编写的。

用户体验

这可能不是您见过的最漂亮的窗口,但这里的目的是系统地展示各种控件如何以不同的状态显示给用户。在每一行中,相同的控件类型显示3次。在第一列中,每个控件都是空的。在第二列中,控件也是空的,但用户需要在按下保存按钮之前输入一些数据。在第三列中,控件包含一些初始数据。

用户现在必须至少填写所有必填字段。然后,保存按钮才会启用。一旦他按下保存按钮,它就会再次禁用。如果用户随后更改了任何数据,保存按钮将再次启用。已启用的保存按钮会告诉用户他已更改了一些数据。

当用户尝试在保存更改之前关闭窗口时会发生什么?

他会收到警告消息,并且窗口会显示他已更改但尚未保存的数据。然后,他可以决定是关闭窗口并放弃更改,还是继续输入数据并可能保存更改。

Using the Code

您几乎不需要编写任何代码就可以获得所有这些功能。WpfWindowsLib 库提供了以下控件:

  • 知道它们的数据何时已更改
  • 知道它们的数据何时未更改(用户撤销了他的更改)
  • 知道“必需”控件何时缺少数据
  • 知道“必需”控件何时有数据
  • 自动查找它们所在的窗口,并告知窗口每个状态更改

该窗口必须继承自WpfWindowsLib 中的CheckedWindow

<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>

CheckedTextBox 继承自TextBox,添加了ICheck 功能(参见下文说明)和IsRequired 属性。当此属性设置为true时,用户必须提供一个值(即Text.Length>0),然后保存按钮才能可用。

与XAML一样,在代码隐藏中,窗口也必须继承自CheckedWindow

using System.Windows;
using WpfWindowsLib;

namespace Samples {
  public partial class SampleWindow: CheckedWindow {
    public SampleWindow() {
      InitializeComponent();

      //write some code here to display data coming from a database, etc.
      TestCheckedTextBox.Text = database.Read(...);
      SaveButton.Click += saveButton_Click;
      updateSaveButtonIsEnabled();
    }

    private void saveButton_Click(object sender, RoutedEventArgs e) {
      //write some code here to save the data the user has entered
      database.Write(..., TestCheckedTextBox.Text);
      Close();
    }

    private void updateSaveButtonIsEnabled() {
      SaveButton.IsEnabled = HasICheckChanged && IsAvailable;
    }

    protected override void OnICheckChanged() {
      updateSaveButtonIsEnabled();
    }

    protected override void OnIsAvailableChanged() {
      updateSaveButtonIsEnabled();
    }
  }
}

该窗口只需要添加非常少量的代码。

保存按钮

窗口的外观没有要求。但最有可能的是,会有一个保存按钮。当某些数据已更改(=HasICheckChanged)且所有必填数据都已输入(=IsAvailable)时,此按钮将启用。

调用updateSaveButtonIsEnabled()

HasICheckChangedIsAvailableCheckedWindow 的属性。如果它们发生更改,CheckedWindow 会调用OnICheckChanged()OnIsAvailableChanged(),需要重写这些方法来更新保存按钮的状态。

幕后运作

使用的控件需要提供ICHeck 接口的功能。WpfWindowsLib 提供了以下控件:

  • 自动完成框
  • CheckBox
  • ComboBox
  • 日期选择器
  • 十进制文本框
  • 电子邮件文本框
  • 整数文本框
  • 电话文本框
  • 文本框

人们不仅限于这些控件,还可以继承自任何现有控件并添加一个实现ICheck 接口功能的IChecker

namespace WpfWindowsLib {

  public interface ICheck {

    /// <summary>
    /// Has the user changed the initial value of the control ?
    /// </summary>
    bool HasChanged { get; }

    /// <summary>
    /// Needs the user to change the initial value of the control ?
    /// </summary>
    bool IsRequired { get; }

    /// <summary>
    /// Has the user changed the initial value of the required control ?
    /// </summary>
    bool IsAvailable { get; }

    /// <summary>
    /// Raised when the user changes the initial value of the control 
    /// or when the user undoes any change and
    /// enters the initial value again.
    /// </summary>
    event Action  HasChangedEvent;

    /// <summary>
    /// Raised when the user changes the initial value of the required value control 
    /// or when the user undoes 
    /// any change and enters the initial value again.
    /// </summary>
    event Action  IsAvailableEvent;

    /// <summary>
    /// Tells the control to use the present value as initial value.
    /// </summary>
    void ResetHasChanged();

    /// <summary>
    /// Changes the background color of the control if its value is now 
    /// different than the initial value 
    /// and isChanged is true. If isChanged is false, the background color 
    /// gets displayed from when the
    /// control got initialised.
    /// </summary>
    /// <param name="isChanged"></param>
    void ShowChanged(bool isChanged);
  }
}
  1. 在初始化期间,ICheck 控件会搜索它所在的窗口。如果该窗口继承自CheckedWindow,它会向该窗口注册。在此注册期间,CheckedWindow 会订阅控件的HasChangedEventIsAvailableEvent 事件。
  2. 当用户随后更改控件中的数据并导致HasChangedIsAvailable 发生更改时,控件会引发相应的事件,从而通知CheckedWindowCheckedWindow 会查询所有已注册的控件,以评估其自身的HasICheckChangedIsAvailable 属性是否需要更改,然后调用OnICheckChangedOnIsAvailableChanged,这使得继承窗口有机会相应地启用或禁用保存按钮。
  3. 当用户尝试关闭窗口时,CheckedWindow 会检查是否有任何控件包含未保存的数据。如果有,它会标记这些控件,以便用户可以看到哪些数据未保存。然后,CheckedWindow 会询问用户是否真的要关闭窗口并丢失输入的数据。

获取WpfWindowsLib

最新版本可在Github上获取:https://github.com/PeterHuberSg/WpfWindowsLib

下载或克隆所有内容到您的 PC,这将为您提供一个名为 WpfWindowsLib 的解决方案,其中包含以下项目

  • WpfWindowsLib:(*.Dll)需要从您的其他解决方案中引用
  • Samples: WPF Core 应用程序,展示所有 WpfWindowsLib 控件
  • WpfWindowsLibTest:包含一些 WpfWindowsLib 单元测试

推荐阅读

历史

  • 2020 年 2 月 20 日:初始版本。
© . All rights reserved.