【问题标题】:How to refactor duplicated code in Windows Forms?如何重构 Windows 窗体中的重复代码?
【发布时间】:2021-01-19 09:47:09
【问题描述】:

我目前正在重构 Windows 窗体项目中 UserControls 中的大量重复代码。

在 UI 层内实现了许多逻辑,该架构还有很多不足之处。这个问题最终会得到解决,我现在正在努力改进我手头上的东西。

问题是,很多重复的代码直接与控件相关,例如:

private void InitDestinationPathControls(string path)
{
  if (someField)
  {
    tbOne.Enabled = false;
    tbOne.Visible = false;
    btnTwo.Enabled = false;
    btnTwo.Visible = false;
    tbOne.Text = string.Empty;
    return;
  }
  // (...)
}

不要太执着于引用的代码本身,这只是一个例子。

我想将此代码移至一个公共基类,但它直接依赖于特定字段(即使它们在所有控件中也完全相同)。另一方面,这些字段是由设计器生成的,因此我无法将它们提取到基类中。

我想到的唯一一件事就是将这些字段作为参数传递给基类中的方法,但是如果某个方法使用了很多它们,我最终会得到一个可怕的接口部分,这不会' t 实际上提高了太多的可读性。

如何处理 Windows 窗体中用户控件的这些常见部分?

【问题讨论】:

  • 似乎缺少一些东西和更多的代码。根类中的方法听起来不错,否则您是否尝试过具有扩展方法的辅助类?
  • 那些启用/可见/文本设置可以是任何底层业务对象中的数据绑定属性。事实上,没有什么能阻止您使用 MVVM,甚至是 WinForms 应用程序。 Here 是一个演示应用程序,它在 WinForms 和 WPF GUI 中使用相同的 ViewModel。 here 是另一个(稍微简单一些)ViewModel 示例,它具有一些 ...Enabled 属性。

标签: c# winforms refactoring


【解决方案1】:

显然,您拥有以多种形式出现的多个控件的组合。换句话说:您有一些按钮、组合框等,您想在不同的表单上显示,并且您希望它们具有相同的行为。

如果不仅这些控件的行为在所有表单上都相同,而且布局也相同,那么考虑创建一个派生自 UserControl 的类。

UserControl 对外界隐藏了哪些控件被显示、它们是如何可视化的以及它们的行为方式。

我假设您已经从数据的可视化中提取了模型。

如果这个用户控件的所有实例都应该调用同一个类的不同对象的相同函数,那么给你的特殊用户控件一个代表这个类的属性,或者至少一个接口。在构造过程中,您可以插入应在操作员输入后处理操作的实际类。

另一方面,如果每个表单的布局不同,但按钮、组合框等的集合以及它们的行为在所有显示此控件集合的表单上都是相似的,并且它们有很多共同的行为,请考虑创建您自己的 ControlCollection。

例如,如果在多个表单上,您有一个选择(文本)文件的按钮、带有所选文件的名称、大小和创建日期的标签,以及一个显示文本文件内容的编辑框,但是你想以不同的方式布局它们,考虑这样的事情:

class FileDisplayControls : IDisposable
{
    public Button ButtonSelectFile {get;} = new Button();
    public Label labelFileName {get; } = new Label();
    public Label labelFileSize {get; } = new Label();
    public TextBox textFileContents {get; } = new FileContents();

    private void ButtonSelectFile_Clicked(object sender, ...)
    {
        // TODO: open file dialog, display result in labels and text box
    }
}

构造函数可以设置控件的初始布局属性,并订阅事件,以便控件对用户输入做出反应。

该类的用户(= 代码,而不是操作员)立即拥有一组具有一些标准行为的控件,例如对按钮单击做出反应。他所要做的就是以自己的形式设置项目的位置。如果需要更改其他布局属性(颜色、背景)并将它们放在他自己的表单上。

如果你想防止其他人改变控件的位置以外的其他视觉方面,不要发布控件自己,只发布控件的位置:

public System.Drawing.Point LocationSelectFileButton
{
    get => this.buttonSelectFile.Location;
    set => this.buttonSelectFile.Location = value;
}

public System.Drawing.Point LocationFileContentTextBox
{
    get => this.textBoxFileContent.Location;
    set => this.textBoxFileContent.Location = value;
}

等等

如果需要,您可以为用户添加事件:

public event EventHandler SelectedFileChanged;

public string FileName => this.labelFileName.Text;
public string FileContents => this.textBoxFileContent.Text;

等等

结论

您选择的解决方案取决于各种表单上控件之间的相似性:

  • 如果行为和布局都相同:UserControl
  • 如果只有位置和一些属性不同:特殊类具有不同的属性。这样您就可以强制使用更相似的样式:所有“选择文件”按钮看起来都相似。
  • 如果只有一两个行为不同:添加Action<...> 属性或事件
  • 如果您想完全控制布局:公开控件。
  • 显示这些控件的所有表单的常见行为(在我的示例中:如何选择文件以及选择文件时执行的操作)都在类中。

【讨论】:

    【解决方案2】:

    可以将重复代码提取到方法中(可能在基类中,或作为辅助类中的静态方法)

    public void DisableControls(params Control[] controls)
    {
        foreach(var c in Controls)
        {
            c.Enabled = false;
            c.Visible = false;
            if (c is TextBox t)
            {
                t.Text = string.Empty;
            }
        }
    }
    
    private void InitDestinationPathControls(string path)
    {
         if (someField)
         {
            DisableControls(tbOne, btnTwo);
            return;
         }
    
         // (...)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-16
      • 1970-01-01
      • 1970-01-01
      • 2016-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多