48

首先,这是关于使用 Windows 窗体的桌面应用程序的问题,而不是ASP.NET问题。

我需要与其他表单上的控件进行交互。我正在尝试使用例如以下内容来访问控件...

otherForm.Controls["nameOfControl"].Visible = false;

它不像我期望的那样工作。我最终从Main. 但是,如果我制作控件public而不是private,则可以直接访问它们,因此...

otherForm.nameOfControl.Visible = false;

但这是最好的方法吗?在其他形式上进行控制是否public被视为“最佳实践”?是否有“更好”的方式来访问另一个表单上的控件?

进一步说明:

这实际上是对我提出的另一个问题的一种跟进,在 C# 中创建“树视图首选项对话框”类型的界面的最佳方法是什么?. 我得到的答案非常好,解决了我在保持 UI 简洁和易于在运行时和设计时使用方面遇到的许多组织问题。然而,它确实提出了容易控制界面其他方面的一个琐碎问题。

基本上,我有一个根表单,它实例化了位于根表单面板中的许多其他表单。因此,例如,其中一个子表单上的单选按钮可能需要更改主根表单上状态条图标的状态。在这种情况下,我需要子表单与父(根)表单的状态条中的控件对话。(我希望这是有道理的,而不是以“谁先上”的方式。)

4

17 回答 17

38

您可以创建一个控制其可见性的属性,而不是将控件公开:

public bool ControlIsVisible
{
     get { return control.Visible; }
     set { control.Visible = value; }
}

这将为该控件创建一个适当的访问器,该访问器不会公开该控件的整个属性集。

于 2008-08-12T07:41:07.093 回答
20

我个人建议要这样做......如果它正在响应某种行动并且需要改变它的外观,我宁愿提出一个事件并让它自行解决......

这种形式之间的耦合总是让我感到紧张。我总是尽量保持 UI 尽可能轻巧和独立

我希望这有帮助。如果没有,也许您可​​以扩展该场景?

于 2008-08-12T07:35:33.000 回答
8

第一个当然是行不通的。窗体上的控件是私有的,设计时仅对该窗体可见。

将其全部公开也不是最好的方法。

如果我想将某些东西暴露给外部世界(这也可能意味着另一种形式),我会为它创建一个公共属性。

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; }
}
于 2008-08-12T07:42:57.603 回答
5

在阅读了额外的细节之后,我同意robcthegeek:引发一个事件。创建一个自定义 EventArgs 并通过它传递必要的参数。

于 2008-08-12T07:48:18.200 回答
3

假设您有两个表单,并且您想通过另一个表单隐藏一个表单的属性:

form1 ob = new form1();
ob.Show(this);
this.Enabled= false;

当您想通过 form2 按钮将焦点移回 form1 时:

Form1 ob = new Form1();
ob.Visible = true;
this.Close();
于 2012-01-31T12:14:46.697 回答
2

我会在父表单中处理这个。您可以通过事件通知另一个表单它需要修改自己。

于 2008-08-12T07:40:55.690 回答
2
  1. 使用事件处理程序通知其他表单来处理它。
  2. 在子窗体上创建一个公共属性并从父窗体访问它(使用有效的强制转换)。
  3. 在子窗体上创建另一个构造函数,用于设置窗体的初始化参数
  4. 创建自定义事件和/或使用(静态)类。

如果您使用非模态表单,最佳做法是#4。

于 2011-06-22T21:36:59.060 回答
1

我同意为此使用事件。由于我怀疑您正在构建 MDI 应用程序(因为您创建了许多子窗体)并动态创建窗口并且可能不知道何时取消订阅事件,所以我建议您查看Weak Event Patterns。唉,这仅适用于框架 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;
}
于 2008-08-12T14:36:54.180 回答
1

你可以

  1. 在子表单上创建一个带有所需参数的公共方法并从父表单调用它(使用有效的强制转换)
  2. 在子表单上创建一个公共属性并从父表单访问它(使用有效的演员表)
  3. 在子窗体上创建另一个构造函数,用于设置窗体的初始化参数
  4. 创建自定义事件和/或使用(静态)类

如果您使用非模态表单,最佳实践将是 #4。

于 2009-04-14T10:06:36.053 回答
1

使用属性(突出显示),我可以获得 MainForm 类的实例。但这是一个好习惯吗?你有什么建议吗?

为此,我使用在 OnLoad 方法上运行的属性 MainFormInstance。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LightInfocon.Data.LightBaseProvider;
using System.Configuration;

namespace SINJRectifier
{

    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            UserInterface userInterfaceObj = new UserInterface();
            this.chklbBasesList.Items.AddRange(userInterfaceObj.ExtentsList(this.chklbBasesList));
            MainFormInstance.MainFormInstanceSet = this; //Here I get the instance
        }

        private void btnBegin_Click(object sender, EventArgs e)
        {
            Maestro.ConductSymphony();
            ErrorHandling.SetExcecutionIsAllow();
        }
    }

    static class MainFormInstance  //Here I get the instance
    {
        private static MainForm mainFormInstance;

        public static MainForm MainFormInstanceSet { set { mainFormInstance = value; } }

        public static MainForm MainFormInstanceGet { get { return mainFormInstance; } }
    }
}
于 2011-05-13T19:30:06.157 回答
0

您的子表单真的需要是表单吗?它们可以成为用户控件吗?这样,它们可以轻松地引发事件以供主窗体处理,并且您可以更好地将它们的逻辑封装到单个类中(至少,从逻辑上讲,它们毕竟已经是类了)。

@Lars:你就在这里。这是我一开始就做的事情,从那以后就不必这样做了,这就是为什么我首先建议提出一个事件,但我的其他方法真的会破坏任何封装的外观。

@Rob:是的,听起来不错:)。0/2 在这个...

于 2008-08-12T07:55:55.207 回答
0

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:

  1. 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.

  2. 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).

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.

于 2008-08-17T01:23:36.650 回答
0

@Lars,很好地呼吁传递表单引用,我自己也看到了。讨厌。从未见过他们将它们传递到 BLL 层!这甚至没有意义!这可能会严重影响性能,对吗?如果在 BLL 中的某个地方保留了引用,那么该表单将保留在内存中,对吗?

你有我的同情!;)


@Ed,重新评论您关于制作表单用户控件的评论。Dylan 已经指出,根窗体实例化了许多子窗体,给人一种 MDI 应用程序的印象(我假设用户可能想要关闭各种窗体)。如果我在这个假设中是正确的,我认为最好将它们保存为表格。当然可以更正:)

于 2008-08-12T07:57:25.453 回答
0

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).

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.

于 2008-09-07T18:20:44.037 回答
0

步骤1:

string regno, exm, brd, cleg, strm, mrks, inyear;

protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
    string url;
    regno = GridView1.Rows[e.NewEditIndex].Cells[1].Text;
    exm = GridView1.Rows[e.NewEditIndex].Cells[2].Text;
    brd = GridView1.Rows[e.NewEditIndex].Cells[3].Text;
    cleg = GridView1.Rows[e.NewEditIndex].Cells[4].Text;
    strm = GridView1.Rows[e.NewEditIndex].Cells[5].Text;
    mrks = GridView1.Rows[e.NewEditIndex].Cells[6].Text;
    inyear = GridView1.Rows[e.NewEditIndex].Cells[7].Text;

    url = "academicinfo.aspx?regno=" + regno + ", " + exm + ", " + brd + ", " +
          cleg + ", " + strm + ", " + mrks + ", " + inyear;
    Response.Redirect(url);
}

第2步:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        string prm_string = Convert.ToString(Request.QueryString["regno"]);

        if (prm_string != null)
        {
            string[] words = prm_string.Split(',');
            txt_regno.Text = words[0];
            txt_board.Text = words[2];
            txt_college.Text = words[3];
        }
    }
}
于 2011-06-29T08:24:32.630 回答
0

将修饰符从公共更改为内部。.Net 故意使用私有修饰符而不是公共修饰符,以防止任何非法访问您的项目之外的方法/属性/控件。事实上,公共修饰符可以在任何地方访问,所以它们真的很危险。您项目中的任何主体都可以访问您的方法/属性。但是在内部修饰符中,没有任何主体(您当前项目的其他主体)可以访问您的方法/属性。

假设您正在创建一个项目,其中包含一些秘密字段。因此,如果可以从您的项目中访问这些字段,则可能很危险,并且与您最初的想法背道而驰。作为一个很好的建议,我可以说总是使用内部修饰符而不是公共修饰符。

但是有些奇怪!

我还必须在 VB.Net 中说明,虽然我们的方法/属性仍然是私有的,但可以通过将表单作为变量调用而从其他表单/类访问它,没有任何问题。

我不知道为什么这种编程语言的行为与 C# 不同。正如我们所知,两者都使用相同的平台,并且声称它们几乎是相同的后端平台,但正如您所见,它们的行为仍然不同。

但我已经用两种方法解决了这个问题。任何一个; 通过使用接口(这不是推荐的,如你所知,接口通常需要公共修饰符,并且不推荐使用公共修饰符(正如我在上面告诉你的那样)),

或者

在某个静态类和静态变量中声明您的整个表单,并且仍然有内部修饰符。然后,当您想使用该表单向用户显示时,请将新Form()构造传递给该静态类/变量。现在它可以随心所欲地在任何地方访问。但是你还需要更多的东西。您也可以在 Designer File of Form 中声明元素内部修饰符。当您的表单打开时,它可以在任何地方访问。它可以很好地为您工作。

考虑这个例子。

假设您要访问表单的 TextBox。

所以第一项工作是在静态类中声明一个静态变量(静态的原因是易于访问,而无需在未来使用任何新的密钥)。

其次转到该表单的设计器类,该类应该由其他表单访问。将其 TextBox 修饰符声明从私有更改为内部。不用担心; .Net 在您更改后永远不会再将其更改为私有修饰符。

第三,当您想调用该表单打开时,请将新的表单构造传递给该静态变量-->>静态类。

第四; 在 From 打开时,您可以从任何其他表单(项目中的任何位置)访问该表单/控件。

看下面的代码(我们有三个对象。1-一个静态类(在我们的例子中我们命名它A

2 - 任何其他想要打开最终表单的表单(在我们的示例中具有 TextBox FormB)。

3 - 我们需要打开的真实表单,我们假设可以访问它的内部TextBox1(在我们的示例中FormC)。

看看下面的代码:

internal static class A
{
    internal static FormC FrmC;
}

FormB ...
{
    '(...)
    A.FrmC = new FormC();
    '(...)
}

FormC (Designer File) . . . 
{
     internal System.Windows.Forms.TextBox TextBox1;
}

您可以随时随地访问该静态变量(此处FormC)及其内部控制(此处Textbox1),而 whileFormC是开放的。


任何评论/想法让我知道。我很高兴收到您或其他任何人关于这个话题的更多信息。老实说,过去我对这个提到的问题有一些问题。最好的方法是第二种解决方案,我希望它对你有用。让我知道任何新的想法/建议。

于 2016-08-16T22:12:04.587 回答
0
public void Enable_Usercontrol1()
{
    UserControl1 usercontrol1 = new UserControl1();
    usercontrol1.Enabled = true;
} 
/*
    Put this Anywhere in your Form and Call it by Enable_Usercontrol1();
    Also, Make sure the Usercontrol1 Modifiers is Set to Protected Internal
*/
于 2017-05-24T20:29:07.457 回答