I am trying to disable some menu strip items while a textbox is displaying "Calculating...". Once that value goes away, I wish to re-enable the menu items. Its purpose is not to interrupt MD5/CRC32 calculations. So far, I've tried various method of code, and have had no luck so far. What's listed below should work, but for some reason it does not. Any help would be appreciated.
// THIS PART WORKS
if (boxMD5.Text.Contains("Calculating") == true)
{
openROMToolStripMenuItem.Enabled = false;
saveROMDataToolStripMenuItem.Enabled = false;
asTXTToolStripMenuItem.Enabled = false;
asHTMLToolStripMenuItem.Enabled = false;
}
// THIS PART DOES NOT WORK
else if (boxMD5.Text.Contains("Calculating") == false)
{
openROMToolStripMenuItem.Enabled = true;
saveROMDataToolStripMenuItem.Enabled = true;
asTXTToolStripMenuItem.Enabled = true;
asHTMLToolStripMenuItem.Enabled = true;
}
I can't quite tell you why the code isn't doing what you expect, but I can make a suggestion that will change your approach and may help achieve your goal at the same time. What you are trying to do shouldn't be to disable the menu when the textbox contains "Calculating" but instead you should disable the menu while the calculations are being performed. From a user/UI perspective, these are the same thing, but the inner-workings of your program know better.
Based on you PasteBin code, try this:
private void openROMToolStripMenuItem_Click(object sender, EventArgs e)
{
//Other code omitted for brevity
if (File.Exists(OpenFileDialog1.FileName))
{
UpdateUI("Calculating...");
backgroundWorker1.RunWorkerAsync(OpenFileDialog1.FileName);
}
//Other code omitted for brevity
}
and
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
UpdateUI(e.Result.ToString());
}
where the new method UpdateUI() looks like this:
void UpdateUI(string hash)
{
var calculating = hash == "Calculating...";
if (!calculating)
{
progressBar1.Value = 0;
}
openROMToolStripMenuItem.Enabled = !calculating;
saveROMDataToolStripMenuItem.Enabled = !calculating;
asTXTToolStripMenuItem.Enabled = !calculating;
asHTMLToolStripMenuItem.Enabled = !calculating;
boxMD5.Text = hash;
}
Also, notice how you are able to just put !calculating in the if statement rather than calculating == false. This is because the value is already true or false so you don't have to compare it to anything to figure that out. The same thing applies to your original code but you don't need it anymore with this approach.
Related
I have several NumericUpDowns looking like this:
public void numericUpDown1_ValueChanged (object sender, EventArgs e){}
Now if one of them reaches a certain Value they all get disabled. I do this, with the following code as follows
if(Value == 0)
{
numericUpDown1.Enabled = false;
numericUpDown2.Enabled = false;
numericUpDown3.Enabled = false;
.....
}
And so on.
My Question is: can I declare an array in my public form or something that includes all the NumericUpDowns I want disabled and then disable them calling the array?
If you are using .NET 3.5 or newer one you can access all controls of type by:
var my controls = Controls.OfType<NumericUpDown>();
And the iterate through:
foreach(var control in controls)
{
if(controls.Where(control => control.Value == 0).Any())
{
control.Enabled = false;
}
}
This question already has answers here:
Is async always asynchronous in C#? [duplicate]
(1 answer)
Do you have to put Task.Run in a method to make it async?
(3 answers)
async method in C# not asynchronous?
(3 answers)
Closed 5 years ago.
I have a TextBox with a TextChanged event that calls a custom event if the text of the textbox represents an existing file. In this event, there is a call to an outside dll that does some processing on the File, which can take upwards of a minute to finish. There is also some post-processing I do, dependent on what result this method returns to me. Currently, this is blocking my UI, which is highly undesirable.
There are essentially 2 "options"/scenarios I see.
Within the custom event, somehow wait for the dll call to finish, before continuing the event, while also keeping the UI free. This seems like the simplest idea from my multithreading-untrained self, but it also conceptually throws red flags at me: Is this even possible given that the custom event itself (called from TextChanged) is on the UI thread?
Throw the entire custom event into it's own thread using Task.Run(). Downside here is that apart from the dll method call, there is quite a good amount of UI elements that are affected by getters/setters after the long method. I could write alternated getters/setters based on the appropriate InvokeRequired, but if there is a more correct way to do this, I'd rather take that approach.
I made a much shorter (although contrived) example project, which shows essentially what I'm after, using option 2 from above:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
comboBox1.Items.Add("Select One...");
comboBox1.Items.Add("Item 1");
comboBox1.Items.Add("Item 2");
Value = 0;
}
public string SetMessage
{
set
{
if (lblInfo.InvokeRequired)
lblInfo.BeginInvoke((MethodInvoker)delegate () { lblInfo.Text = Important ? value + "!" : value; });
else
lblInfo.Text = Important ? value + "!" : value;
}
}
public bool Important
{
get
{
return chkImportant.Checked;
}
set
{
if (chkImportant.InvokeRequired)
chkImportant.BeginInvoke((MethodInvoker) delegate() { chkImportant.Checked = value; });
else
chkImportant.Checked = value;
}
}
public SomeValue Value
{
get
{
if (comboBox1.InvokeRequired)
{
SomeValue v = (SomeValue)comboBox1.Invoke(new Func<SomeValue>(() => SomeValue.Bar));
return v;
}
else
{
switch (comboBox1.SelectedIndex)
{
case 1:
return SomeValue.Foo;
case 2:
return SomeValue.Bar;
default:
return SomeValue.Nothing;
}
}
}
set
{
if (comboBox1.InvokeRequired)
{
comboBox1.BeginInvoke((MethodInvoker)delegate ()
{
switch (value)
{
case SomeValue.Nothing:
comboBox1.SelectedIndex = 0;
break;
case SomeValue.Foo:
comboBox1.SelectedIndex = 1;
break;
case SomeValue.Bar:
comboBox1.SelectedIndex = 2;
break;
}
});
}
else
{
switch (value)
{
case SomeValue.Nothing:
comboBox1.SelectedIndex = 0;
break;
case SomeValue.Foo:
comboBox1.SelectedIndex = 1;
break;
case SomeValue.Bar:
comboBox1.SelectedIndex = 2;
break;
}
}
}
}
private void CustomEvent(object sender, EventArgs e)
{
if (!Important)
Important = true;
SetMessage = "Doing some stuff";
if (Value == SomeValue.Foo)
Debug.WriteLine("Foo selected");
//I don't want to continue until a result is returned,
//but I don't want to block UI either.
if (ReturnsTrueEventually())
{
Debug.WriteLine("True!");
}
Important = false;
SetMessage = "Finished.";
}
public bool ReturnsTrueEventually()
{
//Simulates some long running method call in a dll.
//In reality, I would interpret an integer and return
//an appropriate T/F value based on it.
Thread.Sleep(5000);
return true;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
//Do I *need* to multithread the whole thing?
Task.Run(() => CustomEvent(this, new EventArgs()));
}
}
public enum SomeValue
{
Nothing = 0,
Foo = 100,
Bar = 200
}
Note: I'm not asking for code review on my option 2 code. Rather, I'm asking if option 2 is necessary to accomplish, since that option causes me to change a considerably larger portion of code, given that it's only 1 method within it holding up the entire process.
I also realize I can simplify some of the code in these properties to prevent replication. For the sake of demonstrating to myself and debugging, I am holding off on that at this time.
Here is what I had related to option 1 (left out duplicate code and the getters/setters without their invokes):
private async void CustomEvent(object sender, EventArgs e)
{
if (!Important)
Important = true;
SetMessage = "Doing some stuff";
if (Value == SomeValue.Foo)
Debug.WriteLine("Foo selected");
//I don't want to continue until a result is returned,
//but I don't want to block UI either.
if (await ReturnsTrueEventually())
{
Debug.WriteLine("True!");
}
Important = false;
SetMessage = "Finished.";
}
public async Task<bool> ReturnsTrueEventually()
{
//Simulates some long running method call in a dll.
//In reality, I would interpret an integer and
//return an appropriate T/F value based on it.
Thread.Sleep(5000);
return true;
}
This is basically what you want. I'm violating a couple best-practices here, but just showing it's not that complicated. One thing to keep in mind is that the user can now click this button multiple times in a row. You might consider disabling it before processing. Or you can do a Monitor.TryEnter() to make sure it's not already running.
private async void buttonProcess_Click(object sender, RoutedEventArgs e)
{
textBlockStatus.Text = "Processing...";
bool processed = await Task.Run(() => SlowRunningTask());
}
private bool SlowRunningTask()
{
Thread.Sleep(5000);
return true;
}
See the linked screenshot below.
In short, I need those little white boxes to disappear - they're supposed to house an image, but there is no image, and so I'd rather they disappear.
I've accomplished this using the follow code:
foreach (ToolStripMenuItem menuItem in mnuMain.Items)
((ToolStripDropDownMenu)menuItem.DropDown).ShowImageMargin = false;
This works for what I guess are the main items, but not the sub-items, as you can see in the picture.
I've tried a few variations on the above code to try and get it to capture everything instead of just the first level items, but no luck.
What am I doing wrong?
http://i.imgur.com/bst1i4v.png
You should do that for sub items too. To do so, you can use this code:
private void Form1_Load(object sender, EventArgs e)
{
SetValuesOnSubItems(this.menuStrip1.Items.OfType<ToolStripMenuItem>().ToList());
}
private void SetValuesOnSubItems(List<ToolStripMenuItem> items)
{
items.ForEach(item =>
{
var dropdown = (ToolStripDropDownMenu)item.DropDown;
if (dropdown != null)
{
dropdown.ShowImageMargin = false;
SetValuesOnSubItems(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
}
});
}
This is a modified version of above. Use:
MainMenuStrip.HideImageMargins();
Because the recursive method performs the intended manipulation, I used overloading to make it clearer what is intended. Pattern matching is used because the above sample will throw an exception, not return null.
public static void HideImageMargins([NotNull] this MenuStrip menuStrip)
{
HideImageMargins(menuStrip.Items.OfType<ToolStripMenuItem>().ToList());
}
private static void HideImageMargins([NotNull] this List<ToolStripMenuItem> toolStripMenuItems)
{
toolStripMenuItems.ForEach(item =>
{
if (!(item.DropDown is ToolStripDropDownMenu dropdown))
{
return;
}
dropdown.ShowImageMargin = false;
HideImageMargins(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
});
}
I have a simple application that reverses any text typed to it in another textbox. The catch is, you can modify either textbox and the changes will be (literally) reflected in the other.
I wrote this code, believing for it to cause problems.
private void realText_TextChanged(object sender, EventArgs e)
{
mirrorText.Text = mirror(realText.Text);
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
realText.Text = mirror(mirrorText.Text);
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
I then tried it out, believing that it would cause an infinite loop (realText changes mirrorText, another event happens, mirrorText changes realText, etc). However, nothing except the intended behavior happened.
I'm of course happy about this, I could just leave it here. Or could I?
I'm quite sure the TextChanged event is supposed to be fired whenever Text is changed. Is this intended behavior of some error protection in the events, or was I just lucky? Can this code misbehave on another computer, with other build settings, etc? It can be easily fixed:
private void realText_TextChanged(object sender, EventArgs e)
{
if (realText.Focused)
{
mirrorText.Text = Mirror(realText.Text);
}
}
I'll probably do it anyway to be safe, but is it required to check this? (I'm not even going to ask if it's recommended.)
Per the comments, and as already answered, the TextChanged event is not getting raised when you set the Text property to the value it already has.
It's not clear whether this is something you can safely rely upon. It is a sensible optimisation, and I would be very surprised if future versions of .NET Framework drop it, but I cannot speak for older versions, nor for third-party implementations (Mono).
To be absolutely safe, I would not use the Focused check you put in your question. I would do exactly what the Text setter does now.
private void realText_TextChanged(object sender, EventArgs e)
{
var newMirrorText = Mirror(realText.Text);
if (mirrorText.Text != newMirrorText)
mirrorText.Text = newMirrorText;
}
This has the same advantage of preventing infinite recursion, but plays more nicely with other code you may put in your form that changes the text as a result of some other event.
The reason it doesn't cause a loop is that it checks whether the Text property actually changed, i.e. if the new value does not equal the old value. In your case the mirror function happens to reverse itself, which leads to the same text after two passes.
It's pretty easy to check.
First, replace both textbox controls with
class T : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Second, set the breakpoint on setter. Add these expressions to the Watch window:
Name
Text
value
Third, launch the app, copy '123' from somewhere and paste it to the first textbox. Here it goes:
1st break:
Name: "mirrorText"
Text: ""
value: "321"
2nd break:
Name: "realText"
Text: "123"
value: "123"
3rd... whoops, it does not breaks anymore. To detect why we had to go deeper. Look at referencesource: text box setter does nothing unusual, but TextBoxBase's one looks interesting:
set {
if (value != base.Text) { // Gotcha!
base.Text = value;
if (IsHandleCreated) {
// clear the modified flag
SendMessage(NativeMethods.EM_SETMODIFY, 0, 0);
}
}
}
So, as hvd already answered, the reason is the textbox does not raise TextChanged if old and new values are the same. I don't think the behavior will change, at least for winforms. But if you want more robust solution, here it is:
private void RunOnce(ref bool flag, Action callback)
{
if (!flag)
{
try
{
flag = true;
callback();
}
finally
{
flag = false;
}
}
}
private bool inMirror;
private void realText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
mirrorText.Text = mirror(realText.Text);
});
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
realText.Text = mirror(mirrorText.Text);
});
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
P.S. mirror() will fail on surrogate pairs. Here're some solutions.
If textbox has a Text, and we try to change it with the same Text, the TextChange event is not raising because new text is same as the previous.
In your code, the realText_TextChanged event reverses the text and changes the mirrorText with it.
The mirrorText_TextChanged event reverses the text and try to change the realText.
The realText has already this text and does not raises the realText_TextChanged event.
I have a method that disables all the butttons on my window.
But i can't seem to get the type of Button to match it to the Resource collection
I'm using Expression Blend 3 with a c# code-behind
void DisableButtons()
{
for(int i = 0; i>= this.Resources.Count -1; i ++)
{
if (this.Resources[i].GetType() == typeof(Button))
{
Button btn = (Button)this.Resources[i];
btn.IsEnabled = false;
}
}
}
Update
Thanks for the answers!
Ok The loop is working but my code is incorrect.
this.Resources
Does not seem to include my buttons! This could be an Blend thing?
So Yeah.
I ended up doing it manually. Cause I'm hasty and there isn't a short easy solution. Thanks for all the input though!
void DisableButtons()
{
for(int i = 0; i < Resources.Count; i ++)
{
var btn = Resources[i] as Button;
if(btn != null)
{
btn.IsEnabled = false;
}
}
}
Easy way to implement it is use foreach instruction with LINQ query, but this way need more resuources whan easy for.
void DisableButtons()
{
foreach(var button in Resources.OfType<Button>())
{
button.IsEnabled = false;
}
}
It is possible I misunderstood something, but you're trying to find Buttons CONTAINED in your window in the RESOURCES of your window? Because those two things are different things alltogether.
If that is the case, either try setting this.IsEnabled = false (but that disables other things, not just buttons), or traverse the logical tree (or visual tree if silverlight) with LogicalTreeHelper/VisualTreeHelper, although that is a VERY expensive method.
Manual workaround would be to give names to all your buttons, make a list of them in codebehind and iterate that list.
However the best would be to create a boolean property called AreButtonsEnabled in your ViewModel (if you're not using MVVM than simply on the control itself - but make it DependencyProperty) and bind all your button's IsEnabled property to them! And then in codebehind simply set that boolean to false ... and magic ensues.
If this is not your case then sorry I wasted your time.
what about this?
if (this.Resources[i].GetType() == typeof(Button)))
or even better
if (this.Resources[i] is Button))
What about that?
if (this.Resources[i] is Button)
That way you can get anything that inherits from Button.
It looks like your loop statement is wrong. i>= this.Resources.Count -1 should be i <= this.Resources.Count - 1; It'll never get into your loop.
Also, this is just a style thing, but I'd rewrite it as:
for(int i = 0; i < Resources.Count; i ++)
{
Button btn = Resources[i] as Button; // btn will be null if not a Button
if( btn != null )
{
btn.IsEnabled = false;
}
}
I use a simple method.
First you create a bool.
bool enableButtons = true;
Now I add a timer to the form which is active all the time.
private void timer1_Tick(object sender, EventArgs e)
{
if (enableButtons = false)
{
button1.Enabled = false;
button2.Enabled = false;
}
else
{
button1.Enabled = true;
button2.Enabled = true;
}
}
So whenever I want to disable the buttons, I just change enableButtons to false.