【问题标题】:Why doesn't my event handler update my Windows Form textbox?为什么我的事件处理程序不更新我的 Windows 窗体文本框?
【发布时间】:2014-02-01 05:27:09
【问题描述】:

谁能解释或说明为什么我的事件处理程序不更新我的 Windows 窗体文本框?我已将事件处理程序放在 UI 线程中以更新我的 GUI 窗口中的文本框。我的 UI 线程 #1 SetOperation 类中的 EventLaunch 方法会启动一个事件。 UI 线程 #1 SetOperation 类 OnChDetDisplay 事件处理程序完成,但窗口窗体文本框未更新为分配的值。我缺少什么将事件和处理程序绑定到更新文本框?

感谢任何人都可以分享的任何帮助,

下面是一些代码:

// Class runs in Thread #2:  Prepares message data for Windows Form GUI display and passes to UI Thread #1
    public class Aag_PrepDisplay
    {
        private Aag_PrepDisplay mAagPrep;

        public Aag_PrepDisplay AagPrep
        {
            get { return mAagPrep; }
            set { mAagPrep = value; }
        }

        // Thread #2: prepares message for Windows Form GUI display in UI Thread #1
        public void PrepareDisplay(/*stuff*/)
        {
            mAagPrep = new Aag_PrepDisplay();
            // does message prep stuff  

            SetOperation setOp1 = new SetOperation(); 
            setOp1.FireEvent(mAagPrep);  // call to UI Thread #1 method to fire event to update GUI; passes object with data
        }
    }


    // UI Thread #1 class is the Windows Form. Displays and updates all textboxes. 
    public partial class SetOperation : Form
    {
        public event Action<object> OnChDet;    // declared delegate object event that passes an object

        public SetOperation()
        {
            InitializeComponent();
            OnChDet += chDetDisplayHandler;     // hooks handler to event
        }

        // Thread #1: accepts object w/data from Thread #2; Fires an event to update GUI Textbox(s)
        private void FireEvent(Aag_PrepDisplay aagPrep)
        {
            OnChDet(aagPrep);
        }

        // UI Thread #1 event handler.
        public void chDetDisplayHandler(object name)
        {
            // **** Problem:  event is triggered and value assigned, but doesn't update the GUI window Textbox ********
            actFreqChan1.Text = "402.5";    // this is only a test to see if event handler will update Textbox
            // Next step:  updateAll(name); // pass the object from Aag_PrepDisplay class  
        }   

        //Thread #1: update GUI Textbox values
        public void updateAll(object name)
        {
            // this is where data from the Thread #2 AagPrep object will assign and update Textbox values
        }
    }

【问题讨论】:

  • 非常难读的代码。 “ChDet”、“Chan”和“Aag”可能是什么意思? AagDisEvt1? 2在哪里?这种困惑隐藏了 this 问题,即在某处创建但实际上不可见的表单对象。因为它的 Show() 方法从未被调用过,或者是在一个不抽水的工作线程上创建的。
  • 对不起。试图显示与目标有关的代码。名称是设备的简写。试图了解为什么事件处理程序在完成后不更新 ActFreqChan1.Text。请参阅下面的 cmets 到 pid。确实会出现 GUI 表单,但事件处理程序完成时文本框未更新。你能解释一下工作线程上的 Show() 吗?我有第二个线程(未显示),它定期将数据传递给第一个线程中的 EventLaunch(~) 方法。尚未用数据实现。目前,我只想在事件完成时更新文本框。我希望这更有意义。感谢您的任何建议。

标签: c# events user-interface


【解决方案1】:

在问题行设置断点,告诉我们你看到了什么。

在事件基础架构中,它可能不会被调用并且问题是向上的。 如果它被调用,则问题出在Text 字段的设置器中。

在这两种情况下,缺陷都不是您认为的。

我会简化代码。可能我错过了一些东西,但我正在尝试一下。

public partial class SetOperation : Form
{
    public event Action<object> OnChDet;

    public SetOperation()
    {
        InitializeComponent();
        OnChDet += chDetDisplayHandler;
    }

    private void chDetDisplayHandler(object name)
    {
        ActFreqChan1.Text = "402.5";
    }
}

然后您可以简单地触发事件:

mySetOperationInstance.OnChDet(myNameObject);

问题是会触发这个事件?这取决于你自己去发现。 您必须将上述行放在某处。

据我所知,您不需要:

  • ChanEventArg;
  • ChDetHandler;
  • Aag_DisplayEvent(这看起来完全错误);
  • EventLaunch() 方法。

你唯一应该关心的是:

  • 拥有event
  • 附加处理程序;
  • 在需要时使用参数调用它。

执行此操作:制作代码的备份副本并尝试简化。

如果没有帮助,抱歉,请恢复到您的备份。否则你做的太多了,在某个地方你已经对事件的确切发送方式迷失了方向。

【讨论】:

  • 感谢您的回复。我在 ActFreqChan1.Text = "402.5" 下放置了一个 Trace Writeline()。该值已分配,但事件处理程序会一直执行到最后并完成。然后代码从它开始的地方返回。它似乎工作正常,除了没有更新。在代码的先前版本中,我的文本框已使用计时器事件处理程序进行了更新。我删除了计时器,因为当随机事件发生时,文本框现在正在更新。你能解释一下可能有缺陷的事件基础设施以及文本字段设置器吗?谢谢
  • 文本字段设置器只有在您手动实现时才需要考虑。从代码中我看不出ActFreqChan1 到底是什么,这就是我提到它的原因。如果您认为这是可能的原因,请发布 setter 代码。我正在添加我的答案,请稍等。
  • 谢谢 pid。 ActFreqChan1 是我的 GUI 窗口中的一个文本框。如果你不介意的话,我明天再回复。
  • 我不介意。我只是用一个文本框和一个按钮对其进行了测试。它完美地工作。一个建议:为您的实例(控件)使用小写名称,否则它们看起来像类名:Textbox actFreqChan1.
  • 我真的很喜欢你的代码。你能看看上面实现的代码吗?事件像以前一样触发,但仍然没有文本框更新。某些文本框属性会被禁用或丢失吗?即应用程序或数据绑定?你的文本框属性是什么?我的 GUI 光标在运行时出现在组合框中。另外,我正在运行 2 个独立的线程。线程 2 将数据传递给线程 1,线程 1 将更新文本框。我不想从线程 1 触发事件,所以我从线程 2 调用线程 1 FireEvent() 并从这个方法触发事件。我错过了什么?非常感谢 pid!
【解决方案2】:

事件处理程序可能会抛出一个Exception,它不会出现在 UI 中并保持潜在状态。以下代码将防止创建控件的线程以外的其他线程抛出异常:

官方参考:MSDN on InvokeRequired

类似问题:Using InvokeRequired vs control.InvokeRequired

更长的解释但真的很好:MSDN tutorial on Thread-Safety in WinForms

将线程安全保护(InvokeRequired 等)包裹在事件处理程序内的此分配周围:

actFreqChan1.Text = "402.5";

我希望这会对你有所帮助。否则你仍然可以回到这里。

【讨论】:

    【解决方案3】:

    谢谢 pid。当事件从线程 1 的 SetOperation() 中触发时,我返回并重新创建了更新文本框的代码。事件处理程序更新了文本框。然后我尝试从线程 2 的 PrepareDisplay() 调用线程 1 方法,并从线程 1 方法触发事件。事件处理程序不会更新文本框。接下来,我将安全线程调用代码添加到 Thread 1 SetOperation 类。文本框不会使用安全线程调用代码进行更新。我把它从 MSDN 教程中拿出来。当我通过它时很难遵循代码流。它在方法之间来回跳跃。看来 InvokeRequired 给出了错误。无论哪种情况,文本框都应更新为 402.5。你有没有看到我放错地方的东西或其他缺失的代码?

    下面是我模拟的整个代码。再次感谢您愿意在其中一些方面对我进行辅导。

    namespace TstTxtBoxUpdate
    {
        static class Program
        {
            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            [STAThread]
            static void Main()
            {
                Aag_PrepDisplay aag_Prep1 = new Aag_PrepDisplay();
    
                Thread AagPrepDisplayThread = new Thread(new ThreadStart(aag_Prep1.PrepareDisplay));
                AagPrepDisplayThread.Start();
    
                while(!AagPrepDisplayThread.IsAlive)
                    ;
                Thread.Sleep(1000);
    
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new SetOperation());
            }
        }
    }
    
    namespace TstTxtBoxUpdate
    {
        // Thread 1: UI
        public partial class SetOperation : Form
        {
            private string text;
            public event Action<object> OnChDet;
    
            delegate void SetTextCallback(string text);
            private Thread demoThread = null;
    
            public SetOperation()
            {
                InitializeComponent();
                OnChDet += chDetDisplayHandler;
            }
    
            public void FireEvent(Aag_PrepDisplay aagPrep)
            {
                OnChDet(mName);
            }
    
            private void chDetDisplayHandler(object name)
            {
                this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
                this.demoThread.Start();
            }
    
            private void ThreadProcSafe()
            {
                this.SetText("402.5");
            }
    
            private void SetText(string text)
            {
                if(this.actFreqChan1.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(SetText);
                    this.Invoke(d, new object[] { text });
                }
                else
                {
                    // TextBox NOT updated when event called from FireEvent() that was called from Thread 2 PrepareDisplay()
                    // TextBox IS updated when event called from Thread 1: SetOperation() or FireEvent()
                    this.actFreqChan1.Text = text;
                }
            }
        }
    }
    
    namespace TstTxtBoxUpdate
    {
        // Thread 2: Data prepare
        public class Aag_PrepDisplay
        {
            #region Fields
    
            private Aag_PrepDisplay mAagPrep;
    
            #endregion Fields
    
            #region Properties
    
            public Aag_PrepDisplay AagPrepDisp;
    
            public Aag_PrepDisplay AagPrep
            {
                get { return mAagPrep; }
                set { mAagPrep = value; }
            }
    
            #endregion Properties
    
            #region Methods
    
            public void PrepareDisplay()
            {
                mAagPrep = new Aag_PrepDisplay();
                SetOperation setOp1 = new SetOperation();
                setOp1.FireEvent(mAagPrep);     // calls Thread 1 method that will fire the event
            }
    
            #endregion Methods
        }
    }  
    

    【讨论】:

    • 肯特 - 如果这是您原来问题的延续,请使用此更新编辑您的问题。如果这对您来说是一个新问题(即使它是同一个项目的一部分),请为此创建一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2015-08-29
    • 2022-01-07
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多