在.NET表单中组织UI代码

时间:2010-06-16 09:44:44

标签: .net winforms code-organization

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

前段时间,我开始使用C#开发GUI程序来控制传感器,项目蓬勃发展。我只是想知道如何在我的表单中最好地组织代码,特别是UI代码。

我的表格目前是乱七八糟的,或者至少对我来说是一团糟。

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

我的问题似乎是混乱,特别是国家财产,并且变得无法维护。此外,我的应用程序逻辑代码和UI代码在同一个文件中,在某种程度上,混合似乎是错误的,这意味着我需要做很多滚动才能找到我需要的东西。

如何构建.net表单?

由于

8 个答案:

答案 0 :(得分:5)

有许多模式可以帮助您在应用程序中分离逻辑,从而使代码更清晰,更易于维护。 MVP模式是一个很好的开始。它基于定义3个响应区域,即MVP M =模型,V =视图,P =演示者。如果你熟悉使用接口你会没事的,否则这将是一个很好的起点(回顾基本的OO原则:封装,抽象,多态)。 MVP的基本原理是将应用程序逻辑放在Presenter中。 prenter通过一个接口与视图(你的表单)对话,当用户与它交互时,视图回调给演示者(我也使用这个接口)。该模型是解决方案的域对象层次结构,它包含商业逻辑和实体关系。

大多数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();

      }
}

//演示逻辑

class Presenter 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;
   }

}

希望这能解释责任是如何分离的。除了显示/格式化之外,表单没有逻辑,它也可以被删除以进行测试。演示者是视图和模型之间的中介,并调用服务,模型包含您的业务逻辑。正如已经提出的那样,这种模式存在差异,这可以使您的代码更轻薄,更灵活,但这概述了基本原则。我希望这会有所帮助。

: - )

答案 1 :(得分:2)

对于复杂的表单,我通常将代码拆分为单独的文件。你可以使用“partial class”来做到这一点。每个源代码文件都基于表单命名。例如,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查看没有人关心你的代码是什么样的。在“组织”之前要考虑的事情。

答案 2 :(得分:1)

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

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

指出:您的表单(视图)不应包含任何状态信息;这将在演示者中,并且它不应该关心从何处获取数据,只要数据符合指定的合同即可。这样可以提高可测试性,因为您可以轻松地在演示者上测试状态和数据,并将视图分离,从而允许PLAF,相同数据的不同呈现以及相似的数据。

祝你好运:)

答案 3 :(得分:1)

一些快速建议:

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

您应首先分析您的代码,以划分什么是应用程序逻辑和什么是UI逻辑。这些都不应该在同一个文件中。你的状态绝对绝不是UI逻辑,所以首先将它移出你的形式。这有助于您清除表单代码。

其次,阅读一些设计模式和原则。您可以找到一些很好的例子here,在您的情况下,我会查看行为模式,更具体地说是State和Mediator模式。它们不是解决问题的银子弹,但它可以让您更好地了解如何拆分应用程序和UI逻辑。

答案 6 :(得分:0)

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

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

您甚至可以像在计时器上那样在表单上看不到自定义控件。

答案 7 :(得分:0)

我使用区域,如下:

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

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

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

听起来你的State属性需要被拆分,或者可能将代码移到其他类或例程中,因此复杂性更加隐藏。