0

Can anyone explain or show why my event handler doesn't update my Windows Form textbox? I have put the event handler in the UI thread to update a textbox in my GUI window. An EventLaunch method in my UI Thread #1 SetOperation class initiates an event. The UI Thread #1 SetOperation class, OnChDetDisplay event handler completes but the Window Form textbox doesn't update to the assigned value. What am I missing to tie the event and handler to updating the textbox?

Thanks for any help anyone can share,

Below is some code:

// 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
        }
    }
Kent
  • 59
  • 1
  • 7
  • Remarkably hard to read code. What could "ChDet", "Chan" and "Aag" possibly mean? AagDisEvt1? Where is 2? This kind of confuzzlement hides a *this* problem, a form object that was created somewhere but isn't actually visible. Because its Show() method was never called or was created on a worker thread that doesn't pump. – Hans Passant Jan 29 '14 at 23:15
  • Sorry. Trying to show code pertaining to objective. Names are shorthand for devices. Trying to understand why event handler doesn't update ActFreqChan1.Text when completed. See comments below to pid. GUI form does appear, but textbox not updated when event handler completes. Can you explain about Show() on worker thread? I have 2nd thread (not shown) that periodically passes data to EventLaunch(~) method in 1st thread. Haven't yet implemented with the data. Currently, I just want the textbox to update when event completes. I hope this makes more sense. Thanks for any suggestions. – Kent Jan 29 '14 at 23:54

3 Answers3

0

Put a breakpoint on the problem line and tell us what you see.

Probably it won't get called and the problem is upwards, in the event infrastructure. If it gets called, the problem is in the Text field's setter.

In both cases, the defect is not where you think it is.

I'd simplify the code. Probably I'm missing something but I'm giving this a try.

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

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

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

You can then fire the event simply with:

mySetOperationInstance.OnChDet(myNameObject);

The question is WHO will fire the event? This is up to you to find out. You'll have to put the above line somewhere.

As far as I can tell, you don't need to have:

  • ChanEventArg;
  • ChDetHandler;
  • Aag_DisplayEvent (this looks completely wrong);
  • EventLaunch() method.

The only thing you should care about is:

  • having an event;
  • attaching a handler;
  • invoking it with parameters when need be.

Do this: make a backup copy of your code and try to simplify.

If it doesn't help I'm sorry, revert to your backup. Otherwise you have done way too much and somewhere you've lost your bearings about how exactly the event is dispatched.

pid
  • 11,472
  • 6
  • 34
  • 63
  • Thanks for your response. I put a Trace Writeline() just under the ActFreqChan1.Text = "402.5." The value is assigned, but the event handler steps right thru to the end and completes. The code then returns from where it began. It appears to work correctly, except no update. In a previous revision of the code, my textboxes were updated with a timer event handler. I removed the timer, because the textboxes are now being updated when a random event occurs. Can you explain some more about the event infrastructure that may be flawed and also the Text field setter? Thanks – Kent Jan 29 '14 at 23:30
  • The text field setter is only a concern if you manually implemented it. From the code I can't see what `ActFreqChan1` really is, that's why I mentioned it. Post the setter code if you think it's a possible cause. I'm adding to my answer, wait a sec. – pid Jan 29 '14 at 23:39
  • Thanks pid. ActFreqChan1 is a textbox in my GUI window. I'll respond more tomorrow if you don't mind. – Kent Jan 29 '14 at 23:57
  • I don't mind. I just tested it with one text box and one button. It works perfectly. One advise: use lower case names for your instances (controls) or they will look like class names: `Textbox actFreqChan1`. – pid Jan 30 '14 at 00:04
  • I really like your code. Could you take a look at the implemented code above? Event fires as before, but still no Textbox update. Could some Textbox properties be disabled or missing? i.e. application or data bindings? What are your Textbox properties? My GUI cursor appears in a combobox during runtime. Also, I'm running 2 independent threads. Thread 2 will pass data to Thread 1 that will update the Textbox(s). I didn't want to fire the event from Thread 1, so I called Thread 1 FireEvent() from Thread 2 and fired the event from this method. Did I miss something? Thanks alot pid! – Kent Jan 30 '14 at 20:06
  • Wait, Kent! You can't change a control from a thread that's not the one creating the control. WinForms is picky about that and you'll have to use an `Invoke` workaround. Probably inside the event and `Exception` is thrown but you won't see it because in event handlers exceptions break the handler itself but not the running application. Below I'll post how to handle these cases in a minute. – pid Jan 30 '14 at 20:16
  • On your other question: I didn't change any properties on the text box or button. I just created a form in VS2010 and dragged a text box and button unto it, copied the code above and fixed some names. But the simple button test is not valid, it has not two threads as yours so it can't break the same way. – pid Jan 30 '14 at 20:25
0

Probably the event handler throws an Exception that doesn't emerge to the UI and remains latent. The following code will prevent other threads than the one creating the control from throwing exceptions:

The official reference: MSDN on InvokeRequired

Similar question: Using InvokeRequired vs control.InvokeRequired

The longer explanation but really good: MSDN tutorial on Thread-Safety in WinForms

Wrap the thread safety protection (InvokeRequired and so on) around this assignment inside the event handler:

actFreqChan1.Text = "402.5";

I hope this will help you out. Otherwise you can still come back here.

Community
  • 1
  • 1
pid
  • 11,472
  • 6
  • 34
  • 63
-1

Thanks pid. I went back and recreated the code to update the Textbox, when the event is fired from within the SetOperation() of Thread 1. The event handler updates the Textbox. I then tried to call a Thread 1 method from PrepareDisplay() of Thread 2 and fire the event from the Thread 1 method. The event handler doesn't update the Textbox. Next, I added the safe thread-call code to Thread 1 SetOperation class. The Textbox doesn't update with the safe thread-call code. I took it right out of the MSDN tutorial. It was hard to follow the code flow when I stepped thru it. It jumped back and forth between methods. It appeared the InvokeRequired gave a false. In either case, the Textbox should be updated to 402.5. Do you see something that I misplaced or other missing code?

Below is the entire code that I simulated. Thanks again for your willingness to tutor me on some of this.

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
    }
}  
Kent
  • 1
  • Kent - if this is a continuation of your original problem, please edit your question with this update. If this is a new problem for you (even if it's part of the same project), please create a new question for this. – Derek Jan 31 '14 at 21:45