是否存在某种用于设计和实现GUI的系统策略?


18

我正在使用Visual Studio在C#中创建GUI应用程序。工具箱是一个漂亮的组件面板,它使我可以轻松地将按钮和其他元素(为清楚起见,每当我表示“控件”,我都会说按钮)拖放到表单上,这使得静态表单非常容易做到。但是,我遇到两个问题:

  • 首先创建按钮需要很多工作。当我使用的表单不​​是静态的时(即,根据用户的操作在运行时创建了按钮或其他控件),我根本无法使用调色板。相反,我必须通过使用我使用的任何方法调用构造函数来手动创建每个按钮,然后通过指定按钮的高度,宽度,位置,标签,事件处理程序等手动进行初始化。这非常繁琐,因为我必须猜测所有这些修饰参数,而又看不到表格的外观,而且每个按钮都会生成许多行重复的代码。
  • 使按钮执行某些操作也是很多工作。在功能齐全的应用程序中处理事件是一个巨大的痛苦。我知道如何执行此操作的唯一方法是选择一个按钮,转到其属性中的“事件”选项卡,单击该OnClick事件,以使其在Form的代码中生成该事件,然后填写该事件的主体。由于我想分离逻辑和表示,因此所有事件处理程序最终都是对适当的业务逻辑功能的单行调用。但是,将它用于许多按钮(例如,想象像MS Word这样的应用程序中存在的按钮数目)会Form用数十种样板事件处理程序方法污染我的代码,并且很难维护。

因此,要让我真正使用任何比Hello World更复杂的GUI程序都是不切实际的。明确地说,在编写具有最小UI的程序中,处理复杂性没有任何问题-我觉得我能够使用具有相当能力的OOP来整齐地构造我的业务逻辑代码。但是在开发GUI时,我陷入了困境。似乎很乏味,以至于我感觉自己正在重新发明轮子,并且那里有一本书解释了如何正确地做GUI,而我还没有读过。

我想念什么吗?还是所有C#开发人员都只是接受无尽的重复事件处理程序和按钮创建代码列表?


作为一个(希望有帮助的)提示,我希望一个好的答案可以谈论:

  • 使用OOP技术(例如工厂模式)简化重复按钮的创建
  • 将许多事件处理程序组合到一个方法中,该方法检查Sender以找出哪个按钮调用了它,并相应地运行
  • XAML并使用WPF代替Windows窗体

你不需要提,当然任何这些。关于我要寻找的答案,这只是我的最佳猜测。


如我所见,是的,Factory是一个很好的模式,但是我看到的是一个更多的Model-View-Controller模式,其中Commands钩接到控件而不是事件上。WPF通常是MVVM,但MVC很有可能。目前,我们在WPF中使用90%的MVC在WPF中拥有一个庞大的软件。所有命令都转发给控制器,他处理一切
弗兰克(Franck)

您是否不能使用GUI编辑器创建具有所有可能按钮的动态表单,并且仅使不需要的内容动态不可见?听起来工作量要少得多。
汉斯·彼得·斯特尔

@hstoerr但是很难使布局生效。许多按钮会重叠。
极好的

Answers:


22

多年来,已经有几种方法可以处理您提到的这些问题,我同意,这是UI框架近年来必须解决的两个主要问题。来自WPF背景,可以通过以下方式进行处理:

声明式设计,而不是命令式

当您刻苦地描述编写代码以实例化控件并设置它们的属性时,您在描述的是UI设计的必要模型。即使使用WinForms设计器,您也只是在该模型上使用包装器-打开Form1.Designer.cs文件,然后看到所有代码。

使用WPF和XAML以及其他框架中的类似模型(当然,从HTML开始),您将需要描述自己的布局,并让该框架承担实现它的繁重工作。您可以使用更智能的控件(例如面板(网格或WrapPanel等))来描述UI元素之间的关系,但是期望您不要手动放置它们。诸如可重复控件(如ASP.NET的Repeater或WPF的ItemsControl)之类的灵活概念可帮助您创建动态缩放的UI,而无需编写重复的代码,从而允许将动态增长的数据实体动态表示为控件。

WPF的DataTemplates允许您再次声明性地定义UI优势的小块,并将它们与您的数据进行匹配;例如,客户数据对象的列表可能绑定到ItemsControl,并根据他是普通员工(使用具有名称和地址的标准网格行模板)还是主要客户来调用不同的数据模板,而不同的模板,带有图片,并显示易于使用的按钮。同样,无需在特定窗口中编写代码,只需更智能的控件即可了解它们的数据上下文,从而使您可以将它们简单地绑定到数据并让它们执行相关的操作。

数据绑定和命令分离

WPF的数据绑定(以及在其他框架中,例如AngularJS)也涉及数据绑定,它允许您通过将控件(例如,文本框)与数据实体(例如,客户的姓名)链接来声明自己的意图。框架处理管道。逻辑处理类似。您可以使用数据绑定机制将控制器的行为(例如,按钮的Command属性)链接到表示活动块的Command对象,而不是将背后的事件处理程序手动连接到基于Controller的业务逻辑。

它允许此命令在Windows之间共享,而无需每次都重写事件处理程序。

更高的抽象水平

与您理所当然感到厌烦的Windows窗体的事件驱动范例相比,这两个针对您两个问题的解决方案都向更高的抽象水平迈进了一步。

这样的想法是,您不想在代码中定义控件具有的每个单个属性,以及从单击按钮开始到之后的每个行为。您希望控件和基础框架为您做更多的工作,并允许您考虑更抽象的数据绑定概念(存在于WinForms中,但远不及WPF有用)和Command模式来定义UI之间的链接和不需要扎根的行为。

MVVM模式是微软的做法,以这种模式。我建议阅读。

这是该模型的外观以及如何为您节省时间和代码行的粗略示例。这不会编译,但是是伪WPF。:)

在应用程序资源中的某个位置,您可以定义数据模板:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

现在,您将主屏幕链接到ViewModel,这是一个公开数据的类。假设有一个客户(简称为List<Customer>)和一个Command对象(同样是类型的简单公共属性)的集合ICommand。该链接允许绑定:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

和用户界面:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

就是这样。ListBox的语法将从ViewModel中获取客户列表,并尝试将其呈现到UI。由于我们前面定义了两个DataTemplates,它将导入相关模板(基于DataType,假设PrimeCustomer继承自Customer),并将其作为ListBox的内容。没有循环,没有通过代码动态生成控件。

同样,Button具有将其行为链接到ICommand实现的预先存在的语法,该ICommand实现大概知道会更新Customers属性-提示数据绑定框架自动再次更新UI。

我当然在这里采取了一些捷径,但这是要点。


5

首先,我推荐该系列文章:http : //codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/-它不能解决详细问题使用按钮代码,但是它为您设计和实现自己的应用程序框架(尤其是GUI应用程序)提供了系统的策略,向您展示了如何将MVC,MVP,MVVM应用于典型的Forms应用程序。

解决有关“处理事件”的问题:如果您有许多具有类似方式工作的表单的按钮,那么您自己在“应用程序框架”中实现事件处理程序分配可能会有所回报。不用使用GUI设计器分配单击事件,而是创建一个命名约定,即如何必须为特定名称的按钮命名“单击”处理程序。例如-用于按钮MyNiftyButton_ClickHandler的按钮MyNiftyButton。然后,编写一个可重复使用的例程(利用反射)来遍历表单的所有按钮,检查表单是否包含相关的单击处理程序并自动将处理程序自动分配给事件,并不难。请参阅此处的示例。

而且,如果您只想对一个按钮使用一个事件处理程序,那也是可能的。由于每个事件处理程序都会传递发送方,并且发送方始终是按下的按钮,因此您可以利用每个按钮的“名称”属性来分派给业务代码。

为了动态创建按钮(或其他控件):您可以与设计者一起在未使用的表单上创建按钮或其他控件,仅用于模板设计。然后,您使用反射(请参见此处的示例)来克隆模板控件,并仅更改位置或大小之类的属性。这可能比在代码中手动初始化两三个属性要简单得多。

我在上面编写此代码时就牢记Winforms(我想就像您一样),但是一般原则应适用于其他具有类似功能的GUI环境,例如WPF。


在WPF中确实可以做到,而且更加容易。您只需填写命令列表(预编码的特定功能),然后在窗口上设置源即可。您创建了一个漂亮的简单可视模板,您需要照顾的只是用所需的命令填充一个集合,而强大的WPF绑定则以可视化的方式照顾其余的工作。
Franck 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.