-2

I have a C# main form that opens up C# sub forms in separate tabs. Each tab is simply an instance of the same sub form so the tab code is like:

SubForm sf = new SubForm();
TabPage tp = new TabPage();
tp.Controls.Add(sf);
tabControl.TabPages.Add(tp);

There can be n tabs and subforms. Then each new subform instance has a delegate to handle external event updates, like so:

public partial class SubForm : Form
{
  ... form setup ...      

  internal void DoStuff(value v)
  {
    if (InvokeRequired)
    {
        // Generic Action delegate
        Invoke(new Action<string, string>(DoStuff), value);
        return;
    }

    myLabel.Text = value;
    Show();
  }
}

Click the subscribe button and there's a Geode registration to specific keys in the subform, and the delegate is passed as an event handler:

private void button_Click(object sender, EventArgs e)
{
  new Geode().RegisterMyListener(cache, key, DoStuff);
}

When the Geode key value is updated then the update is handled.

This is working fine for the 1st subform. Then with a 2nd or 3rd subform all the Geode subscriptions to each subform's keys are updating, but all the updates are being handled only by the most recently instantiated subform's delegate. I had not expected that to happen because doesn't each new subform instance have its own stack with a new delegate?

UPDATE: Once a 2nd key is registered with Geode RegisterMyListener like this:

region.GetSubscriptionService().RegisterKeys(s);
region.AttributesMutator.SetCacheListener(new Listener<string, string>(DoStuff)); 

then every event update references the latest DoStuff delegate and never a previous one. So is a Geode listener a static register? I am looking to be able to subscribe to separate keys with many instances of the same Listener. Is that possible? Or am I going to need multiple listeners for multiple subforms?

rupweb
  • 3,052
  • 1
  • 30
  • 57

1 Answers1

0

You can do it with extension method like this:

public static class Extensions
{
    public static void InvokeAction(this Control control, Action action)
    {
        if (control.InvokeRequired)
        {
            control.Invoke(new MethodInvoker(() => { action(); }));
        }
        else
        {
            action();
        }
    }
}

Usage:

public partial class SubForm : Form
{
    public void SetExampleText(string text)
    {
        this.InvokeAction(() => { this.ExampleTextBox.Text = text; })
    }
}
fdafadf
  • 809
  • 5
  • 13
  • 1
    Not only is this an extremely poorly designed way to run code in the UI thread, it's also *exactly what the code in the question is already doing*. – Servy Feb 05 '18 at 15:49
  • It depends. In many situations I think this is acceptable. I thought it was technical question not about patterns. And it's not the same because there is `InvokeRequired` moved out to the extension method and example about `MethodInvoker` escpecially made for that cases. – fdafadf Feb 05 '18 at 15:54
  • So how does creating a method, instead of inlining it, fix the problem that the OP has brought up with their code? Why isn't their solution working, and what does your change to do fix it? – Servy Feb 05 '18 at 16:05
  • @fdafadf thx for comment tho Servy is right... the problem here is that the Geode Listener is, I think `static` and so overwrites subform instances delegates. I may need separate `client caches` for each subform each with their own `Listener` instance. – rupweb Feb 05 '18 at 16:14
  • @Servy I'd like to hear about better ways to run code in UI threads though?! – rupweb Feb 05 '18 at 16:15
  • 1
    @rupweb Well at a *bare* minimum, there's no reason whatsoever to call `InvokeRequired`. You know you're in a non-UI thread when you run the code, just call `Invoke` and provide the code you want to run. That said, there are higher level tools specifically created to avoid requiring manual marshaling to the UI thread, i.e. using Tasks and awaiting them, among other frameworks. – Servy Feb 05 '18 at 16:18
  • @Servy actually on trying it, it's nicer to use `if (InvokeRequired)` to handle the invoking then update the UI thread after. – rupweb Feb 06 '18 at 10:00
  • @rupweb It's pointless. You *know* you're not on the UI thread. There's no reason to check something that you already know to be true. Just invoke directly, at a minimum. – Servy Feb 06 '18 at 14:26
  • @Servy yeah you're right only trouble is the method is then `doStuff(args) { Invoke(args); Paint(args) }` and the `Invoke` on its own becomes an infinite loop. The *nice* bit is to `Invoke` then `Paint` without all the extra invoke encapsulation... – rupweb Feb 06 '18 at 14:29
  • @Servy here's my question though: Doesn't each subform like `new SubForm()` then have it's own delegate? – rupweb Feb 06 '18 at 14:30
  • 1
    @rupweb Making the method recursive like that is a *disadvantage*. It's far better to just call `Invoke` and run the code you want to run in the UI thread, rather than making a far more complex method just so you can do a bunch of work that you don't need to do. – Servy Feb 06 '18 at 14:31
  • @rupweb "Doesn't each subform like new SubForm() then have it's own delegate?" That doesn't even make sense. Forms don't have delegates at all. If you mean UI threads, then they *shouldn't*. They can, but it's poor design to do it. You should most certainly avoid it at all costs. – Servy Feb 06 '18 at 14:32
  • @Servy each SubForm object instance... from `new` – rupweb Feb 06 '18 at 14:33
  • @rupweb Yes new object instances have new object instances. The question still doesn't make sense. Different objects are...different objects. What does that have to do with this question/answer? – Servy Feb 06 '18 at 14:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164620/discussion-between-rupweb-and-servy). – rupweb Feb 06 '18 at 14:34