Posts: 20
Threads: 9
Joined: Jan 2024
11-08-2024, 11:48 AM
(This post was last modified: 11-08-2024, 11:49 AM by AutoStarter.)
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?
Posts: 12,075
Threads: 141
Joined: Dec 2002
Toolbar button text can be changed after creating the toolbar, but need a workaround.
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);
}
Posts: 20
Threads: 9
Joined: Jan 2024
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.
Posts: 12,075
Threads: 141
Joined: Dec 2002
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.
Posts: 12,075
Threads: 141
Joined: Dec 2002
In any case this can be useful.
// 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);
Posts: 20
Threads: 9
Joined: Jan 2024
Thank you very much - works perfectly.
I'm going to use it as base for the toolbarManager of my program.
Problem solved.