In an MVVM application I have a long running calculation that runs
in legacy code.
That legacy code shows a MessageBox to ask the user if it shall continue.
Now I want this code to stick to MVVM as easy as possible and thought
about handing in a callback to show the MessageBox and evaluating the
result inside.
How can this be done the easiest?
Have often seen Action for callbacks, but I have no idea how
to work with the bool inside the legacy code.
I want to pass the string to show in the MessageBox from the legacy code
and return the decision (a bool) to the legacy code.
Please note: I do not have to do a bigger refactoring right now, but want
to get rid of the MessageBox inside the legacy code right now.
Perhaps I can use a function like
private bool ShowMessageBox(string text)
{
var result = MessageBox.Show(text, "", MessageBoxButton.YesNo);
if (result.Equals(MessageBoxResult.Yes))
{
return true;
}
return false;
}
-edit-
Should I use some
Action<string, Action<bool>>
for the method signature?
How can I access the bool in the legacy code?
Maybe you can use a delegate?
For the method you showed, you can create a delegate like this:
public delegate bool ShowMessageBoxDelegate(string text);
Then let's say you have a property using the delegate as the type:
public ShowMessageBoxDelegate ShowMessageBoxDelegateProperty { get; set; }
Now if your ShowMessageBox method matches the signature of this delegate...
public bool ShowMessageBox(string text)
{
var result = MessageBox.Show(text, "", MessageBoxButton.YesNo);
if (result.Equals(MessageBoxResult.Yes))
{
return true;
}
return false;
}
... then you could set it as the value of the ShowMessageBoxDelegateProperty property:
ShowMessageBoxDelegateProperty = ShowMessageBox;
Note the missing parenthesis. A delegate can also be multicast, which simply means that they can have more than one method attached to them:
ShowMessageBoxDelegateProperty += ShowMessageBox;
You can also use them as parameters in methods:
public void ProxyShowMessageBox(ShowMessageBoxDelegate showMessageBoxDelegate)
{
if (showMessageBoxDelegate != null)
{
bool result = showMessageBoxDelegate("MessageBox message");
}
}
You would then call it like this:
ProxyShowMessageBox(ShowMessageBox);
You can find out more from the Delegates Tutorial page at MSDN.
Related
I'm working on an app that uses the WimgApi package by Jeff Kluge to apply a windows image to a disk.
I'm having issues getting the example callback method to update UI components, specifically a label on a form (ideally a progressbar).
I've tried to use a delegate to set the value but this does not seem to work as I cannot figure how to pass the delegate down to the callback method.
If I make the callback method non-static, I can access the form properties but then I get a deadlock that even if I disable deadlock breaking, it just locks up.
I've been told to look at using IProgress and async but whilst I can change the code to run the method asynchronously (this works and UI doesn't lock), I still cannot figure out how to get the MyCallbackMethod to send info back to the ui.
//Apply Image Method
public void ApplyImage()
{
using (WimHandle wimHandle = WimgApi.CreateFile(#"C:\osimages\test.wim",
WimFileAccess.Read,
WimCreationDisposition.OpenExisting,
WimCreateFileOptions.None,
WimCompressionType.None))
{
// Always set a temporary path
WimgApi.SetTemporaryPath(wimHandle, Environment.GetEnvironmentVariable("TEMP"));
// Register a method to be called while actions are performed by WIMGAPi for this .wim file
WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod);
try
{
// Get a handle to the first image in the .wim file
using (WimHandle imageHandle = WimgApi.LoadImage(wimHandle, 1))
{
// Apply the image contents to C:\Apply
// This call is blocking but WIMGAPI will be calling MyCallbackMethod() during the process
WimgApi.ApplyImage(imageHandle, #"X:\", WimApplyImageOptions.None);
}
}
finally
{
// Be sure to unregister the callback method
//
WimgApi.UnregisterMessageCallback(wimHandle, MyCallbackMethod);
}
}
private static WimMessageResult MyCallbackMethod(WimMessageType messageType, object message, object userData)
{
switch (messageType)
{
case WimMessageType.Progress: // Some progress is being sent
// Get the message as a WimMessageProgress object
//
WimMessageProgress progressMessage = (WimMessageProgress)message;
// UPDATE UI
//THIS IS WHERE I WANT TO SEND BACK PROGRESS INFO
break;
//REMOVED OTHER MESSAGE CASE STATEMENTS TO CONDENSE CODE
}
// Depending on what this method returns, the WIMGAPI will continue or cancel.
//
// Return WimMessageResult.Abort to cancel. In this case we return Success so WIMGAPI keeps going
return WimMessageResult.Success;
}
//full example code at Example code is https://github.com/jeffkl/ManagedWimgApi/wiki/Message-Callbacks
If I try and access the label property in the callback method , I receive an 'object reference is required for non static field, method or property form1.progressLabel.text . I've tried to create a delegate but seem to have issues accessing the method in the call back.
I've watched several videos and tried to understand the msdn documents for delegates, callbacks and things like async / backgroundworker but I just seem to come away more confused.
Really appreciate any pointers / things I should be focusing on.
Dislaimer: I don't have any experience with the WimgApi package.
But there is an overload of the WimgApi.RegisterMessageCallback method taking an arbitrary object that will be passed to the callback.
So please try this:
WimgApi.RegisterMessageCallback(wimHandle, MyCallbackMethod, this);
and in the callback:
var form = (MyForm)userData;
if (form.InvokeRequired)
{
form.Invoke((MethodInvoker)(() => UpdateProgressUI(...)));
}
else
{
form.UpdateProgressUI(...);
}
Making some assumptions here but if you will only be showing one progress form at a time, you should be able to get away with storing a static reference to it. I.e.:
class ProgressForm
{
private static ProgressForm staticRef;
private void Form_Loaded(object sender, EventArgs e)
{
staticRef = this;
}
private void InternalCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
{
// Ensure we're touching UI on the right thread
if (Dispatcher.InvokeRequired)
{
Dispatcher.Invoke(() => InternalCallback(m, w, l, u));
return;
}
// Update UI components
// ....
}
private static uint StaticCallback(uint m, IntPtr w, IntPtr l, IntPtr u)
{
staticRef?.InternalCallback(m, w, l, u);
return 0;
}
}
We have built a huge winforms project, already in progress for multiple years.
Sometimes, our users get an exception which looks like this one.
The resolution of this problem seems to be:
don't acces UI components from a background thread
.
But since our project is a very big project with a lot of different threads, we don't succeed in finding all these.
Is there a way to check (with some tool or debugging option) which components are called from a background thread?
To clarify:
I created a sample winforms project with a single Form, containing two Button
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Clicked!";
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
button2.BackColor = Color.Red; //this does not throw an exception
//button2.Text = "Clicked"; //this throws an exception when uncommented
});
}
}
The background color of button2 is set to red when the button is clicked. This happens in a background thread (which is considered bad behavior). However, it doesn't (immediately) throw an exception. I would like a way to detect this as 'bad behavior'. Preferably by scanning my code, but if it's only possible by debugging, (so pausing as soon as a UI component is accessed from a background thread) it's also fine.
I've got 2 recommendations to use together, the first is a Visual Studio Plugin called DebugSingleThread.
You can freeze all the threads and work on one at a time (obviously the non-main-UI threads) and see each threads access to controls. Tedious I know but not so bad with the second method.
The second method is to get the steps in order to reproduce the problem. If you know the steps to reproduce it, it will be easier to see whats causing it. To do this I made this User Action Log project on Github.
It will record every action a user makes, you can read about it here on SO: User Activity Logging, Telemetry (and Variables in Global Exception Handlers).
I'd recommend you also log the Thread ID, then when you have been able to reproduce the problem, go to the end of the log and work out the exact steps. Its not as painful as it seems and its great for getting application telemetry.
You might be able to customise this project, eg trap a DataSource_Completed event or add a dummy DataSource property that sets the real Grids DataSource property and raises an INotifyPropertyChanged event - and if its a non-main thread ID then Debugger.Break();.
My gut feeling is you're changing a control's (eg a grid) data source in a background thread (for that non-freeze feel) and thats causing a problem with synchronisation. This is what happened to the other DevExpress customer who experienced this. Its discussed here in a different thread to the one you referenced.
Is your app set to ignore cross threading intentionally?
Cross-thread operations should be blowing up all the time in winforms. It checks for them like crazy in just about every method. for a starting point check out https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs.
Somewhere in your app, somebody might have put this line of code:
Control.CheckForIllegalCrossThreadCalls = False;
Comment that out and run the app, then follow the exceptions.
(Usually you can fix the problem by wrapping the update in an invoke, e.g., in a worker thread if you see textbox1.text=SomeString; change it to `textbox.invoke(()=>{textbox1.text=SomeString;});.
You may also have to add checking for InvokeRequired, use BeginInvoke to avoid deadlocks, and return values from invoke, those are all separate topics.
this is assuming even a moderate refactor is out of the question which for even a medium sized enterprise app is almost always the case.
Note: it's not possible to guarantee successful discovery of this case thru static analysis (that is, without running the app). unless you can solve the halting problem ... https://cs.stackexchange.com/questions/63403/is-the-halting-problem-decidable-for-pure-programs-on-an-ideal-computer etc...
I did this to search for that specific situation but of course, need to adjust it to your needs, but the purpose of this is to give you at least a possibility.
I called this method SearchForThreads but since it's just an example, you can call it whatever you want.
The main idea here is perhaps adding this Method call to a base class and call it on the constructor, makes it somewhat more flexible.
Then use reflection to invoke this method on all classes deriving from this base, and throw an exception or something if it finds this situation in any class.
There's one pre req, that is the usage of Framework 4.5.
This version of the framework added the CompilerServices attribute that gives us details about the Method's caller.
The documentation for this is here
With it we can open up the source file and dig into it.
What i did was just search for the situation you specified in your question, using rudimentary text search.
But it can give you an insight about how to do this on your solution, since i know very little about your solution, i can only work with the code you put on your post.
public static void SearchForThreads(
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
var startKey = "this.Controls.Add(";
var endKey = ")";
List<string> components = new List<string>();
var designerPath = sourceFilePath.Replace(".cs", ".Designer.cs");
if (File.Exists(designerPath))
{
var designerText = File.ReadAllText(designerPath);
var initSearchPos = designerText.IndexOf(startKey) + startKey.Length;
do
{
var endSearchPos = designerText.IndexOf(endKey, initSearchPos);
var componentName = designerText.Substring(initSearchPos, (endSearchPos - initSearchPos));
componentName = componentName.Replace("this.", "");
if (!components.Contains(componentName))
components.Add(componentName);
} while ((initSearchPos = designerText.IndexOf(startKey, initSearchPos) + startKey.Length) > startKey.Length);
}
if (components.Any())
{
var classText = File.ReadAllText(sourceFilePath);
var ThreadPos = classText.IndexOf("Task.Run");
if (ThreadPos > -1)
{
do
{
var endThreadPos = classText.IndexOf("}", ThreadPos);
if (endThreadPos > -1)
{
foreach (var component in components)
{
var search = classText.IndexOf(component, ThreadPos);
if (search > -1 && search < endThreadPos)
{
Console.WriteLine($"Found a call to UI thread component at pos: {search}");
}
}
}
}
while ((ThreadPos = classText.IndexOf("Task.Run", ++ThreadPos)) < classText.Length && ThreadPos > 0);
}
}
}
I hope it helps you out.
You can get the Line number if you split the text so you can output it, but i didn't want to go through the trouble, since i don't know what would work for you.
string[] lines = classText.Replace("\r","").Split('\n');
Try that:
public static void Main(string[] args)
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(exception handler);
// Set the unhandled exception mode to force all Windows Forms errors to go through the handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += // add the handler here
// Runs the application.
Application.Run(new ......);
}
Then you can log the message and the call stack and that should give you enough information to fix the issue.
I recommend you update your GUI to handle this situation automatically for your convenience. You instead use a set of inherited controls.
The general principle here is to override the property Set methods in a way to make them Thread Safe. So, in each overridden property, instead of a straight update of the base control, there's a check to see if an invoke is required (meaning we're on a separate thread the the GUI). Then, the Invoke call updates the property on the GUI thread, instead of the secondary thread.
So, if the inherited controls are used, the form code that is trying to update GUI elements from a secondary thread can be left as is.
Here is the textbox and button ones. You would add more of them as needed and add other properties as needed. Rather than putting code on individual forms.
You don't need to go into the designer, you can instead do a find/replace on the designer files only. For example, in ALL designer.cs files, you would replace System.Windows.Forms.TextBox with ThreadSafeControls.TextBoxBackgroundThread and System.Windows.Forms.Button with ThreadSafeControls.ButtonBackgroundThread.
Other controls can be created with the same principle, based on which control types & properties are being updated from the background thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ThreadSafeControls
{
class TextBoxBackgroundThread : System.Windows.Forms.TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
class ButtonBackgroundThread : System.Windows.Forms.Button
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
}
I have the following code I want to run on a windows forms application some of my coworkers are using:
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
if (userName.ToLower().Contains("trollthisguy#1") ||
userName.ToLower().Contains("trollthisguy#2"))
{
//initiate rickroll
System.Diagnostics.Process.Start("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
}
Right now, I have this code in the main form .cs file on a button pressed event. Is there a clever way for me to hide this? I want to make them work for it... :)
Create a DLL with a class with some familiar name, like String and some familiar static function, like IsNullOrEmpty and start the process inside:
public class String
{
public static bool IsNullOrEmpty(string value)
{
System.Diagnostics.Process.Start("https://www.youtube.com/watch?v=dQw4w9WgXcQ");
return string.IsNullOrEmpty(value);
}
}
Then, reference your DLL and in your button pressed event, do something very simple just to call this function. Something like:
if(String.IsNullOrEmpty(MyButton.Content) {
// ....
}
Maybe they won't notice. ;)
I have this code to do some basic sanity checking before posting a record:
if (string.IsNullOrWhiteSpace(textBoxFirstName.Text))
{
errorProvider.SetError(textBoxFirstName, "Enter a first name");
}
if (string.IsNullOrWhiteSpace(textBoxLastName.Text))
{
errorProvider.SetError(textBoxLastName, "Enter a last name");
}
...but I want to then do something like this to exit the handler if either of those conditions has been met:
if (errorProvider.SetErrorCount > 0) then return;
...but I see no way to do that. I don't want to have to write an "OR" statement to see if either of the textBoxes I'm checking are empty and then short circuit the handler that way.
Is there a way to tell whether the errorProvider is "dirty" to avoid cluttery code?
Write a method and pass it the error message and the control. Have a counter variable and increase the counter within the method. Here is some pseudocode:
private int errorCount;
SetError(Control c, string message)
{
errorProvider.SetError(c, message);
errorCount++;
}
One option would be to use the GetError method off of the ErrorProvider.
// possibly use a backing field for all controls to evaluate
private readonly Control[] textBoxes = new[] { textBoxFirstName, textBoxLastName };
// helper property to evaluate the controls
private bool HasErrors
{
get { return textBoxes.Any(x => !string.IsNullOrEmpty(errorProvider.GetError(x)); }
}
I've got a C# program that talks to an instrument (spectrum analyzer) over a network. I need to be able to change a large number of parameters in the instrument and read them back into my program. I want to use backgroundworker to do the actual talking to the instrument so that UI performance doesn't suffer.
The way this works is - 1) send command to the instrument with new parameter value, 2) read parameter back from the instrument so I can see what actually happened (for example, I try to set the center frequency above the max that the instrument will handle and it tells me what it will actually handle), and 3) update a program variable with the actual value received from the instrument.
Because there are quite a few parameters to be updated I'd like to use a generic routine. The part I can't seem to get my brain around is updating the variable in my code with what comes back from the instrument via backgroundworker. If I used a separate RunWorkerCompleted event for each parameter I could hardwire the update directly to the variable. I'd like to come up with a way of using a single routine that's capable of updating any of the variables. All I can come up with is passing a reference number (different for each parameter) and using a switch statement in the RunWorkerCompleted handler to direct the result. There has to be a better way.
I think what I would do is pass a list of parameters, values, and delegates to the BackgroundWorker. That way you can write the assign-back code "synchronously" but have execution deferred until the values are actually retrieved.
Start with a "request" class that looks something like this:
class ParameterUpdate
{
public ParameterUpdate(string name, string value, Action<string> callback)
{
this.Name = name;
this.Value = value;
this.Callback = callback;
}
public string Name { get; private set; }
public string Value { get; set; }
public Action<string> Callback { get; private set; }
}
Then write your async code to use this:
private void bwUpdateParameters_DoWork(object sender, DoWorkEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
WriteDeviceParameter(update.Name, update.Value);
update.Value = ReadDeviceParameter(update.Name);
}
e.Result = updates;
}
private void bwUpdateParameters_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
var updates = (IEnumerable<ParameterUpdate>)e.Argument;
foreach (var update in updates)
{
if (update.Callback != null)
{
update.Callback(update.Value);
}
}
}
Here's how you would kick off the update. Let's say you've got a bunch of member fields that you want to update with the actual values of the parameters that were used:
// Members of the Form/Control class
private string bandwidth;
private string inputAttenuation;
private string averaging;
// Later on, in your "update" method
var updates = new List<ParameterUpdate>
{
new ParameterUpdate("Bandwidth", "3000", v => bandwidth = v),
new ParameterUpdate("InputAttenuation", "10", v => inputAttenuation = v),
new ParameterUpdate("Averaging", "Logarithmic", v => averaging = v)
};
bwUpdateParameters.RunWorkerAsync(updates);
That's all you have to do. All of the actual work is done in the background, but you're writing simple variable-assignment statements as if they were in the foreground. The code is short, simple, and completely thread-safe because the actual assignments are executed in the RunWorkerCompleted event.
If you need to do more than this, such as update controls in addition to variables, it's very simple, you can put anything you want for the callback, i.e.:
new ParameterUpdate("Bandwidth", "3000", v =>
{
bandwidth = v;
txtBandwidth.Text = v;
})
Again, this will work because it's not actually getting executed until the work is completed.
[Edit - look back at update history to see previous answer. Talk about not being able to see the wood for the trees]
Is there any reason that, rather than passing a reference number to the Background Worker, you can't pass the ID of the label that should be updated with any value passed back?
So the UI adds an item in the work queue containing:
Variable to change
Attempted change
UI ID
and the BackgroundWorker triggers an event with EventArgs containing
Attempted change
Actual value after attempt
UI ID
Error Message (null if successful)
which is all the information you need to update your UI without a switch or multiple event args and without your Background Worker ever being aware of UI detail.
How about something like this?
[TestFixture]
public class BGWorkerTest
{
string output1;
string output2;
[Test]
public void DoTest()
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, args) =>
{
output1 = DoThing1();
output2 = DoThing2();
};
backgroundWorker.RunWorkerAsync();
//Wait for BG to finish
Thread.Sleep(3000);
Assert.AreEqual("Thing1",output1);
Assert.AreEqual("Thing2",output2);
}
public string DoThing1()
{
Thread.Sleep(1000);
return "Thing1";
}
public string DoThing2()
{
Thread.Sleep(1000);
return "Thing2";
}
}