c# - Multithreading and updating UI in Tray-only application, -
i have tray-only application gathering data service on separate thread , updating context menu it.
ui can updated on ui thread, i'm trying invoke ui update, without window there no handle created, invoke throw invalidoperationexception, , invokerequired returns false.
so how can update context menu in case?
edit: create tray icon this:
public class trayicon : idisposable { notifyicon ni; public trayicon() { ni = new notifyicon(); } public void display() { ni.icon = resources.trayicon; ni.text = "trayapp"; ni.visible = true; ni.contextmenustrip = new contextmenu(); } public void dispose() { ni.dispose(); } } public static void main(string[] args) { application.enablevisualstyles(); application.setcompatibletextrenderingdefault(false); using (trayicon icon = new trayicon()) { settings = new settings(); icon.display(); application.run(); } } edit2: data stored inside obix worker object available staticaly in application, event onupdated handlers execute after each update.
public class contextmenu: contextmenustrip { private bool _issettingsopen = false; private deviceslist deviceslist; public contextmenu() { deviceslist = new deviceslist(); deviceslist.text = "devices"; deviceslist.image = resources.online; items.add(deviceslist); app.obix.onupdated += invokeupdate; } void invokeupdate(object o, eventargs e) { console.writeline(this.ishandlecreated);//<<false contextmenu menu = this; methodinvoker method = delegate { deviceslist.repopulate(); }; invoke(method);//<<invalidoperationexception } } public class deviceslist : toolstripmenuitem { public deviceslist() { repopulate(); } public void repopulate() { iqueryable<toolstripitem> listed = (iqueryable<toolstripitem>)dropdownitems.asqueryable(); ienumerable<toolstripitem> remove = listed.where(item => !app.obix.devices.contains(new devicestatus(item.name))); foreach (toolstripitem ritem in remove) { dropdownitems.remove(ritem); } ienumerable<devicestatus> add = app.obix.devices.where(device => dropdownitems.indexofkey(device.name) == -1); foreach (devicestatus adevice in add) { toolstripmenuitem item = new toolstripmenuitem(); item.name = adevice.name; item.text = adevice.name; dropdownitems.add(item); } foreach (toolstripmenuitem item in dropdownitems) { devicestatus device = app.obix.devices.single(_device => _device.name == item.name); if (device.state == "down") item.image = resources.offline; else item.image = resources.online; } } } }
you can check invokerequired , invoke using contextmenustrip. it's control.
example
using system; using system.windows.forms; static class program { [stathread] static void main() { application.enablevisualstyles(); application.setcompatibletextrenderingdefault(false); var notifyicon1 = new notifyicon(); notifyicon1.icon = new form().icon; notifyicon1.visible = true; var contextmenustrip1 = new contextmenustrip(); notifyicon1.contextmenustrip = contextmenustrip1; var timer = new system.timers.timer(); timer.interval = 3000; timer.elapsed += (sender, e) => /*runs in different thread ui thread.*/ { if (contextmenustrip1.invokerequired) contextmenustrip1.invoke(new action(() => { contextmenustrip1.items.add(e.signaltime.tostring()); })); else contextmenustrip1.items.add(e.signaltime.tostring()); }; timer.start(); application.run(); } } note 1: invokerequired property , and invoke method belong control class. in above code, before showing contextmenustrip invokerequired returns false, because handle has not been created. show contextmenustrip, invokerequired return true.
note 2: invokerequired , invoke use deepest parent available. in case control does't have parent, control used.
Comments
Post a Comment