Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Redraw of MenuItem in open toolbar
#1
I have an C# program and use the latest nuget package of LibreAutomate 1.5.0 to automate windows using toolbar triggers.
I have several submenus with radiobuttons in toolbars and want to update the choosen item text in the parent toolbar button (toolbarItem.Text = "Update") while the toolbar stays open.

I found in code the following Update function in decompiled toolbar code which should take care of redraw:
   private void _Invalidate(TBItem ti = null) {
    _ThreadTrap();
    if (IsOpen) {
      if (ti != null) {
        Api.InvalidateRect(_w, ti.rect);
      }
      else {
        Api.InvalidateRect(_w);
      }
    }
Unfortunately the redraw happens only if I move the mouse over the button.
What shall I do to redraw the changed button without mouseover?
Has it something to do with threading?
#2
Toolbar button text can be changed after creating the toolbar, but need a workaround.

Code:
Copy      Help
using Au.Triggers;

ActionTriggers Triggers = new();
Triggers.Options.ThreadOfTriggers();
Triggers.Window[TWEvent.VisibleOnce, "LibreAutomate", flags: TWFlags.RunAtStartup] = _Toolbar;
Triggers.RunThread();

void _Toolbar(WindowTriggerArgs ta) {
    toolbar t = new("sgcqjw8d6dw", TBCtor.DontSaveSettings) { ActionThread = false };
    t["Aaaaa"] = o => { };
    TBItem menuButton = null;
    menuButton = t.Menu("Bbbbb", m => {
        m["Change button text"] = o => {
            menuButton.Text = "New longer text";
            t.DisplayText ^= true; t.DisplayText ^= true; //a workaround to remeasure and redraw the toolbar
        };
    });

    t["Cccc"] = o => { };
    t.Show(ta);
}
#3
Thank you for showing the workaround.
My toolbars are running in separate threads and I'm storing the created instances of each toolbar in a static dictionary and access them later by unique names.
If I try to update the button text by calling toolbar.DisplayText ^= true twice I receive an wrong thread exception as follows:
System.InvalidOperationException: Wrong thread.`n   at Au.Types.MTBase._ThreadTrap()
I am already using the option Triggers.Options.ThreadOfTriggers(); to create the toolbar triggers.
Is there any other chance (via windows message?) to invalidate and force redraw of that toolbar from other thread too?
Otherwise I might have to rewrite the business logic either by using the tag-property of toolbar to store the needed information within the same thread as the toolbar itself or by writing a wrapper class.
#4
I thought you want to update button text from the click action of the radio item. Then code like in my example should work. Note the `ActionThread = false`.

Not so easy if from another thread. One way is to use DispatcherSynchronizationContext or WindowsFormsSynchronizationContext. I'll post example code if you need it.
#5
In any case this can be useful.

Code:
Copy      Help
// script "Change toolbar button text2.cs"
using Au.Triggers;
using System.Windows.Threading;

class Program {
    ConcurrentDictionary<string, MyToolbarData> _toolbars = new();
    
    static void Main(string[] a) => new Program(a);
    Program(string[] args) {
        run.thread(_Thread);
        
        var b = new wpfBuilder("Window").WinSize(400);
        b.R.AddButton("Change toolbar button text", _ => { _ChangeButtonText(); });
        b.R.AddOkCancel();
        b.End();
        if (!b.ShowDialog()) return;
    }

    
    void _Thread() {
        var synCon = new DispatcherSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synCon);
        
        ActionTriggers Triggers = new();
        Triggers.Options.ThreadOfTriggers();
        Triggers.Window[TWEvent.VisibleOnce, "LibreAutomate", flags: TWFlags.RunAtStartup] = _Toolbar;
        Triggers.Run();
        
        void _Toolbar(WindowTriggerArgs ta) {
            toolbar t = new("sgcqjw8d6dw", TBCtor.DontSaveSettings) { ActionThread = false };
            t["Aaaaa"] = o => { };
            t["Bbbbb"] = o => { };
            TBItem button = t.Last;
            t["Cccc"] = o => { };
            
            _toolbars["unique name"] = new(t, button, synCon);
            t.Closed += () => { _toolbars.Remove("unique name", out _); };
            
            t.Show(ta);
        }
    }

    
    void _ChangeButtonText() {
        var q = _toolbars["unique name"];
        q.threadContext.Post(_ => { //runs in the toolbar's thread
            q.button.Text = "New longer text";
            q.tb.DisplayText ^= true; q.tb.DisplayText ^= true; //a workaround to remeasure and redraw the toolbar. Don't need in next LA.
        }, null);
    }
}


record class MyToolbarData(toolbar tb, TBItem button, SynchronizationContext threadContext);
#6
Thank you very much - works perfectly.
I'm going to use it as base for the toolbarManager of my program.

Problem solved.


Forum Jump:


Users browsing this thread: 1 Guest(s)