在Windows窗体中访问另一个窗体上的控件的最佳方法?
首先,这是一个有关使用Windows窗体的桌面应用程序的问题,而不是ASP.NET问题。
我需要与其他表单上的控件进行交互。我正在尝试通过使用以下命令来访问控件。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
otherForm.Controls["nameOfControl"].Visible = false;
它不能按我期望的方式工作。我最终从Main抛出了一个异常。但是,如果我将控件设为" public"而不是" private",则可以直接访问它们,因此...
otherForm.nameOfControl.Visible = false;
但这是最好的方法吗?以其他形式将控件"公开"是否被视为"最佳实践"?是否存在"更好"的方式来访问其他表单上的控件?
进一步说明:
这实际上是对我问的另一个问题的一种补充,这是在C#中创建树形视图首选项对话框类型的接口的最佳方法。我得到的答案很好,并且解决了我在保持UI直且易于在运行时和设计时使用的组织方面遇到的许多组织问题。但是,它确实提出了一个容易控制接口其他方面的问题。
基本上,我有一个根表单,可以实例化许多其他表单,这些其他表单都位于该根表单的面板中。因此,例如,这些子窗体之一上的单选按钮可能需要更改主根窗体上的状态条图标的状态。在这种情况下,我需要子窗体与父(根)窗体的状态栏中的控件进行对话。 (我希望这是有道理的,而不是以"谁在第一"的方式。)
解决方案
回答
我个人建议不要执行此操作...如果它正在响应某种操作并且需要更改其外观,则我希望引发一个事件并让其自行解决...
形式之间的这种联系总是让我感到紧张。我总是尽力使用户界面尽可能的简洁和独立。
我希望这有帮助。如果没有,也许我们可以扩展该方案?
回答
我会以父表格的形式处理。我们可以通过事件通知其他表单需要修改自身。
回答
我们可以创建一个控制其可见性的属性,而不是将控件公开。
public bool ControlIsVisible { get { return control.Visible; } set { control.Visible = value; } }
这将为该控件创建一个适当的访问器,该访问器不会公开该控件的整个属性集。
回答
首先当然是行不通的。表单上的控件是私有的,设计上仅对该表单可见。
将其全部公开也不是最好的方法。
如果我想将某些东西暴露给外部世界(这也可能意味着另一种形式),我将为其设置一个公共财产。
public Boolean nameOfControlVisible { get { return this.nameOfControl.Visible; } set { this.nameOfControl.Visible = value; } }
我们可以使用此公共属性来隐藏或者显示控件,或者询问控件当前的可见性属性:
otherForm.nameOfControlVisible = true;
我们也可以公开完整的控件,但是我认为这太多了,我们应该只在当前窗体外部显示我们真正想要使用的属性。
public ControlType nameOfControlP { get { return this.nameOfControl; } set { this.nameOfControl = value; } }
回答
在阅读了其他详细信息之后,我同意robcthegeek的观点:提出一个事件。创建一个自定义EventArgs并通过它传递必要的参数。
回答
孩子表格真的需要表格吗?他们可以改为用户控件吗?这样,它们可以轻松地引发事件以供主表单处理,并且我们可以更好地将其逻辑封装到单个类中(至少从逻辑上讲,它们已经存在于所有类中)。
@拉尔斯:你在这里。这是我在开始的时候就做过的事情,从那以后就不必这样做了,这就是为什么我最初建议引发一个事件,但是我的另一种方法确实会破坏封装的任何外观。
@Rob:是的,听起来不错:)。这个上的0/2 ...
回答
@Lars,在传递Form引用时很好,我自己也看到了。可恶的。没见过他们将它们传递到BLL层!那甚至都没有道理!那会严重影响性能吧?如果在BLL的某处保留了引用,则该表格将保留在内存中,对吗?
你有我的同情! ;)
@Ed,请回复我们对制作Forms UserControls的评论。 Dylan已经指出,根表单实例化了许多子表单,给人以MDI应用程序的印象(我假设用户可能要关闭各种表单)。如果我对这个假设是正确的,我认为最好将它们保留为表格。当然可以纠正:)
回答
我同意为此使用事件。由于我怀疑我们正在构建MDI应用程序(因为我们创建了许多子窗体)并动态创建了窗口,并且可能不知道何时取消订阅事件,因此建议我们看一下弱事件模式。 las,这仅适用于框架3.0和3.5,但是使用弱引用可以轻松实现类似的操作。
但是,如果要基于表单的引用在表单中找到控件,仅查看表单的控件集合是不够的。由于每个控件都有其自己的控件集合,因此我们将必须递归遍历所有控件以找到特定的控件。我们可以使用这两种方法(可以改进)来执行此操作。
public static Control FindControl(Form form, string name) { foreach (Control control in form.Controls) { Control result = FindControl(form, control, name); if (result != null) return result; } return null; } private static Control FindControl(Form form, Control control, string name) { if (control.Name == name) { return control; } foreach (Control subControl in control.Controls) { Control result = FindControl(form, subControl, name); if (result != null) return result; } return null; }
回答
如果要创建更复杂的控件/模块/组件,则只能访问另一个视图的内容。否则,我们应该通过标准的"模型-视图-控制器"体系结构来执行此操作:我们应该将所需控件的启用状态与提供正确信息的某些模型级谓词联系起来。
例如,如果我只想在输入所有必需信息时才启用"保存"按钮,那么我将有一个谓词方法来告知表示该表单的模型对象何时处于可以保存的状态。然后,在我要选择是否启用按钮的情况下,我只会使用该方法的结果。
这样可以使业务逻辑与表示逻辑更加清晰地分离,从而使两者都可以更独立地发展,从而使我们可以轻松地创建一个具有多个后端的前端或者一个前端具有一个后端的多个前端。
编写单元测试和验收测试也将变得非常容易,因为我们可以遵循"信任但验证"模式进行操作:
- 我们可以编写一组以各种方式设置模型对象的测试,并检查"可保存"谓词是否返回适当的结果。
- 我们可以编写另一组检查,以检查"保存"按钮是否以适当的方式连接到"可保存"谓词(无论是哪种框架,在Mac OS X上的Cocoa中,这通常都是通过绑定)。
只要这两组测试都通过了,我们就可以确信用户界面将按我们希望的方式工作。
回答
这看起来像是将表示形式与数据模型分离的主要候选对象。在这种情况下,首选项应存储在一个单独的类中,该类将在特定属性发生更改时触发事件更新(如果属性是离散集,请查看INotifyPropertyChanged;如果它们是更多自由格式的基于文本的键,请查看单个事件)。
在树状视图中,我们将对首选项模型进行更改,然后将触发一个事件。在其他表单中,我们将订阅感兴趣的更改。在用于订阅属性更改的事件处理程序中,我们将使用this.InvokeRequired来查看我们是否在创建UI的正确线程上调用,如果没有,则使用this.BeginInvoke调用所需的方法来更新表单。
回答
你可以
- 在子表单上创建带有所需参数的公共方法,并从父表单中调用它(使用有效的强制转换)
- 在子窗体上创建一个公共属性,并从父窗体访问它(使用有效的强制转换)
- 在子窗体上创建另一个构造函数以设置窗体的初始化参数
- 创建自定义事件和/或者使用(静态)类
如果我们使用的是非模式形式,则最佳做法是#4.