10

我是自学编程的人,没有接受过任何正式的 .NET 编程培训。

不久前,我开始使用 C# 开发一个 GUI 程序来控制传感器,并且该项目已经开花结果。我只是想知道如何最好地在我的表单中组织代码,尤其是 UI 代码。

我的表格目前一团糟,或者至少在我看来是一团糟。

  • 我有一个构造函数,它初始化所有参数并创建事件。
  • 我有一个巨大的 State 属性,当用户通过由 States 枚举控制的应用程序(即:断开连接、连接、设置、扫描)时,它会更新我所有表单控件的启用状态。
  • 我有 3-10 个通过属性访问的私有变量,其中一些在更改表单元素的值方面有副作用。
  • 我有很多“UpdateXXX”函数来处理依赖于其他 UI 元素的 UI 元素 - 即:如果更改了传感器,则更改波特率下拉列表。它们被分成区域
  • 我有很多事件调用这些更新函数
  • 我有一个后台工作人员负责所有的扫描和分析。

我的问题是这似乎一团糟,尤其是 State 财产,并且变得无法维护。此外,我的应用程序逻辑代码和 UI 代码在同一个文件中,并且在某种程度上混合在一起,这似乎是错误的,这意味着我需要进行大量滚动才能找到我需要的内容。

您如何构建您的 .net 表单?

谢谢

4

8 回答 8

5

有许多模式可以帮助您在应用程序中分离逻辑,从而产生更清洁和更可维护的代码。MVP 模式是一个很好的开始。它基于定义 3 个责任领域,即 MVP M = 模型、V = 视图、P = 演示者。如果您熟悉使用接口就可以了,否则这将是一个很好的起点(查看基本的 OO 原则:封装、抽象、多态)。MVP 的基本原理是将你的应用程序逻辑放在 Presenter 中。演示者通过界面与视图(您的表单)对话,当用户与之交互时,视图会回调演示者(我也为此使用界面)。模型是解决方案的领域对象层次结构,它实现了业务逻辑和实体关系。

大多数 UI 模式(MVP、MCV 等)都在尝试做同样的事情,将您的关注点分开。下面是一个简单的例子:

//视图界面

interface IUserDetailsView
{

      string Username{set;get;}
      string FirstName{get;set;}
      string LastName{get;set;}
      UserDetailsPresenter Presenter{get;set;}
      void DisplayMessage(string message);


}

//视图实现 //具有文本框、标签、组合等的标准窗体

class UserDetailsView : Form, IUserDetails
{

      public string Username{set{txtUserName.text = value;}get{return txtUserName.text;}}
      public string FirstName{set{txtFirstName.text = value;}get{return txtFirstName.text;}}
      public string LastName{set{txtLastName.text = value;}get{return txtLastName.text;}}

      Public UserDetailsPresenter Presenter{get;set;}

      public void DisplayMaessage(string message)
      {
         MessageBox.Show(message);
      }

      private void saveButton_Click(object sender, EventArgs e)
      {
         Presenter.SaveUserDetails();

      }
}

//表示逻辑

类演示者 UserDetailsPresenter {

  //Constructor
  public userDetailsPresenter(IUserDetailsView view)
  {
    //Hold a reference to the view interface and set the view's presnter
     _view = view;
     _view.Presenter = this;
  }

  private IUserDetailsView _view;

  DisplayUser(string userName)
  {
     //Get the user from some service ...
     UserDetails details = service.GetUser(userName);

     //Display the data vioa the interface
     _view.UserName = details.UserName;
     _view.FirstName = details.FirstName;
     _view.LastName = details.LastName;

  }

  public void SaveUserDetails()
  {

       //Get the user dryaiols from the view (i.e. the screen
       UserDetails details = new UserDetails();

       details.UserName = _view.UserName;
       details.FirstName = _view.FirstName;
       details.LastName = _view.LastName;

       //Apply some business logic here (via the model)
       if(!details.IsValidUserDetails())
       {
          _view.DisplayMessage("Some detail outlining the issues");
         return;
       }

       //Call out to some service to save the data
       service.UpdateUser(details);

  }

}

//最后是模型

public class UserDetails
{

   public UserName {get;set;}
   public FirstName{get;set;}
   public LastName{get;set;}

   public bool IsValidUserDetails()
   {
       if(LastName == "Smith")
       {
          //We do not allow smiths, remember what happened last time ... or whatever
          return false;
       }

       return true;
   }

}

希望这能解释责任是如何分开的。该表单除了显示/格式化等之外没有任何逻辑,它也可以被存根用于测试。演示者是视图和模型之间的中介并调用服务,模型实现您的业务逻辑。正如已经建议的那样,这种模式有一些变化,这可以使您的代码更苗条和更灵活,但这概述了基本原则。我希望这有帮助。

:-)

于 2010-06-16T10:47:39.250 回答
2

对于复杂的表单,我通常将代码拆分为单独的文件。您可以使用“部分课程”来做到这一点。每个源代码文件都根据格式命名。例如 MainForm.cs、MainForm.State.cs、MainForm.Update.cs、MainForm.Menu.cs 等等。如果我有许多复杂的表格,我将为每个表格创建一个子文件夹。这里的一个提示是创建一个 MainForm.Wip.cs 表单。这个部分类表单是您当前正在处理的代码。完成此代码后,您可以重命名它或将代码移动到其他源代码文件。

此外,我还将创建用户控件。这具有代码重用的好处,并且将许多功能移出表单。查看http://msdn.microsoft.com/en-us/library/6hws6h2t.aspx上的“使用 .NET Framework 开发自定义 Windows 窗体控件” 。

http://www.codinghorror.com/blog/2007/12/nobody-cares-what-your-code-looks-like.html查看没人关心你的代码是什么样子。在“组织”之前要考虑的事情。

于 2010-06-16T09:55:52.613 回答
1

看看 Model-View-Presenter 模式:http ://en.wikipedia.org/wiki/Model_View_Presenter

使用这种模式,表单的代码隐藏应该主要包含对演示者的简单级联调用,这反过来会改变模型,将事件级联回视图(有时通过演示者,具体取决于您的实现)。

要点是:您的表单(视图)不应包含任何状态信息;这将在演示者中,它不应该关心从哪里获取数据,只要数据符合指定的合同。这提高了可测试性,因为您可以轻松地在演示器上测试您的状态和数据,并解耦视图,允许 PLAF、相同数据的不同表示和类似数据。

祝你好运 :)

于 2010-06-16T09:53:19.127 回答
1

一些快速的建议:

尝试将所有非 UI 代码移出表单,如果可能,您只希望在实际表单中包含 GUI 代码。如果一个属性有副作用,它可能应该是一个函数。您的 State 属性几乎可以肯定是一个方法,看看您是否可以将代码分解为单独的方法,因此它只是每个状态的一个函数调用。

于 2010-06-16T09:55:17.523 回答
0

我使用区域,如下所示:

#Region "_Edit"
    Private Sub _edit_VisibleChanged(...) Handles _edit.VisibleChanged
    End Sub
#End Region

从上到下,我的表单代码有:

  • 私人声明
  • 朋友属性
  • 朋友潜艇
  • 私有财产
  • 私人潜艇
  • 事件
  • 私有表单/类的事件处理程序

听起来您的 State 属性需要分解,或者可能将代码移到其他类或例程中,以便更加隐藏复杂性。

于 2010-09-30T15:39:26.677 回答
0

这是一个经常使用的架构模式的链接。

http://en.wikipedia.org/wiki/Model_View_ViewModel

我也会查找一些其他架构模式,并对此进行更多研究,查找一些示例代码等。

于 2010-06-16T09:52:36.647 回答
0

您应该首先分析您的代码,以区分什么是应用程序逻辑和什么是 UI 逻辑。这两个都不应该在同一个文件中。您的状态属性绝对不是 UI 逻辑,因此请先将其移出您的表单。这将帮助您清除表单代码。

其次,阅读一些设计模式和原则。你可以在这里找到一些很好的例子,在你的情况下,我会检查行为模式,更具体地说是状态和调解者模式。它们不是解决问题的灵丹妙药,但它应该让您更好地了解如何拆分应用程序和 UI 逻辑。

于 2010-06-16T10:09:36.373 回答
0

我倾向于在用户控件或自定义控件中放置尽可能多的代码。组件更易于重用,表单代码更易于阅读。

用户控件还可以处理和公开事件,这可以使动态部分更容易与表单代码分离。

您甚至可以制作在窗体上看不到的自定义控件,例如计时器。

于 2010-06-16T10:15:52.673 回答