Hi all new to C# so apologies upfront
I am trying to create an handle in a program to retrieve the string/text and event from this public function in this (websocketwrapper) class.
I have part of the class in question here:
public WebSocketWrapper OnMessage(Action<string, WebSocketWrapper> onMessage)
{
_onMessage = onMessage;
return this;
}
note: _onMessage is declared in the class as:
private Action<string, WebSocketWrapper> _onMessage;
I understand how delegates work and in the process of learning how to use them in actions and event handling but can't work out how to create away to retrieve the result of the above method.
That pattern is typically used for you to add your own callback, i.e.
server.OnMessage((s, wrapper) => {
Console.WriteLine($"received: {s}");
});
So: whenever a message is received, your callback is invoked and provides the value as s that is only defined inside the callback lambda. You could also write this as:
server.OnMessage(ProcessMessage);
...
void ProcessMessage(string s, WebSocketWrapper wrapper)
{
Console.WriteLine($"received: {s}");
}
where now the ProcessMessage method will be invoked whenever a message is received.
I think I'm missing something here. I've got a WPF form that has some methods on it I need to call from an external source (usually on a non-UI thread). I retrieve a reference to the form, then attempt to call the method via Dispatcher.Invoke so it's marshalled to the UI thread. The problem is that this code won't work as the Invoke fires an Action, so the result is always an empty string (even though the docs say Invoke is supposed to be synchronous).
public string GetValueById(string id, string value)
{
Application.Current.Dispatcher.Invoke(() =>
{
var main = Application.Current.MainWindow as MainWindow;
if (main != null)
{
return main.GetValue(id);
}
});
return "";
}
I can't quite wrap my head around how to make this work.
If you look at the documentation for that Dispatcher.Invoke overload, you'll see that if you pass it a Func<TResult> callback then it will return the TResult returned by executing that callback. All you have to do is actually make use the return value:
public string GetValueById(string id, string value)
{
return Application.Current.Dispatcher.Invoke(() =>
{
var main = Application.Current.MainWindow as MainWindow;
if (main != null)
{
return main.GetValue(id);
}
});
}
I have an extension that works in WinForms and I'm trying to get it to work in WPF, but it's throwing the following exception when ic all .Compile().DynamicInvoke().
Here is how I'm calling the method:
spDetailControls.InvokeThreadSafeMethod(() => spDetailControls.Children.Add(_generalDetail));
And here is the extension method:
public static void InvokeThreadSafeMethod(this System.Windows.FrameworkElement control, Expression<Action> method)
{
if (!control.Dispatcher.CheckAccess())
{
var del = new InvokeThreadSafeMethodDelegate(InvokeThreadSafeMethod);
control.Dispatcher.Invoke(del, control, method);
}
else
{
method.Compile().DynamicInvoke();
}
}
Is there something else I should be calling in WPF? Or something else I'm missing?
EDIT:
Per LucasTrzesniewski's suggestion, I changed Expression to just Action. It no longer throws that error, but instead throws a cross thread exception error. The whole point of this extension method is so that it can be called within a background thread.
Here's the changed code. It's likely that I changed something wrong
public static void InvokeThreadSafeMethod(this System.Windows.FrameworkElement control, Action method)
{
if (!control.Dispatcher.CheckAccess())
control.Dispatcher.Invoke(new InvokeThreadSafeMethodDelegate(InvokeThreadSafeMethod), control, method);
else
method.DynamicInvoke();
}
I had to change the delegate as well:
private delegate void InvokeThreadSafeMethodDelegate(System.Windows.FrameworkElement control, Action method);
Your extension method is unnecessarily complicated, and may be obfuscating an error originating in your delegate. This is all you need:
public static void InvokeThreadSafeMethod(
this System.Windows.FrameworkElement control,
Action method)
{
if (control.Dispatcher.CheckAccess())
method();
else
control.Dispatcher.Invoke(method);
}
If you're getting a cross-thread exception while using the implementation above, then your delegate is probably touching a UI element that was created on a different thread than control.
For example, if this is your actual code:
spDetailControls.InvokeThreadSafeMethod(
() => spDetailControls.Children.Add(_generalDetail));
...then maybe you _generalDetail on a background thread?
I am using threads to run long operations in my program's UI so that it doesn't lock up. However, in those tasks I need to update controls, which is impossible not from the thread they were created on. It is suggested to use control.BeginInvoke(Delegate) to execute the method you want.
However, to do that you have to declare a delegate type and only then you can call them.
So, it goes like this: if I want to execute method void Update(), i have to go:
delegate void CallbackVoid();
void Update() {...}
...(in task code)...
this.BeginInvoke(new CallbackVoid(Update));
This is rather tiresome to do for every single method out there. Can't I just somehow do it naturally, like:
void Update() {...}
this.BeginInvoke(Update);
One option which simplified things is to add an extension method:
public static void BeginInvokeAction(this Control control, Action action)
{
control.BeginInvoke(action);
}
Then you can just use:
this.BeginInvokeAction(action);
The reason this works is that we're now providing a concrete delegate type for the compiler to convert the method group to.
UPDATED: WORKS FOR WPF!!!
You can use short syntax with anonymous methods, without even declaring your methods
Dispatcher.BeginInvoke(DispatcherPriority.Background, new MethodInvoker(() =>
{
//Your Update code
}));
Try the following:
if (this.controlname.InvokeRequired && !this.controlname.IsDisposed)
{
Invoke(new MethodInvoker(delegate()
{
//Update control on GUI here!
}));
else if(!this.controlname.IsDisposed)
{
//AND here!
}
BeginInvoke is asynchronous, Invoke is synchronous, which one you use depends on what you're trying to do. If you need the call to complete before you move on, then you want synchronous calls.
Here's my favorite construct for synchronous invokes:
static void InvokeIfRequired(Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action.Invoke();
}
}
Used:
void MyTestFunction()
{
InvokeIfRequired(myControl, () =>
{
MyFunction();
MyOtherFunction();
});
// Or more simply:
InvokeIfRequired(myControl, () => MyFunction());
}
There is a little overhead in the creation of the Action, but it simplifies the code quite a bit to not have to think about the details everywhere.
Presently I'm working with WinForms(in C#) and I have to run the application in the background. For this purpose I'm using asynchronous. When I run the application it's showing an exception like
"Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
How can I solve this error?
When making method calls to a control, if the caller is on a different thread than the one the control was created on, you need to call using Control.Invoke. Here is a code sample:
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.Invoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
Hope it helps.
Most often, the best way to do this sort of thing with WinForms is to use BackgroundWorker, which will run your work on a background thread, but provide you with a nice clean way to report status back to the UI.
In a lot of everyday .NET programming, explicitly creating threads or calling .Invoke is a sign that you're not using the framework to its full advantage (of course, there are lots of legitimate reasons to do low-level stuff too, it's just that they're less common that people sometimes realise).
You need to check if Invoke is required for the control you're trying to update. Something like this:
Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;
void SetControlText(Control toSet, string text) {
if (this.InvokeRequired) {
this.Invoke(setterCallback, toSet, text);
}
else {
setterCallback(toSet, text);
}
}
Updated from Invoke to begin Invoke
// you can define a delegate with the signature you want
public delegate void UpdateControlsDelegate();
public void SomeMethod()
{
//this method is executed by the background worker
InvokeUpdateControls();
}
public void InvokeUpdateControls()
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateControlsDelegate(UpdateControls));
}
else
{
UpdateControls();
}
}
private void UpdateControls()
{
// update your controls here
}
A pattern you might find useful is to do a check at the top of functions that interact with the GUI to see whether you are running on the correct thread or not and have the function invoke itself if required. Like this:
public delegate void InvocationDelegate();
public void DoGuiStuff(){
if (someControl.InvokeRequired){
someControl.Invoke(InvocationDelegate(DoGuiStuff));
return;
}
//GUI manipulation here
}
Using this pattern - if you are on the correct thread when the method is called it doesn't invoke itself, but if you are on a different thread it will invoke itself and then return (so the GUI manipulation logic is only ever called once either way).
The UI changes can be done with Control.Invoke() methods, this cross thread exception can be solved using below code snippet.
void UpdateWorker()
{
//Here ddUser is the user control
//Action to be performed should be called within { } as like below code
if (this.ddUser.InvokeRequired)
ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); }));
}
I knew the topic is 10 years old, but I would like to improve the solution for generic through lambda selector instead of defining of each type of setter
private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
{
if (this.InvokeRequired)
this.Invoke(MyUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
Or static
public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
{
if (control.InvokeRequired)
control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
GetSetter method from here to assign value to a property has been selected through lambda
public static Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> pExpression
)
{
var parameter1 = Expression.Parameter(typeof(T));
var parameter2 = Expression.Parameter(typeof(TProperty));
// turning an expression body into a PropertyInfo is common enough
// that it's a good idea to extract this to a reusable method
var member = (MemberExpression)pExpression.Body;
var propertyInfo = (PropertyInfo)member.Member;
// use the PropertyInfo to make a property expression
// for the first parameter (the object)
var property = Expression.Property(parameter1, propertyInfo);
// assignment expression that assigns the second parameter (value) to the property
var assignment = Expression.Assign(property, parameter2);
// then just build the lambda, which takes 2 parameters, and has the assignment
// expression for its body
var setter = Expression.Lambda<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
Then the using is pretty simple
SetControlSafety(txtStatus, x => x.Text, "Loading resources...");
BeginInvoke
It is a good way to prevent a cross-thread exception. I read it in a book "The C# Programmer’s Study Guide (MCSD"
You can use BeginInvoke
BeginInvoke method is used to change values of UI control from other threads. It does it in a thread-safe way. It requires a delegate; it tells which UI control needs to change its value.
private async void button1_Click(object sender, EventArgs e)
{
Task task = Task.Run(() =>
{
this.BeginInvoke(new Action(() =>
{
label1.Text = "Hello";
}));
});
await task;
}
The value of label1.Text shall be changed to “Hello” and no exception will arise because it’s a threadsafe operation.