C# 在 Windows 窗体中访问另一个窗体上的控件的最佳方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to access a control on another form in Windows Forms?
提问by Dylan Bennett
First off, this is a question about a desktop application using Windows Forms, not an ASP.NETquestion.
首先,这是一个关于使用 Windows 窗体的桌面应用程序的问题,而不是一个ASP.NET问题。
I need to interact with controls on other forms. I am trying to access the controls by using, for example, the following...
我需要与其他表单上的控件进行交互。我正在尝试使用以下方法访问控件...
otherForm.Controls["nameOfControl"].Visible = false;
It doesn't work the way I would expect. I end up with an exception thrown from Main
. However, if I make the controls public
instead of private
, I can then access them directly, as so...
它不像我期望的那样工作。我最终从Main
. 但是,如果我使用控件public
而不是private
,则可以直接访问它们,因此...
otherForm.nameOfControl.Visible = false;
But is that the best way to do it? Is making the controls public
on the other form considered "best practice"? Is there a "better" way to access controls on another form?
但这是最好的方法吗?public
在另一种形式上进行控制是否被视为“最佳实践”?是否有“更好”的方式来访问另一种表单上的控件?
Further Explanation:
进一步说明:
This is actually a sort of follow-up to another question I asked, Best method for creating a “tree-view preferences dialog” type of interface in C#?. The answer I got was great and solved many, many organizational problems I was having in terms of keeping the UI straight and easy to work with both in run-time and design-time. However, it did bring up this one niggling issue of easily controlling other aspects of the interface.
这实际上是我问的另一个问题的后续,在 C# 中创建“树视图首选项对话框”类型界面的最佳方法?. 我得到的答案很好,解决了我在保持 UI 简洁和易于在运行时和设计时使用的许多组织问题。然而,它确实带来了轻松控制界面其他方面的一个小问题。
Basically, I have a root form that instantiates a lot of other forms that sit in a panel on the root form. So, for instance, a radio button on one of those sub-forms might need to alter the state of a status strip icon on the main, root form. In that case, I need the sub-form to talk to the control in the status strip of the parent (root) form. (I hope that makes sense, not in a "who's on first" kind of way.)
基本上,我有一个根表单,它实例化了许多其他表单,这些表单位于根表单的面板中。因此,例如,这些子表单之一上的单选按钮可能需要更改主根表单上的状态条图标的状态。在这种情况下,我需要子窗体与父(根)窗体的状态条中的控件对话。(我希望这是有道理的,而不是以“谁在先”的方式。)
回答by Rob Cooper
I personally would recommend NOTdoing it... If it's responding to some sort of action and it needs to change its appearance, I would prefer raising an event and letting it sort itself out...
我个人建议不要这样做......如果它响应某种动作并且需要改变它的外观,我宁愿引发一个事件并让它自行解决......
This kind of coupling between forms always makes me nervous. I always try to keep the UI as light and independentas possible..
这种形式之间的耦合总是让我紧张。我总是尽量保持 UI 尽可能简洁和独立。
I hope this helps. Perhaps you could expand on the scenario if not?
我希望这有帮助。如果没有,也许你可以扩展场景?
回答by Ed S.
I would handle this in the parent form. You can notify the other form that it needs to modify itself through an event.
我会在父表单中处理这个。您可以通过事件通知另一个表单它需要修改自己。
回答by Jon Limjap
Instead of making the control public, you can create a property that controls its visibility:
您可以创建一个属性来控制其可见性,而不是将控件设为公开:
public bool ControlIsVisible
{
get { return control.Visible; }
set { control.Visible = value; }
}
This creates a proper accessor to that control that won't expose the control's whole set of properties.
这会为该控件创建一个适当的访问器,该访问器不会公开控件的整个属性集。
回答by Biri
The first is not working of course. The controls on a form are private, visible only for that form by design.
第一个当然不起作用。表单上的控件是私有的,仅针对该表单设计可见。
To make it all public is also not the best way.
让它全部公开也不是最好的方法。
If I would like to expose something to the outer world (which also can mean an another form), I make a public property for it.
如果我想将某些东西暴露给外部世界(这也可能意味着另一种形式),我会为它创建一个公共属性。
public Boolean nameOfControlVisible
{
get { return this.nameOfControl.Visible; }
set { this.nameOfControl.Visible = value; }
}
You can use this public property to hide or show the control or to ask the control current visibility property:
您可以使用此公共属性来隐藏或显示控件或询问控件当前的可见性属性:
otherForm.nameOfControlVisible = true;
You can also expose full controls, but I think it is too much, you should make visible only the properties you really want to use from outside the current form.
你也可以公开完整的控件,但我认为它太多了,你应该只让你真正想从当前表单外部使用的属性可见。
public ControlType nameOfControlP
{
get { return this.nameOfControl; }
set { this.nameOfControl = value; }
}
回答by Biri
After reading the additional details, I agree with robcthegeek: raise an event. Create a custom EventArgs and pass the neccessary parameters through it.
阅读更多细节后,我同意robcthegeek 的观点:发起一个事件。创建一个自定义 EventArgs 并通过它传递必要的参数。
回答by Ed S.
Do your child forms really need to be Forms? Could they be user controls instead? This way, they could easily raise events for the main form to handle and you could better encapsulate their logic into a single class (at least, logically, they are after all classes already).
您的子表单真的需要成为表单吗?它们可以代替用户控件吗?这样,它们可以轻松引发主窗体处理的事件,您可以更好地将它们的逻辑封装到单个类中(至少,从逻辑上讲,它们毕竟已经是所有类了)。
@Lars: You are right here. This was something I did in my very beginning days and have not had to do it since, that is why I first suggested raising an event, but my other method would really break any semblance of encapsulation.
@Lars:你就在这里。这是我在刚开始时所做的事情,从那时起就不必这样做了,这就是为什么我首先建议引发一个事件,但我的另一种方法确实会打破任何封装的表象。
@Rob: Yup, sounds about right :). 0/2 on this one...
@Rob:是的,听起来不错:)。0/2 在这个...
回答by Rob Cooper
@Lars, good call on the passing around of Form references, seen it as well myself. Nasty. Never seen them passed them down to the BLL layer though! That doesn't even make sense! That could have seriously impacted performance right? If somewhere in the BLL the reference was kept, the form would stay in memory right?
@Lars,很好地呼吁传递表单引用,我自己也看到了。可恶的。不过,从未见过他们将它们传递到 BLL 层!这甚至没有意义!这可能会严重影响性能,对吗?如果在 BLL 中的某个地方保留了引用,表单将保留在内存中,对吗?
You have my sympathy! ;)
你有我的同情!;)
@Ed, RE your comment about making the Forms UserControls. Dylan has already pointed out that the root form instantiates manychild forms, giving the impression of an MDI application (where I am assuming users may want to close various Forms). If I am correct in this assumption, I would think they would be best kept as forms. Certainly open to correction though :)
@Ed,请回复您关于制作 Forms UserControls 的评论。Dylan 已经指出根表单实例化了许多子表单,给人一种 MDI 应用程序的印象(我假设用户可能想要关闭各种表单)。如果我的这个假设是正确的,我认为最好将它们保留为表格。不过当然可以纠正:)
回答by Patrik Svensson
I agree with using events for this. Since I suspect that you're building an MDI-application (since you create many child forms) and creates windows dynamically and might not know when to unsubscribe from events, I would recommend that you take a look at Weak Event Patterns. Alas, this is only available for framework 3.0 and 3.5 but something similar can be implemented fairly easy with weak references.
我同意为此使用事件。由于我怀疑您正在构建 MDI 应用程序(因为您创建了许多子窗体)并动态创建窗口并且可能不知道何时取消订阅事件,我建议您查看弱事件模式。唉,这仅适用于框架 3.0 和 3.5,但使用弱引用可以很容易地实现类似的东西。
However, if you want to find a control in a form based on the form's reference, it's not enough to simply look at the form's control collection. Since every control have it's own control collection, you will have to recurse through them all to find a specific control. You can do this with these two methods (which can be improved).
但是,如果要根据表单的引用在表单中查找控件,仅查看表单的控件集合是不够的。由于每个控件都有自己的控件集合,因此您必须遍历它们才能找到特定的控件。您可以使用这两种方法(可以改进)来做到这一点。
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;
}
回答by Chris Hanson
You should only ever access one view's contents from another if you're creating more complex controls/modules/components. Otherwise, you should do this through the standard Model-View-Controller architecture: You should connect the enabled state of the controls you care about to some model-level predicate that supplies the right information.
如果您要创建更复杂的控件/模块/组件,您应该只从另一个视图访问一个视图的内容。否则,您应该通过标准的模型-视图-控制器架构来做到这一点:您应该将您关心的控件的启用状态连接到一些提供正确信息的模型级谓词。
For example, if I wanted to enable a Save button only when all required information was entered, I'd have a predicate method that tells when the model objects representing that form are in a state that can be saved. Then in the context where I'm choosing whether to enable the button, I'd just use the result of that method.
例如,如果我想仅在输入所有必需信息时启用“保存”按钮,我将有一个谓词方法,该方法会告诉表示该表单的模型对象何时处于可以保存的状态。然后在我选择是否启用按钮的上下文中,我只使用该方法的结果。
This results in a much cleaner separation of business logic from presentation logic, allowing both of them to evolve more independently — letting you create one front-end with multiple back-ends, or multiple front-ends with a single back-end with ease.
这导致业务逻辑与表示逻辑的分离更加清晰,允许它们更加独立地发展——让您可以轻松地创建一个带有多个后端的前端,或多个带有单个后端的前端。
It will also be much, much easier to write unit and acceptance tests for, because you can follow a "Trust But Verify" pattern in doing so:
编写单元测试和验收测试也会容易得多,因为您可以遵循“信任但验证”模式这样做:
You can write one set of tests that set up your model objects in various ways and check that the "is savable" predicate returns an appropriate result.
You can write a separate set of that check whether your Save button is connected in an appropriate fashion to the "is savable" predicate (whatever that is for your framework, in Cocoa on Mac OS X this would often be through a binding).
您可以编写一组以各种方式设置模型对象的测试,并检查“可保存”谓词是否返回适当的结果。
您可以编写一组单独的检查您的 Save 按钮是否以适当的方式连接到“is savable”谓词(无论您的框架是什么,在 Mac OS X 上的 Cocoa 中,这通常是通过绑定)。
As long as both sets of tests are passing, you can be confident that your user interface will work the way you want it to.
只要两组测试都通过,您就可以确信您的用户界面将按您希望的方式工作。
回答by Garo Yeriazarian
This looks like a prime candidate for separating the presentation from the data model. In this case, your preferences should be stored in a separate class that fires event updates whenever a particular property changes (look into INotifyPropertyChanged if your properties are a discrete set, or into a single event if they are more free-form text-based keys).
这看起来像是将演示文稿与数据模型分离的主要候选者。在这种情况下,您的首选项应存储在一个单独的类中,该类在特定属性更改时触发事件更新(如果您的属性是离散集,请查看 INotifyPropertyChanged,如果它们是更自由形式的基于文本的键,则查看单个事件)。
In your tree view, you'll make the changes to your preferences model, it will then fire an event. In your other forms, you'll subscribe to the changes that you're interested in. In the event handler you use to subscribe to the property changes, you use this.InvokeRequired to see if you are on the right thread to make the UI call, if not, then use this.BeginInvoke to call the desired method to update the form.
在树视图中,您将对首选项模型进行更改,然后它会触发一个事件。在您的其他表单中,您将订阅您感兴趣的更改。在您用于订阅属性更改的事件处理程序中,您使用 this.InvokeRequired 来查看您是否在正确的线程上制作 UI调用,如果没有,则使用 this.BeginInvoke 调用所需的方法来更新表单。