0

So I have this bit of code that refreshes controls on a form to reflect register states in a connected device. In this code, I use MethodInvoker to perform the process into another thread such as to not lock up the entire UI, kind of an asynchronous refresh. Anyways, in this MethodInvoker body, I first "-= new EventHandler(this._assignedEvent)", disable the control, update one of it's properties based on the device register status, and re-enable the control, and reassign the EventHandler.

I have replicated this several times throughout my current project, and about 90% of the time, this works. However for a about three/four controls I have on a particular form this is what happens:

  1. Remove event for control x
  2. Modify control x
  3. Event for control y is fired, event was never assigned to x, ever.
  4. Event for control x is fired
  5. me = confused

And this only happens to a few select controls and I was wondering if there was some cross thread execution rules that I didn't adhere to or something...

The refresh code in question looks something like below:

if (_prbsRxLockTime.Enabled)
            {
                ParentDevice.Dongle.Read((RxSelect() == 0 ? _registers.PRBS_RX_2 : _registers.PRBS_RX_16).Offset.ToString("X"), ref result, 1);
                bitMask = (ushort)(Bit.B0 | Bit.B1 | Bit.B2 | Bit.B3);

                UpdateControlUsingMethod(new MethodInvoker(
                    () =>
                    {
                        this._prbsRxLockTime.SelectedIndexChanged -= new System.EventHandler(this._prbsRxLock_SelectedIndexChanged);
                        this._prbsRxLockTime.Enabled = false;
                        foreach (object i in _prbsRxLockTime.Items)
                        {
                            KeyValuePair<string, ushort> item = (KeyValuePair<string, ushort>)i;
                            if ((ushort.Parse(result, NumberStyles.HexNumber) & bitMask) == (ushort)(item.Value))
                            {
                                _prbsRxLockTime.SelectedItem = i;
                                break;
                            }
                        }
                        this._prbsRxLockTime.Enabled = true;
                        this._prbsRxLockTime.SelectedIndexChanged += new System.EventHandler(this._prbsRxLock_SelectedIndexChanged);
                    }
                ));
            }

The event that is fired upon modifying _prbsRxLockTime.SelectedItem is this:

private void _prbsRxLOL_SelectedIndexChanged(object sender, EventArgs e)
    {
        ushort comboBoxData = (ushort)(((ComboBox)sender).SelectedValue);
        ushort bitMask = (ushort)(Bit.B0 | Bit.B1 | Bit.B2 | Bit.B3);
        string regAddress = string.Empty;
        string regData = comboBoxData.ToString("X").PadLeft(4, '0');

        switch (((ComboBox)sender).Name)
        {
            case "_prbsRxLOLTime":
                regAddress = (RxSelect() == 0 ? _registers.PRBS_RX_5 : _registers.PRBS_RX_19).Offset.ToString("X");
                break;
            case "_prbsRxLOLThresh":
                regAddress = (RxSelect() == 0 ? _registers.PRBS_RX_6 : _registers.PRBS_RX_20).Offset.ToString("X");
                break;
            default:
                break;
        }

        ParentDevice.Dongle.Modify(regAddress, regData, 1, bitMask);
    }1.4.
James
  • 1

1 Answers1

0

I suggest you to read this Methodinvoker Invoke For a GUI App thread and this Safe,Simple Multithreading in WinForms article for checking if you are using methodinvoker correctly.

It may also be helpful to suggest you an alternative solution if you explain when and by who the first code block is triggered to run.

Edit After Comments

Sorry but it is difficult to suggest anything because of not having all source codes. It looks like an event subscriber left somewhere in your code or having a problem related to events and multi-threading environment like Events and Deadlocks (See Comments Section)

Another suggestion may be an alternative solution such as using a flag rather than controling event subscription which has some side-affects. You can create a thread-safe property which indicates the updating progress that should not handle by the SelectedIndexChanged procedures.

UpdateControlUsingMethod(new MethodInvoker(
    () =>
    {
        //this._prbsRxLockTime.SelectedIndexChanged -= new System.EventHandler(this._prbsRxLock_SelectedIndexChanged);
        this._prbRxLockTimeUpdatingByThread = true;

        //the code has been omitted here

        //this._prbsRxLockTime.SelectedIndexChanged += new System.EventHandler(this._prbsRxLock_SelectedIndexChanged);
        this._prbRxLockTimeUpdatingByThread = false;
    }
));

and then you can check that property in your event handler for not running the standard procedure.

private void _prbsRxLOL_SelectedIndexChanged(object sender, EventArgs e)
{
    if (this._prbRxLockTimeUpdatingByThread)
        return;

    //the code has been omitted here
}

Hope this help.

Community
  • 1
  • 1
orka
  • 1,328
  • 8
  • 13
  • The first code block is triggered by the main thread, which runs a portal application that loads the UI containing this code as an assembly. It is triggered by an event (Refresh button, OnEnter, etc.). The first code block is one of several very similar code blocks that are all contained within a large refresh method. For example, the next few lines in the code look exactly like the first block except with the register address, bitmask, and control name altered. – James Aug 27 '10 at 15:27
  • James, it may be as simple as some typo in method names such as writing prbsRxLOL_SelectedIndexChanged rather than prbsRxLock_SelectedIndexChanged when subscribing to events at the other code blocks. Suggest you to search "prbsRxLOL_SelectedIndexChanged" and ".SelectedIndexChanged += new System.EventHandler(" keywords for improper uses. – orka Aug 27 '10 at 16:53
  • I considered that first and checked for misusage of the events but couldn't find any references that weren't supposed to be there. – James Aug 27 '10 at 20:34