I have a winform with some buttons that are updated in an event handler. The event is fired from a background thread, then the appearance is set via the Invoke method. The buttons will just get enabled or disabled. Something will happen at unpredicable times, though:
The button will not change appearance visually. When it should be enabled, it still looks like it's disabled
Clicking on the "disabled" button still fires the click event - as if its actually enabled underneath
After resizing or moving the form, the component's appearance is set correctly to enabled.
Only the components that are updated in this manner are affected. Other components on the form look/behave fine.
Here is how the button is getting updated in code:
public class Form1 :Form
{
void eventFromThread(object sender, CustomEventArgs e)
{
if(e.enable) RunOnUiThread(ShowEnabledView);
else RunOnUiThread(ShowDisabledView);
}
void ShowEnabledView()
{
button1.Enabled = true;
}
void ShowDisabledView()
{
button1.Enabled = false;
}
void RunOnUiThread(MethodInvoker method)
{
try
{
if(InvokeRequired)
{
Invoke(method);
}
else
method.Invoke();
}
catch(ObjectDisposedException)
{ return;}
catch(InvalidOperationException)
{return;}
}
}
I have tried forcing a refresh on the button, and it hasn't re-occurred yet, but its only been a couple of days. The issue just seems to pop up when it wants to, so I can't really be sure I'm fixing anything. Can anyone shed any light on this?
try calling
System.Windows.Forms.Application.DoEvents()
after you change the button's Enabled property
Related
I'd like to have a TextBlock changed when the button is pressed, and then return to the previous state when the button is released.
It appears that RepeatButton is not a solution here, as it only reacts to itself being held and not released - and I need to know when it is released so that I can run a proper method to return TextBlock to its original state. Being desperate I also tried to loop while(button.IsPressed) (yeah, I know, awful idea :() but to no avail - the code would hang (as if IsPressed did not change to false after the button had been released).
Is there any way to achieve it? Thanks in advance.
Maybe not the cleanest way, but I decided to create multiple handlers to my button: Click, PointerPressed, PointerCancelled, PointerCaptureLost, PointerReleased. First two are for handling the button being pressed, while the last three are for handling the release. I used all three due to recommendation here:
http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.pointerreleased
This is because PointerReleased may sometimes be substituded by other events being fired at button release.
PreviewMouseDown and PreviewMouseUp seem to work fine, if you want both left and right click to have your desired effect:
public partial class MainWindow : Window
{
private string TextBlockPreviousState = "";
public MainWindow()
{
InitializeComponent();
ButtonStatusTextBlock.Text = "foo";
}
private void StoreAndUpdate()
{
TextBlockPreviousState = ButtonStatusTextBlock.Text;
ButtonStatusTextBlock.Text = "Button Down";
}
private void Restore()
{
ButtonStatusTextBlock.Text = TextBlockPreviousState;
}
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
StoreAndUpdate();
}
private void Button_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
Restore();
}
}
Since TabStop does not work on RadioButtons (see linked question), how can I prevent a (WinForm) RadioButton from being tabbed into, but also allow the user to click on the RadioButton, without the tab focus jumping somewhere else.
I've read this and so I thought the following would work:
rbFMV.Enter += (s, e) => focusFirstWorkflowButton();
rbFMV.MouseUp += (s, e) => rbFMV.Focus();
But it doesn't. When I click on the RB, the focus jumps away, and does not come back on Mouse Up.
Any dirty workarounds out there?
Try something like this:
Set TabStop property of the radiobuttons to "false" in the form's constructor. Then attach the following events handlers to the CheckedChanged events of the radiobuttons.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
radioButton1.TabStop = false;
radioButton2.TabStop = false;
}
}
You can attach these event handlers using lambda aswell, as you have shown in your question.
But the important point here is that whenever a radiobutton is checked/unchecked, it's tabstop property is also modified simultaneously. Hence you need to set it to false everytime that event occurs.
The underlying Win32 RadioButton does not automatically change the TabStop property. However, if you use .NET Reflector you can see that the .NET control runs code to update the TabStop property whenever OnEnter method is called because focus has entered the control or whenever the AutoCheck or Checked properties are modified.
Luckily there is a simple solution to your problem. Just derive a new class that overrides the OnTabStopChanged method and automatically set it back to false again. Here is the impl...
public class NonTabStopRadioButton : RadioButton
{
protected override void OnTabStopChanged(EventArgs e)
{
base.OnTabStopChanged(e);
if (TabStop)
TabStop = false;
}
}
Then always use the NonTabStopRadioButton in your application instead of the standard one.
only one control can have input focus at the time i think, so when they click the radio button it will get focus..
But what if you do something like this?
rbFMV.GotFocus += (s, e) => someothercontrol.Focus();
also, have you looked at the TabStop property?
-edit-
i see you have, sorry, missed that :/
I have a windows form with a panel on the left, which consists purely of radiobuttons, and a tabcontrol in the middle, with multiple tab pages within it. Each of these individual tabpages have a series of datagridviews within it, which are shown and hidden depending on which radio button you check.
I accomplish this effect by having each of the radiobuttons on the left assigned a CheckChanged event, which loops through all of the controls within the tabpagecontrol.SelectedTab, and calls .Show() on the corresponding datagridview and calls .Hide() on the rest so that only one datagridview is visible at one time.
My problem occurs when i try to programmatically check one of these RadioButtons. Lets say in Method X, I write RadioButtonA.checked = true. This triggers the usual CheckedChange event handling, which loops through all the datagridviews on the currently selected tabpage and calls .Hide() on everything except the one datagridview form that the radiobutton is supposed to bring up and calls .Show() instead. However, on one of these .Hide() calls on the datagridview, it ends up triggering the RadioButtonA.CheckedChange event AGAIN for a second time. When i look at the sender argument passed to the function, it shows that the sender is the RadioButton i just programmatically clicked on.
I am adding these datagridviews programmatically and can confirm that there are no eventhandlers assigned whatsoever to them. Can anyone help me determine what is causing this additional event to get triggered? Thanks.
For obnoxious change events that trickle through and upset other event handlers on my forms, I've found the only solution is to add a small boolean value:
bool radioIng;
void MyMethod() {
radioIng = true;
try {
radioButton1.Checked = true;
// etc.
} finally {
radioIng = false;
}
}
void radioButton_EventHandler(object sender, EventArgs e) {
if (radioIng) return;
// rest of code here
}
EDIT:
Alternately, you could just remove all of your event handlers and reconnect them later:
void MyMethod() {
try {
radioButton1.CheckChanged -= radioButton_EventHandler;
radioButton2.CheckChanged -= radioButton_EventHandler;
radioButton3.CheckChanged -= radioButton_EventHandler;
// execute your code
radioButton1.Checked = true;
} finally {
radioButton1.CheckedChanged += new EventHandler(radioButton_EventHandler);
radioButton2.CheckedChanged += new EventHandler(radioButton_EventHandler);
radioButton3.CheckedChanged += new EventHandler(radioButton_EventHandler);
}
}
void radioButton_EventHandler(object sender, EventArgs e) {
if (sender == radioButton1) {
// code here to handle
} else if (sender == radioButton2) {
// code here to handle
} else if (sender == radioButton3) {
// code here to handle
}
}
I am writing an IM program, and I have the method to make a form flash and stop flashing... question is, how do I implement it?
When a message arrives, I can set the window flashing, but I need to make sure it doesn't have focus. Checking the focued method always seems to return false and so it flashes even when the form is open.
Also, which event to I need to handle to stop it flashing? When the user clicks the form to make it maximise, or switches focus to the form, I need a way of stopping it.
What's the best way?
You can handle the Activated and Deactivate events of your Form, and use them to change a Form-level boolean that will tell your code whether your form has the focus or not, like this:
private bool _IsActivated = false;
private void Form1_Activated(object sender, EventArgs e)
{
_IsActivated = true;
// turn off flashing, if necessary
}
private void Form1_Deactivate(object sender, EventArgs e)
{
_IsActivated = false;
}
When a message arrives, you check _IsActivated to determine if your Form is already the active window, and turn on flashing if it isn't. In the Activated event, you would turn off the flashing if it's on.
The Focused property of your form will always return false if it has any controls on it. This property refers to whether the control in question (the form, in this case) has the focus within your application's form, not whether the application itself has the focus within Windows.
Checking if the form is minimized or not:
if (this.WindowState == FormWindowState.Minimized)
{
MakeFormFlash();
}
else
{
MakeFormStopFlash();
}
Event to trigger when the form is activated by user or code:
this.Activated += new EventHandler(Form_Activated);
Well Focused should be the property to check, so you need to try and work out why that is always returning false.
As for what event to listen to, probably the GotFocus event, though that may not work until you can work out what is wrong with the Focused property.
There are a number of ways you can handle this. Probably the easiest would be to have a flag that you set whenever the form is flashing so this can be reset on re-activation of the form e.g.
Code for base IM window form
private bool IsFlashing;
....
// Code for IM windows
public void OnActivate(EventArgs e)
{
if (IsFlashing)
{
// stop flash
IsFlashing = false;
}
}
public void Flash()
{
// make flash
IsFlashing = true;
}
Then wherever you do your code to handle the new message you would just need to check that the particular conversation window (if you handle multiple ones) that the message is directed at is the current active one:
public void OnNewMessage(AMessage msg)
{
Form convoWindow = FindConvoWindow(msg.Sender);
if (Form.ActiveForm == convoWindow)
{
// update the conversation text
}
else
{
convoWindow.Flash();
}
}
I use C#.
I have a Windows Form with an edit box and a Cancel button. The edit box has code in validating event. The code is executed every time the edit box loses focus. When I click on the Cancel button I just want to close the form. I don't want any validation for the edit box to be executed. How can this be accomplished?
Here is an important detail: if the validation fails, then
e.Cancel = true;
prevents from leaving the control.
But when a user clicks Cancel button, then the form should be closed no matter what. how can this be implemented?
If the validation occurs when the edit box loses focus, nothing about the the cancel button is going to stop that from happening.
However, if the failing validation is preventing the cancel button from doing its thing, set the CausesValidation property of the button to false.
Reference: Button.CausesValidation property
Obviously CausesValidation property of the button has to be set to false and then the validating event will never happen on its click. But this can fail if the parent control of the button has its CausesValidation Property set to true. Most of the time developers misses/forgets to change the CausesValidation property of the container control (like the panel control). Set that also to False. And that should do the trick.
I was having problems getting my form to close, since the validation of certain controls was stopping it. I had set the control.CausesValidation = false for the cancel button and all the parents of the cancel button. But still was having problems.
It seemed that if the user was in the middle of editing a field that was using validation and just decided to give up (leaving the field with an invalid input), the cancel button event was being fired but the window would not close down.
This was fixed by the following in the cancel button click event:
private void btnCancel_Click(object sender, EventArgs e)
{
// Stop the validation of any controls so the form can close.
AutoValidate = AutoValidate.Disable;
Close();
}
Set the CausesValidation property of the Cancel button to false.
Set the CausesValidation property to false.
None of these answers quite did the job, but the last answer from this thread does. Basically, you need to:
Insure that the Cancel button (if any) has .CausesValidation set to false
Override this virtual method.
protected override bool ProcessDialogKey(Keys keyData) {
if (keyData == Keys.Escape) {
this.AutoValidate = AutoValidate.Disable;
CancelButton.PerformClick();
this.AutoValidate = AutoValidate.Inherit;
return true;
}
return base.ProcessDialogKey(keyData);
}
I didn't really answer this, just pointing to the two guys who actually did.
Setting CausesValidation to false is the key, however this alone is not enough. If the buttons parent has CausesValidation set to true, the validating event will still get called. In one of my cases I had a cancel button on a panel on a form, so I had to set CausesValidation = false on the panel as well as the form. In the end I did this programatically as it was simpler than going through all the forms...
Control control = cancelButton;
while(control != null)
{
control.CausesValidation = false;
control = control.Parent;
}
In my case, in the form I set the property AutoValidate to EnableAllowFocusChange
By using Visual Studio wizard you can do it like that:
Judicious use of the Control.CausesValidation property will help you achieve what you want.
Just above the validation code on the edit box add:
if (btnCancel.focused)
{
return;
}
That should do it.
In complement of the answer of Daniel Schaffer: if the validation occurs when the edit box loses focus, you can forbid the button to activate to bypass local validation and exit anyway.
public class UnselectableButton : Button
{
public UnselectableButton()
{
this.SetStyle(ControlStyles.Selectable, false);
}
}
or if you use DevExpress:
this.simpleButtonCancel.AllowFocus = false;
Note that doing so will change the keyboard experience: the tab will focus anymore on the cancel button.
Maybe you want to use BackgroundWorker to give little bit delay, so you can decide whether validation should run or not. Here's the example of avoiding validation on form closing.
// The flag
private bool _isClosing = false;
// Action that avoids validation
protected override void OnClosing(CancelEventArgs e) {
_isClosing = true;
base.OnClosing(e);
}
// Validated event handler
private void txtControlToValidate_Validated(object sender, EventArgs e) {
_isClosing = false;
var worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
// Do validation on complete so you'll remain on same thread
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
if (!_isClosing)
DoValidationHere();
}
// Give a delay, I'm not sure this is necessary cause I tried to remove the Thread.Sleep and it was still working fine.
void worker_DoWork(object sender, DoWorkEventArgs e) {
Thread.Sleep(100);
}
This is an old question however I recently ran into this issue and solved it this way:
1st, we are loading a UserControl into a 'shell' Form that has a save and cancel button. The UserControl inherit an interface (like IEditView) that has functions for Save, Cancel, Validate and ToggleValidate.
In the shell form we used the mouse enter and mouse leave like so:
private void utbCancel_MouseEnter(object sender, EventArgs e)
{
((Interface.IEdit)tlpMain.Controls[1]).ToggleValidate();
}
private void utbCancel_MouseLeave(object sender, EventArgs e)
{
((Interface.IEdit)tlpMain.Controls[1]).ToggleValidate();
}
Then in ToggleValidate (Say a simple form with two controls...you can always just loop through a list if you want) we set the CausesValidation
public bool ToggleValidate()
{
uneCalcValue.CausesValidation = !uneCalcValue.CausesValidation;
txtDescription.CausesValidation = !txtDescription.CausesValidation;
return txtDescription.CausesValidation;
}
Hope this helps.
I found this thread today while investigating why my form would not close when a validation error occurred.
I tried the CausesValidation = false on the close button and on the form itself (X to close).
Nothing was working with this complex form.
While reading through the comments I spotted one that appears to work perfectly
on the form close event , not the close button (so it will fire when X is clicked also)
This did the trick.
AutoValidate = AutoValidate.Disable;
Create a bool:
bool doOnce;
Set it to false in your function and then:
if (doOnce == false)
{
e.cancel = true;
doOnce = true;
}
This means it will only run once and you should be able to cancel it. This worked for me anyways.
This work for me.
private void btnCancelar_MouseMove(object sender, MouseEventArgs e)
{
foreach (Control item in Form.ActiveForm.Controls)
{
item.CausesValidation = false;
}
}