I have developed a wrapper for a library that uses a callback to notify events. This callback is called using another thread than UI's thread, so the wrapper uses the following script to call the event handlers into the right thread for a WinForm application.
void AoComm::Utiles::Managed::DispatchEvent( Delegate^ ev, Object^ sender, Object^ args )
{
ComponentModel::ISynchronizeInvoke^ si;
array<Delegate^>^ handlers;
if(ev != nullptr)
{
handlers= ev->GetInvocationList();
for(int i = 0; i < handlers->Length; ++i)
{
// target implements ISynchronizeInvoke?
si = dynamic_cast<ComponentModel::ISynchronizeInvoke^>(handlers[i]->Target);
try{
if(si != nullptr && si->InvokeRequired)
{
IAsyncResult^ res = si->BeginInvoke(handlers[i], gcnew array<Object^>{sender, args});
si->EndInvoke(res);
}else{
Delegate^ del = handlers[i];
del->Method->Invoke( del->Target, gcnew array<Object^>{sender, args} );
}
}catch(System::Reflection::TargetException^ e){
Exception^ innerException;
if (e->InnerException != nullptr)
{
innerException = e->InnerException;
}else{
innerException = e;
}
Threading::ThreadStart^ savestack = (Threading::ThreadStart^) Delegate::CreateDelegate(Threading::ThreadStart::typeid, innerException, "InternalPreserveStackTrace", false, false);
if(savestack != nullptr) savestack();
throw innerException;// -- now we can re-throw without trashing the stack
}
}
}
}
This code works pretty well, but I have read about Dispatcher class for WPF that do the same than my code (and more, of course).
So, is there something (class, mechanism, ...) equivalent to Dispatcher class for WinForms?
Thanks.
Right, this isn't the right way to do it. Winforms and WPF have different synchronization providers, they install theirs in System::Threading::SynchronizationContext::Current.
To use it, copy the Current value in your constructor. When you are ready to fire the event, check if it is nullptr. If it was then your object got constructed in a worker thread and you should fire your event directly. If it isn't then use the Post() method to run a helper method on the UI thread. Have that helper method fire the event.
Related
I'm trying to capture mouse and keyboard events from SDL2 using the SDL2-CS binding library. The events are polled for but these events are never raised.
I think this is because the polling needs to happen on the UI thread. I tried initializing SDL from the UI thread by calling App.Current.Dispatcher.Invoke(Init) but no events are polled.
Basic implementation of my class:
public override void Initialize()
{
if (hooked)
{
return;
}
App.Current.Dispatcher.Invoke(Init); //Run on the UI thread
}
private void Init()
{
var init = SDL.SDL_Init(SDL.SDL_INIT_VIDEO);
if (init != 0)
{
throw new Exception("Could not initialize SDL");
}
hooked = true;
ListenForEvents();
}
private void ListenForEvents()
{
SDL.SDL_Event ev;
while (true)
{
if (SDL.SDL_PollEvent(out ev) != 1) //This is continuously trigged
{
continue;
}
switch (ev.type) //This is never reached
{
case SDL.SDL_EventType.SDL_MOUSEMOTION:
if (MouseMoved != null) { MouseMoved(this, ev.motion); }
break;
...
}
}
}
I'm wondring if I'm invoking the Init on the UI thread wrong, or if the SDL initialization is wrong.
P.S. Hooking with user32.dll is not desired because this code will run on non windows environments as well.
Looking at your code I would say your UI is blocked because ListenForEvents is not running on a different thread and invoking the Init call will run the method - that never returns - on the UI thread.
It might be a good idea to call Init invoked, but then you should start a new thread for polling.
I have a basic form with a progress bar and want to pass in a delegate of sorts like this:
ProgressDialog.ShowAndExecute(delegate);
I can't figure out how to connect the delegate to progress messages.
void ShowAndExecute()
{
// Handle form disabling and whatnot...
thread = new Thread(new ThreadStart(ExecuteCommand));
while (thread.IsAlive)
{
Application.DoEvents();
Thread.Sleep(100);
}
}
// Example of the method I would like to pass in
void ExecuteCommand()
{
for (int i = 0; i < 10; i++) Thread.Sleep(1000);
}
I thought about creating an interface that the commands should implement. They can fire an event whenever an update occurs, but how do I let the calling thread know it was fired?
How do you handle passing in delegates that report progress through events and act (move progress bar) based on those? This is a static dialog (to make it easier to call throughout the app)
Only the ExecuteCommand knows its current progress. So some interface or class must be passed to the ExecuteCommand method. The ExecuteCommand will set the progress, then it's up to the implementation of the listener to determine what to do with the progress. In general, the listener will check if it's a significant change** in progress, and if so, it will BeginInvoke a call to update a progress bar.
** - If you process thousands of items in a short period of time, then you want to protect against calling BeginInvoke too much, otherwise it will lock up the UI thread.
void ExecuteCommand(IThreadController tc)
{
for (int i = 0; i < 10; i++) {
Thread.Sleep(1000);
tc.setProgress("processing ...", (i+1), 10);
}
}
I am planning to have a threadsafe observable collection, where in I run a task on the background and update the UI as and when result is obtained using the dispatcher.
I got a download from the internet which is ThreadSafe. But I have a small concern. I wish to do a silent update on the UI. Since the user is already working or selecting the bound collection, I would not want to disturb the user selected entry. In other words, I would like to add an entry similar to Microsoft Outlook, when a new mail arrives.
Is this possible, and are there any such examples.
Thanks
You can have derived class from ObservableCollection and override it OnCollectionChanged function and raise it handler when ever you require. follows are code.
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
private bool suspendCollectionChangeNotification;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!this.suspendCollectionChangeNotification)
{
NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk through invocation list
bool isEventInvoked = false;
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
isEventInvoked = false;
if (handler.Target is DispatcherObject)
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
// This check is to make sure if the call on a different thread than the dispatcher.
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
{
// Invoke handler in the target dispatcher's thread
// Intentionally called begin invoke because there is a problem with Dispatcher.Invoke that it hangs when
// called simultaneously
dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, e);
isEventInvoked = true;
}
}
if (!isEventInvoked)
{
handler(this, e);
}
}
}
}
}
I found a nice Find Name code snippet that I'm using in a WPF solution:
public static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(Control.NameProperty) as string;
if (controlName == name)
{
return child as T;
}
else
{
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
}
return null;
}
But this only works if I am on the UI thread.
I have another thread that is playing an audio file with an end sync. I want to use the code above to set a dep property on the ui thread, but I keep getting a cross-thread error.
Even trying a simple:
SoundFXPad selectedSoundFXPad = (SoundFXPad)m_parent.FindName("panelC" + numbervar);
Gives me the same error
All the other thread-safe WPF Dispatcher-Invoke codes I have seen assume you already know the name of the control. Is there a way to use the either code above in a thread-safe way to affect a UI control from another thread where the name needs to be "found"?
Thank You!
There's generally one UI thread per application (generally; you can create multiple, but it's not common). So you don't need the control name to find the dispatcher - try this:
Application.Current.Dispatcher.Invoke(new Action(
delegate {
// Put code that needs to run on the UI thread here
}));
I need in wpf app check messages on server. I have own method which load messages on server-LoadRp().
I would like to create some kind of listener which would check, every 3 seconds whether on the server are not new messages.
I call method for loading messages on dispatcher timer tick event, it is suitable? Any another solution. It’s possible call timer in another thread in wpf?
Code is here:
public MessangerWindow(PokecCommands pokecCmd)
{
InitializeComponent();
PokecCmd = pokecCmd;
_friendsData = PokecCmd.LoadFriends();
friendsListBox.DataContext = _friendsData;
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick+=new EventHandler(DispatcherTimer_Tick);
_dispatcherTimer.Interval = new TimeSpan(0,0,3);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
{
try
{
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if (message != null)
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
What is suitable to use:
Dispatcher with no background threads
Dispatcher with background threads
Multiple Threads
If you are ok with locking up your UI for the time it takes to check on the server, using a DispatcherTimer the way you are doing it will work fine.
If checking for new messages could take more than a few milliseconds and you want your UI to be responsive while it checks, you should use multiple threads. In that case, once the new data had arrived you would use Dispatcher.Invoke to display it.
Your code in the thread that checks for messages might look like this:
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if( message != null )
Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
);