Custom Message Box displays after Message box - c#

I have some code that when I call my CustomMessageBox it displays the box with a user prompt for an amount of my object to add, once that is done I have it added to a list of objects. Once added, it then Displays a MessageBox.Show to just let the user know it was added.
My problem is that when I run the code it executes all the code, bypasses the display of the Custom message box, then displays the MessageBox.Show, and THEN displays the CMB.Show. I ran the code through the debugger and followed the trail and it hits the CMB.Show before the MessageBox.Show, but is displayed once the code is done. Sorry, I am still learning and might not be telling the problem well, please let me know if there is anything I can further explain upon.
Some code:
private int BasicLand(Card basicLand)
{
var countBox = new TextBox
{
Name = "count",
Width = 100,
};
var cmbCount = new CustomMessageBox
{
Caption = "Blah",
Content = countBox,
RightButtonContent = "ok",
};
cmbCount.Dismissed += (s1, e1) =>
{
switch (e1.Result)
{
case CustomMessageBoxResult.RightButton:
if (int.TryParse(countBox.Text, out tempInt) && Convert.ToInt32(countBox.Text) > 0)
{
countReturn = Convert.ToInt32(tempInt);
break;
}
else
{
//Some code for error....
}
}
};
cmbCount.Show();
return countReturn;
}
Then the other part that triggers first but is last in the code block.
MessageBox.Show("Object was added to List!");
I tried adding the ShowDialog to the custom box but it came up broken in VS. BasicLand is called within another method and when the object is added to the list it will display the MessageBox.Show.

The problem with your code is, it does not take into account that any user interaction is asynchronous. When you call Show() it will actually show the messagebox, but it will not block your currently running thread, the other statements after the call to Show() will be executed immediately and thus your method returns a returnvalue that has not been provided by the user but is just the default. To fix this you have to write your code in continuations.
private void PromtUserForFeeblefezerAmount(Action<int> continueFeeblefzing, Action cancel)
{
var messagebox = CreateFeeblefezerPromt();
messagebox.Dismissed += (sender, args) =>
{
if ( args.Result == CustomMessageBoxResult.RightButton )
continueFeeblefzing( GetFeeblefezerAmount(messagebox) );
else
cancel();
};
messagebox.Show();
}

Related

Why don't delays work when I'm attempting to flash an error message?

Problem: I'm working on a calculator as my first MVVM application and have come across an interesting problem that I would like to understand better. My noob problem is that I'm trying to flash an error message for an invalid input--in this case I don't want the user to use the negate operator in an invalid location. In order to flash the message across the screen, I'm saving the display in another variable, setting the display to say "Invalid Operation", then I'd like to delay for half a second and reset the display to what it was before (from the temp variable). My problem is that the display variable gets set but the actual display doesn't update to show the error message, no matter how long the delay is.
I've tried both blocking (Thread.Sleep) and non-blocking delays (Task.Delay) within the function, writing separate functions to set and reset the display, and delaying within the Negate function instead, but none of these attempts allow the display to update. The display works as expected when adding and deleting characters in other parts of the code, so I don't think there's an issue with that.
Is this some sort of piping issue (the delay function actually starts before it can call the Display property) or something else entirely? I've checked other posts on here and those solutions don't seem to solve my issue. I'd love feedback on why this doesn't work as I'd expect it to as well as more efficient/effective ways to code this. Here are the relevant code blocks:
public void Negate()
{
if (Display.Length > 0)
{
if (Display[Display.Length - 1].Equals('-'))
{
Display = Display.Substring(0, Display.Length - 1);
}
else if (Display[Display.Length - 1].Equals(' ') || Display[Display.Length - 1].Equals('(') ||
Display[Display.Length - 1].Equals('E') || Display[Display.Length - 1].Equals('^'))
{
Display += '-';
}
else
{
InvalidOperation();
}
}
else
{
Display = "-";
}
}
public void InvalidOperation()
{
tempDisplay = Display;
Display = "Invalid Operation";
Thread.Sleep(500);
Display = tempDisplay;
}
public string Display
{
get
{
return _display;
}
set
{
_display = value;
OnPropertyChanged();
}
}
UI will be updated only after method InvalidOperation execution is complete, so because in last line of the method you set value back to original - there are no updates in UI.
Asynchronous approach should work, because await operator will "pause" InvalidOperation method and return execution to the message loop which will update UI controls.
public async Task InvalidOperation()
{
tempDisplay = Display;
Display = "Invalid Operation";
await Task.Delay(2000);
Display = tempDisplay;
}

Control.InvokeRequired changing throughout a method

Look at these screenshots. First shows a breakpoint in my code and shows current value of InvokeRequired.
Seconds screenshot shows value of InvokeRequired after pressing F10 (step over) one time in debugger (IF statement content is not entered, ELSE is).
What happens next, is InvalidOperationException on rtbOutput, because I try to change its fields in ReportProgress. Here is how I call OnProgressReported:
public event EventHandler<BuildProgressEventArgs> BuildProgresReported = delegate { };
public void InvokeReportBuildProgress(BuildProgress progress)
{
BuildProgresReported.Invoke(this, new BuildProgressEventArgs(progress));
}
How is this even possible? Is there other, better way to detect if Invoke should be called?
Update:
I have changed method code to the following:
public void OnProgressReported( object caller, BuildProgressEventArgs progressEventArgs )
{
if( rtbOutput.InvokeRequired )
{
Debug.WriteLine($"Invoke was required on thread: #{Thread.CurrentThread.ManagedThreadId} named '{Thread.CurrentThread.Name}'");
rtbOutput.BeginInvoke( new Action( () => OnProgressReported(caller, progressEventArgs) ) );
}
else
{
Debug.WriteLine($"Invoke was NOT required on thread: #{Thread.CurrentThread.ManagedThreadId} named '{Thread.CurrentThread.Name}'");
if ( this.IsDisposed )
{
throw new InvalidOperationException( "This form has been disposed" );
}
rtbOutput.Text = "abc";
rtbOutput.Text = "xxx";
//ReportProgress(progressEventArgs.Progress);
}
}
The only output is
Invoke was NOT required on thread: #15 named ''
and the method this throw an exception on line rtbOutput.Text = "abc";
HOWEVER! When the line rtbOutput.Text = "xxx"; is commented out, everything works fine, even though rtbOutput.Text = "abc"; is still present!
Update #2 - solution:
The problem was a window handle that was not yet created (I subscribe to an event right AFTER creating the window) I have to wait for handle to be created using
this.IsHandleCreated

Button is Clicking Before File Upload is Complete in Webbrowser C#

On a Windows Form, I am using a Webbrowser control in C#. It's job is to upload a file and then press the submit button. My only problem is that my code tries to press the submit button before the file is finished uploading. I tried using:
System.Threading.Thread.Sleep(1000);
In between the two tasks (commented out below). This seems to pause the entire process so that didn't work. Can anyone tell me what the best way to do this is?
private void imageBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.imageBrowser.DocumentCompleted -= imageBrowser_DocumentCompleted;
Populate().ContinueWith((_) =>
{
//MessageBox.Show("Form populated!");
}, TaskScheduler.FromCurrentSynchronizationContext());
//System.Threading.Thread.Sleep(10000);
try
{
var buttons = imageBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement button in buttons)
{
if (button.InnerText == "done")
{
button.InvokeMember("click");
}
}
}
catch
{
//debug
}
}
async Task Populate()
{
var elements = imageBrowser.Document.GetElementsByTagName("input");
foreach (HtmlElement file in elements)
{
if (file.GetAttribute("name") == "file")
{
file.Focus();
await PopulateInputFile(file);
}
}
}
async Task PopulateInputFile(HtmlElement file)
{
file.Focus();
// delay the execution of SendKey to let the Choose File dialog show up
var sendKeyTask = Task.Delay(500).ContinueWith((_) =>
{
// this gets executed when the dialog is visible
SendKeys.Send("C:\\Users\\00I0I_c0OlVXtE6FO_600x450.jpg" + "{ENTER}");
System.Threading.Thread.Sleep(1000);
SendKeys.Send("{ENTER}");
}, TaskScheduler.FromCurrentSynchronizationContext());
file.InvokeMember("Click"); // this shows up the dialog
await sendKeyTask;
// delay continuation to let the Choose File dialog hide
await Task.Delay(500);
//SendKeys.Send("{ENTER}");
}
Is the WebBrowser loading a local file? Can you post the html code as well?
I came across such a situation when I was working with google-maps-api-3. I was setting some markers on the form in the WebBrowser_DocumentCompleted but was getting a null object exception. So I moved the code for set marker to a .NET Button control. I noticed that the exception was not thrown when I set the marker after the map tiles completed loading. DocumentCompleted was firing before the tiles got loaded and I was getting a null object exception.
So what I did was to use a tilesLoaded event in my javascript. In this event, I set a property back in C# code which set the markers in the OnPropertyChanged event.
I know what I am posting here is not a solution. But if you post your html code, I can give you answer with some code.
I solved this. The code I was using to click the button was in the wrong spot. The code now looks like so:
private void imageBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.imageBrowser.DocumentCompleted -= imageBrowser_DocumentCompleted;
try
{
Populate().ContinueWith((_) =>
{
var buttons = imageBrowser.Document.GetElementsByTagName("button");
foreach (HtmlElement button in buttons)
{
if (button.InnerText == "done")
{
button.InvokeMember("click");
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch
{
//debug
}
}
My mistake was thinking in terms of having a certain amount of seconds pass before executed the next line of code, when I should have been thinking in terms of having the next line of code execute when the previous task was complete.

Visual Studio during Debugging: The function evaluation requires all threads to run

I'm suddenly getting a strange error while debugging. Up to now the variable in the watch windows has been showing correctly. Now I am always getting this error message in the watch windows:
The function evaluation requires all threads to run
I am not able to check any variable anymore. I am not explicitly working with threads. What can I do to get it working again?
I already disabled, as mentioned in some forums, the function: "Enable property Evaluation and other implicit function Calls" in the option window of the debugger. But without success, and it gives me this error:
Error Implicit Function evaluation disabled by the user
From the msdn forum:
This isn't an error in and of itself, but more of a feature of your debugger.
Some properties require code to be executed in order for the property to be read, but if this requires cross-thread interaction, then other threads may have to run as well. The debugger doesn't do this automatically, but certainly can, with your permission.
Just click the little evaluate icon and it will run your code and evaluate the property.
For further details on this behaviour check this excelent article
I ran into this issue when just trying to get items from a table called "AGENCY" using Entity Framework:
var agencies = db.AGENCY.OrderBy(e => e.FULLNAME);
Hovering over agencies in debug mode, clicking to expand the options, and clicking Results would give the dreaded "The function evaluation requires all threads to run" with a "Do Not Enter" icon at the end that, on which, clicking did nothing.
2 possible solutions:
Add .ToList() at the end:
var agencies = db.AGENCY_TABLE.OrderBy(e => e.FULLNAME).ToList();
List<AGENCY_TABLE> agencies = db.AGENCY_TABLE.OrderBy(e => e.FULLNAME).ToList();
Credit goes to Hp93 for helping me come to this solution. In the comments on MUG4N's answer where I found this solution, it also mentions trying .Any() instead of .ToList(), but this gives a Boolean instead of a <T>, like <AGENCY> is, so it probably wouldn't help.
Workaround - try a different path in the debug options. I found that I could click on the "Non-Public Members" > "_internalQuery" > ObjectQuery > Results View and get my values that way.
MUG4N has indeed provided a correct answer however if you hover over the line of code in debug, you may be looking at something like the below. If so, click the little re-evaluate icon highlighted in the image below...
NB: I obtained this image by pinning, normally the re-evaluate icone are in the middle of the window and not down the left hand column.
You should make thread safe call because accessing Windows form controls are not Thread safe in multithreading.
This is my simple code which makes Thread safe call and sets Progress bar.
public partial class Form1 : Form
{// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void StringArgReturningVoidDelegate(string text);
private Thread demoThread = null;
public int Progresscount = 0;
static EventWaitHandle waithandler = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
}
public static bool CheckForInternetConnection()
{
try
{
using (var client = new WebClient())
{
using (var stream = client.OpenRead("http://www.google.com"))
{
return true;
}
}
}
catch
{
return false;
}
}
public void Progressincrement()
{
waithandler.WaitOne();
while (CheckForInternetConnection()==true)
{
if (Progresscount==100)
{
break;
}
SetLabel("Connected");
Progresscount += 1;
SetProgress(Progresscount.ToString());
Thread.Sleep(TimeSpan.FromSeconds(1));
}
if (Progresscount <100)
{
Startthread();
}
SetLabel("Completed");
}
public void Startthread ()
{
this.demoThread= new Thread(new ThreadStart(Progressincrement));
this.demoThread.Start();
SetLabel("Waiting for connection");
while (CheckForInternetConnection() == false) ;
waithandler.Set();
}
private void SetLabel(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.label1.InvokeRequired)
{
StringArgReturningVoidDelegate d = new StringArgReturningVoidDelegate(SetLabel);
this.Invoke(d, new object[] { text });
}
else
{
this.label1.Text = text;
}
}
private void SetProgress(string Value)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.progressBar1.InvokeRequired)
{
StringArgReturningVoidDelegate d = new StringArgReturningVoidDelegate(SetProgress);
this.Invoke(d, new object[] {Value});
}
else
{
this.progressBar1.Value = Convert.ToInt32(Value);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Startthread();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Responsive");
}
}
For more information MSDN
This isn't an error, but more of a feature of your debugger.
The debugger doesn't do this automatically, but certainly can, with users permission. Just click the little space icon and it will run the code and evaluate the property.
I use the next workaround to pass:
var OtherThreadField = "";
Invoke(new MethodInvoker(delegate
{
OtherThreadField = ExecuteNeededMEthod();
}));
Now i have a value for OtherThreadField.
I faced the same issue and solved .The Issue arise due to username and password ,in SQL connection there is user and password but in code there no user and password. so I enable the user and the password and the issue solved
For me, this happened when trying to break on a line that accesses a complex object instance contained by a Settings Class.
A breakpoint on the following if results in Settings.Default.FindSettings with the value being "The function evaluation requires all threads to run." If I press the force eval button, it is null. Stepping with the force eval button click or not enters the if block and initializes the object. If I remove the breakpoint and add a new breakpoint following the if block, the Settings.Default.FindSettings deserializes properly with the expected values.
if (Settings.Default.FindSettings == null)
{
Settings.Default.FindSettings = new FindSettings();
}
After trial and error, I added the following code before the above if block to access the settings prior to breaking. This seems to reliably fix the problem. I do not need it in production so I wrap in conditional compiler directive. I have a comment in the code instead of a non-descript discard:
#if DEBUG
var _ = Settings.Default.FindSettings;
#endif
I am not sure if the above line would be optimized out in production since it has side effects. As I only need it while debugging, I have not checked.

AlertControl doesn't show

I have a strange problem with devexpress AlertControl. I create an alertu using this code
AlertInfo alertInfo = new AlertInfo(caption, text);
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(null,alertInfo);
this code is placed in backgroundWorker_DoWork function and it is supposed to display alerts from time to time. The problem is that alerts are not shown. I can see that show method is invoked however alerts are not shown.
Acording to documentation is I pass null as a parametr of Show function , notification should be shown on main monitor.
What can I do to make it work ?
Considering you're using a worker, I guess it's a thread problem. Try wrapping your code inside an Action object:
Action action = () =>
{
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(this, alertInfo); // "this" being a Form
};
this.Invoke(action);
I use a similar code inside a form with good results and once did a similar code using an AlertControl too.
Your AlertControl need a Parent Control.
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(MyForm,alertInfo); //replace null with a Form/Control instance
You call the Show method with a null paramater - where you should have use an instance of a Form/Control
Don't know anything about the devexpress controls, but maybe you have to show the alert from the main thread via invoke methode?
using DevExpress.XtraBars.Alerter;
// Create a regular custom button.
AlertButton btn1 = new AlertButton(Image.FromFile(#"c:\folder-16x16.png"));
btn1.Hint = "Open file";
btn1.Name = "buttonOpen";
// Create a check custom button.
AlertButton btn2 = new AlertButton(Image.FromFile(#"c:\clock-16x16.png"));
btn2.Style = AlertButtonStyle.CheckButton;
btn2.Down = true;
btn2.Hint = "Alert On";
btn2.Name = "buttonAlert";
// Add buttons to the AlertControl and subscribe to the events to process button clicks
alertControl1.Buttons.Add(btn1);
alertControl1.Buttons.Add(btn2);
alertControl1.ButtonClick += new AlertButtonClickEventHandler(alertControl1_ButtonClick);
alertControl1.ButtonDownChanged +=
new AlertButtonDownChangedEventHandler(alertControl1_ButtonDownChanged);
// Show a sample alert window.
AlertInfo info = new AlertInfo("New Window", "Text");
alertControl1.Show(this, info);
void alertControl1_ButtonDownChanged(object sender,
AlertButtonDownChangedEventArgs e) {
if (e.ButtonName == "buttonOpen") {
//...
}
}
void alertControl1_ButtonClick(object sender, AlertButtonClickEventArgs e) {
if (e.ButtonName == "buttonAlert") {
//...
}
}
ref:https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraBarsAlerterAlertControltopic

Categories

Resources