【问题标题】:How do I communicate with a control of a Form from another class?如何与另一个类的表单控件进行通信?
【发布时间】:2014-03-13 16:52:23
【问题描述】:

对 C# 有点陌生,并且正在接近我之外的东西。抱歉太长了。

我在 Visual Studio C# Express 中有一个 Windows 窗体应用程序,使用默认类 VS 生成。我想从声明它的默认 Form1 以外的类中启动和停止 Marquee 样式 progressBar。 这些似乎非常困难,我确信我错过了一些重要的东西。

我的项目有 Visual Studio 自动生成的常用类: Form1.cs、Form1.Designer.cs、Program.cs。 我添加了想要谈论加载栏的 myClass.cs。 我使用设计器将 progressBar1 栏添加到我的表单中,设置 Style:Marquee。

Form1.cs的'Form()构造函数中,我写

this.progressBar1.Visible = false;

这行得通。 Intellisense '看到' progresBar1. Form1.cs 中的代码可以查看和控制 Form1.Designer.cs 中声明的 progressBar1。 这对我来说很有意义。

但是需要启动和停止加载条的函数必须存在于 myClass.cs 中。 我希望能够在 myClass.cs 中编写这样的代码:

public void myFunction(){
    Form1.progressBar1.visible=true
    //do stuff that takes a bit of time
    Form1.progressBar1.visible=false
}

这不起作用。在 myClass.cs 中键入代码时,Intellisense 无法“看到”progresBar1 事实上,智能感知无法从 myClass.cs. 中“看到”Form1.cs 中的任何内容。 任何添加到 Form1 的公共属性或函数都不会被智能感知看到。 这对我来说没有意义,我很困惑。 这似乎是您想要经常轻松地做的事情。

一些搜索表明,这种阻止对表单控件的外部访问是设计使然。与将逻辑代码与 GUI 代码“解耦”有关,这在原则上是有意义的。所以显然有一种预期的方法,但很难找到一个明确的例子。我只能找到完全在声明它们的表单中控制的加载栏的示例,或者关于创建和注册事件或使用调用或其他我知之甚少的东西的简短示例。有许多明显的解决方案,但没有一个我能清楚地看到适用于我,或者我能够实施,在我的无知中。

如果我的表单是一个实例,我想我可以做到。 [编辑] 不。实例与否,Form1 控件永远不会暴露在 Form1.cs

之外

那么,我如何以正确的方式从默认的 Form1 以外的类中启动和停止 Marquee 样式的 progressBar? 在某处是否有明确且有用的示例?

【问题讨论】:

  • 在哪里创建 myClass 的实例? Form1的内部代码?
  • myClass 是一个单独的类,不在 Form1 中。这是一门大课,做很多需要时间的事情。我在解决方案资源管理器中使用了“添加类”来添加它。它与 Program.cs 和所有其他类共享其命名空间。
  • 查看此答案的链接How to Access a Control in Another Form

标签: c# .net winforms visual-studio


【解决方案1】:

您无法通过这种方式访问​​您的属性:

Form1.progressBar1

因为 Form1 是一种类型(不是实例化对象)。您可以使用此方法访问的唯一方法或属性必须标记为static

要回答您关于如何沟通的问题,您可能希望使用您提到的事件方法。首先,您需要在逻辑类中添加一个事件:

public event Action<int> UpdateProgress;

就像函数一样调用:

if (UpdateProgress != null)
   UpdateProgress(10);

这使用 Action 泛型委托声明了一个新事件,这意味着监听函数必须返回 void 并将一个 int 作为参数。

然后在您的表单代码中,您将拥有:

MyClass logic = new MyClass();
private void SomeFunction
{       
    logic.UpdateProgress += UpdateProgressBar;
}

private void UpdateProgressBar(int newProgress)
{
    progressBar1.BeginInvoke(new Action(() =>
    {
       progressBar1.Value = newProgress;
    }));
}

这会创建逻辑类的新实例,并分配函数“UpdateProgressBar”,以便在逻辑类引发 UpdateProgressBar 事件时调用。该函数本身使用 Dispatcher.BeginInvoke,因为您的逻辑类可能不在 UI 线程上运行,您只能从该线程执行 UI 任务。

这里发生了很多事情,所以如果我可以为您澄清任何事情,请告诉我!

【讨论】:

  • 我将从 UpdateProgressBar 方法开始解释这个概念,然后回过头来解释如何将委托传递给 myClass,并以 myClass 方法内部的调用结束,但我认为你说得对。
  • 是的,发生了很多事情,但似乎是一个重要的 OOP 操作原则。我会看看我是否可以让它工作,然后提出问题。
  • '调度员。'在当前上下文中不存在。我应该“使用:”什么命名空间?
  • @LoganBender,抱歉有点 WPF 蠕变,您想使用控件的 BeginInvoke(答案已更新)。您也许可以在表单本身上调用它(只是 BeginInvoke 之前什么都没有),但我相信该控件被认为是更好的做法。
  • 行 'logic.UpdateProgress += UpdateProgressBar;'不起作用,智能感知提醒我:WindowsFormapplication1.Form1.logic 是一个“字段”,但用作“类型”。
【解决方案2】:

我会创建一个具有与您的表单匹配的属性的模型,然后将其传递出去。

所以你会创建一个这样的新课程......

using Windows.Forms;

public class Form1Model {
    public ProgressBar progressBar { get; set; }
}

然后,当您想要访问持有该函数的其他类时,您将创建一个 Form1Model 的实例,填充它,然后调用您的函数

var fm = new Form1Model {
    progressBar = this.progressBar1;
};
otherClass.MyFunction(fm);

现在您必须更改函数以接受新模型

public void MyFunction(Form1Model fm){
    // do stuff
}

另一种选择是让函数获取表单的一个实例,而不是创建模型,但是你将传递很多你可能不会关心的额外位

public void MyFunction(Form1 form){
    // do stuff
}

然后在你的表单上你会像这样调用函数

otherClass.myFunction(this);

我会推荐第一种方式而不是第二种方式,您可以控制要传递的数据

【讨论】:

  • 绝对是第一个超过第二个,除非必须,否则您不希望传递整个表单对象。这种方法的唯一问题是它将“逻辑”类与 UI 类型紧密耦合。
  • 我本可以进入接口之类的,但他说他是 C# 新手,所以我尽量保持简单
【解决方案3】:

您正在尝试访问类型 Form1 而不是表单实例。我将向您展示如何访问下面的实例。

我假设Form1 是应用程序的主窗体,只要应用程序运行,它就会保持打开状态。当您创建 WinForms 应用程序时,VS 在Program.cs 中创建此代码:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

让您的主表单在整个应用程序中都可访问的一种简单方法是通过公共静态属性使其可访问。像这样更改代码

static class Program
{
    public static Form1 MainForm { get; private set; }

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        MainForm = new Form1();
        Application.Run(MainForm);
    }
}

Form1 中创建一个公开进度条可见性的属性:

public bool IsProgressBarVisible
{ 
   get { return this.progressBar1.Visible; }
   set { this.progressBar1.Visible = value; }

}

现在您可以像这样从程序的任何部分显示进度条:

Program.MainForm.IsProgressBarVisible = true;

访问主窗体的另一种方法是,因为它总是作为第一个窗体打开:

((Form1)Application.OpenForms(0)).IsProgressBarVisible = true;

但是,它需要将表单转换为正确的类型,因为OpenForms 返回一个Form


别忘了:Form 只是一个类,就像任何其他类一样。你可以做几乎所有你可以用其他课程做的事情。因此,只要您不使用多线程,与表单通信与与其他对象通信并没有太大区别。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-24
    • 1970-01-01
    • 2019-09-23
    • 2011-06-16
    • 2022-11-20
    • 1970-01-01
    相关资源
    最近更新 更多