使用Windows窗体从主窗口更新无模式对话框

时间:2012-07-20 16:47:46

标签: c# .net winforms

我正在研究C#/ .NET 3.5 / Windows Forms程序。我想创建一个无模式对话框,然后使用从主窗体生成的信息更新它。我已经找到了另一个方向的几个例子 - 来自对话框的信息更新了主窗口,但是我在反方向上遇到了麻烦。对话框中的GUI无法正常运行。鼠标光标是沙漏。我有时会看到更新,但我经常会看到一个坚固的白色框,一次在对话框顶部的其他窗口的残余,或者它冻结。

最终,对话框将显示更复杂的信息,但是现在我只是想复制一个在主窗口上显示为标签的计数器。要启动对话框,我执行以下操作:

    bool secondWindowOpen = false;
    Thread secondWindowThread;
    MyPopupWindow secondWindow;

    delegate void TextBoxDelegate(string message);

    private void buttonPop_Click(object sender, EventArgs e)
    {
        // If the second window is not open, then open it
        if (!secondWindowOpen)
        {
            secondWindowOpen = true;

            secondWindow = new MyPopupWindow();
            secondWindowThread = new Thread(secondWindow.MyMethod);
            secondWindowThread.Start();

        }
        else // Close the second window
        {
            secondWindowOpen = false;
            secondWindow.ShouldStop = true;

            secondWindowThread.Join();                
        }
    }

我使用以下代码更新计数器:

if (secondWindow != null)
{
    secondWindow.CounterText = args.FrameNumber.ToString();
}

用于控制“无模式”对话框的代码如下所示。我承认设置文本字段的代码可能是错误的,但这是我目前工作的最好的事情(调用调用给我带来了麻烦)。

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

    public bool ShouldStop
    {
        get { return shouldStop; }
        set { shouldStop = value; }
    }
    private bool shouldStop = false;

    public void MyMethod()
    {            
        this.Show();
        this.Refresh();

        while (!shouldStop)
        {
            Thread.Sleep(100);
            labelCounter.Text = CounterText;
            Refresh();
        }

        this.Close();
    }

    public string CounterText;
}

我的主要问题如下:当只需要更新自己的GUI元素时,将无模式对话框放入第二个线程的最佳方法是什么?从主窗口/线程更新这些GUI元素的最佳方法是什么?谢谢!

3 个答案:

答案 0 :(得分:1)

表单的工作方式是,如果程序是一个处理消息并将它们传递给要处理的表单的循环,那么核心就是核心。如果您的表单永远不会将控制权返回到您的消息循环,您的程序将无法正常工你试图通过添加更多线程来解决这个问题,但这种方式导致了它自己的痛苦。

您无法在线程中执行此操作,因为应用于表单的所有操作必须在UI线程上完成。 你可以使用BeginInvoke将像Invalidate之类的调用传递回UI线程,但是这真的很麻烦 - 编写在一个UI线程上运行的协作窗口比触发大量线程更好,然后尝试将UI线程推入为他们做事。

要执行此操作,您将创建表单,然后将控制权返回给您的消息处理循环。在将来的某个时间,您将从按钮单击等事件关闭窗口。如果您需要根据任何事件更新窗口,您可以在事件处理程序中调用invalidate然后返回。

答案 1 :(得分:0)

根据Jason的建议,我最终简化了事情并让它真正发挥作用。

首先,当我显示第二个窗口时,不再启动新线程。相反,我会做以下事情:

bool secondWindowOpen = false;
MyPopupWindow secondWindow;

private void buttonPop_Click(object sender, EventArgs e)
{
    // If the second window is not open, then open it
    if (!secondWindowOpen)
    {
        secondWindowOpen = true;
        secondWindow = new MyPopupWindow();
        secondWindow.Show();                
    }
    else // Close the second window
    {
        secondWindowOpen = false;
        secondWindow.Close();
    }
}

然后我将第二个窗口中的代码简化为以下内容:

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

    public string CounterText
    {
        set { labelCounter.Text = value; }
    }
}

答案 2 :(得分:0)

首先,请为我的英语道歉,因为我是法国人。

然后,如果我理解你的问题,你想从第一个表格更新第二个表格的控件。

请使用表格的Tag对象。

  1. 创建一个类来存储要在表单之间传输的数据
  2. 从第一个表单创建第二个表单的实例,如下所示:

    Form2 f = new Form2();
    f.Tag = MyDataObject();

  3. 显示表格

    f.Show() => Modeless wayf.ShowDialog => Modal way

  4. 在Form2的构造函数中,您可以使用Tag对象更新控件:

    public Form2()
    {
    MyDataObject do = (MyDataObject)this.Tag;
    MyLabel.Text = do.LabelText;
    }

  5. 如果您想从Form2到主表单,请执行相同的操作:

    进入Form2

    this.Tag = MyDataObject;
    this.Close();

  6. 进入主表格:

    `MyDataObject do = (MyDataObject)f2.Tag;`
    

    我知道这个话题很老但希望这有帮助