在父窗体中调用方法和在 C# 中访问父窗体中的 GUI 元素的最佳做法

本文关键字:窗体 元素 GUI 最佳 调用 方法 访问 | 更新日期: 2023-09-27 17:56:26

我正在开发一个win表单应用程序,我发现自己不断需要从另一个类访问父表单中的方法,例如Form1,无论是表单类还是只是一个类。我在表单 1 的构造函数中有一些初始值设定项,因此我无法创建 Form1 的实例。所以我无法访问 Form1 的方法。

所以我觉得这是一种不好的做法。但是,在某些情况下,我不知道还能做什么,例如考虑这种情况。我有一个名为ProcessData的类,其中我有一个接收文件,逐行读取并处理数据的方法。现在,我将此方法作为主窗体 Form1 中的线程调用。我的要求是作为数据处理,我想在主窗体 Form1 的多行文本框中显示当前正在处理的行。

以前我所做的是,我将所有内容都放在同一个 Form1 中,所以我使用了一个委托,例如

delegate void SetTextCallback(string text, Control ctrl);
private void SetText(string text, Control ctrl)
    {
        if (ctrl.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text, ctrl });
        }
        else
        {
            if (ctrl.GetType() == typeof(Label))
            {
                ctrl.Text = text;
            }
            else
            {
                ctrl.Text += Environment.NewLine + text;
            }
        }
    }

我像 SetText("text",Label1) 一样调用了它;

但是如果我从另一个类调用它来引用 Label1,我将需要一个 Form1 的实例,但我将无法创建它,那么这样做的最佳实践是什么?

(我知道我可以将文本传递给 SetText 并在那里处理控件,但我将对从不同类调用的各种文本框和标签控件使用相同的东西)

在父窗体中调用方法和在 C# 中访问父窗体中的 GUI 元素的最佳做法

我通常这样做的方法是让子窗体公开与该窗体上的逻辑操作和事件对应的事件,例如:

/// <summary>
/// Occurrs when an item is selected in the form
/// </summary>
public event EventHandler<ItemSelectedEventArgs> ItemSelected;
/// <summary>
/// Fires the <see cref="ItemSelected" /> event
/// </summary>
protected void OnItemSelected(MyItem item) 
{
    var handler = this.ItemSelected;
    if (handler != null)
    {
        ItemSelectedEventArgs args = new ItemSelectedEventArgs();
        args.Item = item; // For example
        handler(this, args);
    }
}

这个想法是,父表单的逻辑应该响应子表单上的操作,而不是子窗体上的操作驱动父窗体上的操作 - 您应该尝试尽可能多地封装表单逻辑(也称为关注点分离)。

同样作为一种模式,它应该是父/调用表单,它通过InvokeRequired等处理将调用编送到正确的线程......而不是子窗体 - 除非您在后台线程上工作,否则无论如何这都是不必要的。

你的类应该引发事件,你的窗体应该有事件处理程序。这会将表单代码保留在表单中,将类代码保留在类中。漂亮整洁。

按照 Kragen 和 Richard 的建议,引发事件将起作用,并阻止父/子关系紧密耦合,但如果你想要一些真正灵活的东西,请查看事件聚合器模式。这里的 Prism 项目中提供了一个不错的,尽管很多人觉得使用起来不方便,并且已经创建了扩展以使其更容易使用,或者从头开始编写自己的扩展,例如。

基本上,这个想法是父级甚至不在乎消息来自其子窗口。子窗口只是将诸如"Open Order #6"之类的消息抛到消息总线上,假设其他人将处理它,这与引发事件几乎相同。在其他地方,负责包含所有子表单的父表单订阅了"打开订单"消息,因此它接收消息,并为订单 #6 打开一个新的子窗口。

由于父级不再直接"焊接"到子级,因此这些消息现在也可能来自其他地方。您可以从父窗体本身上最近使用的列表引发"未结订单"消息,或者响应单击其他窗体中的超链接。要求打开订单#6不再重要,重要的是有人这样做了。这是一个高度灵活的模型,消除了大量的紧密耦合。