Detect if a TextChanged event wasn't triggered programmatically - winforms

I have a TextChanged event attached to a TextBox in a Windows Form. How to make sure if a particular call to that event wasn't triggered programmatically rather by user interacting with the TextBox?

I would like to extend #rw_'s solution a little. Inside your TextBox event handler,
private void txt_TextChanged(object sender, EventArgs e)
{
if (!(sender is null) &&((TextBox)sender).ContainsFocus)
{
//Code if triggered by Click event
}
else
{
//Code if triggered programmatically
}
}
This will help your program adjust to the case where sender object is not null for some reason.

I am unsure about what your question means. I'll split my answer.
If you want to call the text changed function programmatically and differ when it was called by user interactivity or if it was called programmatically:
Assuming that when you call the function programmatically you pass null on sender and event args txt_TextChanged(null,null);, you could use this solution:
private void txt_TextChanged(object sender, EventArgs e)
{
if(sender == null)
{
// triggered programmatically
}
else
{
// triggered by the user. sender object is the textbox interacted by the user
}
}
If you want to change the text programmatically without triggering the event:
this.txt.TextChanged -= new System.EventHandler(this.txt_TextChanged);
txt.Text = "bar";
this.txt.TextChanged += new System.EventHandler(this.txt_TextChanged);

This is a common problem. You can set a flag on your form before updating the value and then check it in your event handler:
handleEvent = false;
TextBox1.Text = "foo";
handleEvent = true;
Then in your handler, check the flag:
private void TextBox1_TextChanged(object sender, EventArgs e)
{
if(handleEvent)
{
// do stuff
}
}

Related

How to work with BarEditItem and BarCheckItem in RibbonControl Winforms Devexpress ?

I need CheckBox in RibbonControl and if it checked I need to perform some task if not checked I need to perform some other task. So I tried barCheckItem1 It is working properly what I expect but it is Displaying like Button I need exact CheckBox. So again I used barEditItem1 in this Item "CheckChanged" event is not available then if i write code in "EditValueChanged" event, if I check or Uncheck the event not fired. How to complete my task ?
I need CheckBox with CheckedChanged Event.
You have two ways to access the control itself:
One way is:
CheckEdit checkEdit = barEditItem.Edit as CheckEdit;
bool isChecked = checkEdit.Checked;
The other is the repository editor directly:
bool isChecked = repositoryItemCheckedEdit.ValueChecked;
I hope this is helpful.
Is this what you need?
Add a BarEditItem with CheckEdit, attach event CheckedChanged of RepositoryItemCheckEdit. You're done.
private void repositoryItemCheckEdit1_CheckedChanged(object sender, EventArgs e)
{
Console.WriteLine(((CheckEdit) sender).Checked);
}
private void button1_Click(object sender, EventArgs e)
{
bool? ischecked = (bool?)barEditItem1.EditValue;
if(!ischecked.HasValue)
{
//In determinate state
}
else
{
if(ischecked.Value)
{
//Checked
}
else
{
//Not Checked
}
}
}

How to Cancel Datagrid selection changed event in WPF?

I know this question is asked before, but I couldnt find what I am looking for.
private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (oOrdItem.ItemNo == 0)
{
e.Handled = true;
MessageBox.Show("Please save the order item", "Save");
return;
}
}
Even if I call e.Handled = true;
it will select the datagrid row. I dont want to call dataGrid1.SelectedIndex =-1; because it will trigger selectionchanged event again. I also tried dataGrid1.UnSelectAll();
Any other way to cancel the selectionchanged event?
I used a variety of methods to try to cancel the selection changed event, including the method from the selected answer, but none of them worked. This, however, worked great for me:
Using the PreviewMouseDown event-handler for the datagrid:
private void dataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//get the item I am clicking on (replace MyDataClass with datatype in datagrid)
var myItem = (e.OriginalSource as FrameworkElement).DataContext as MyDataClass;
//check if item is different from currently selected item
if (myItem != dataGrid.SelectedItem)
{
//save message dialog
MessageBoxResult result = MessageBox.Show("Changes will be lost. Are you sure?", "Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Question);
//if click no, then cancel the event
if (result == MessageBoxResult.No)
{
e.Handled = true;
}
else
{
//otherwise, reinvoke the click event
dataGrid.Dispatcher.BeginInvoke(
new Action(() =>
{
RoutedEventArgs args = new MouseButtonEventArgs(e.MouseDevice, 0, e.ChangedButton);
args.RoutedEvent = UIElement.MouseDownEvent;
(e.OriginalSource as UIElement).RaiseEvent(args);
}),
System.Windows.Threading.DispatcherPriority.Input);
}
}
}
}
This successfully keeps the current row selected if the user clicks "No", and if they click "Yes", then execution will continue as normal. Hopefully this helps someone in the future, because it took a long time to find something that would work for a seemingly simple problem.
Did you think about an alternative implementation? I'm thinking on Binding and a check method before changing the SelectedItem. An illustration:
<DataGrid ItemsSource="..." SelectedItem="{Binding SelectedEntry}" />
and the underlying VM could look like this:
public class SampleVm : ViewModelBase//assuming that you are using such a base class
{
private object _selectedEntry;
public object SelectedEntry
{
get { return _selectedEntry; }
set
{
if (!SavePrevItem())
return;
_selectedEntry = value;
RaisePropertyChanged("SelectedItem"); // or something similar
}
}
private bool SavePrevItem()
{
// your logic here
}
}

How to stop ResizeEnd event when form is moved?

I write certain code in my form ResizeEnd event. Now problem is when form is moved by clicking and dragging on the caption bar, ResizeEnd event is fired and code is executed even though form size is NOT changed.
I gone through MSDN documentation for Resizeend event and it says that event will fire when form is moved (don't understand why this happens when the size is NOT changed).
For resolution I put the if condition to check if size is changed like below to stop execution of code on form move:
int Prv_Height; int Prv_Width;
private void TemplateGrid_ResizeEnd(object sender, EventArgs e)
{
if (this.Size.Width != Prv_Width || this.Size.Height != Prv_Height)
{
Prv_Width = this.Size.Width;
Prv_Height = this.Size.Height;
//Other code here when form resize ends...
}
}
So is there any way to stop ResizeEnd event to fire when form is moved? or any other better approach to solve the problem?
You could move your check for sizechange to a new baseform. On derived forms the resizeEnd event will then only fire if the size is actually changed.
public partial class CustomForm : Form
{
private Size _prvSize;
public CustomForm()
{
InitializeComponent();
}
protected override void OnShown(EventArgs e)
{
_prvSize = this.Size;
base.OnShown(e);
}
protected override void OnResizeEnd(EventArgs e)
{
if (this.Size == _prvSize)
return;
_prvSize = this.Size;
base.OnResizeEnd(e);
}
}
private void Form1_ResizeBegin(object sender, EventArgs e)
{
oldSize = ClientSize;
}
private Size oldSize = new Size();
private void Form1_ResizeEnd(object sender, EventArgs e)
{
if (oldSize == ClientSize)
return;
//Add Something
}

Determine whether Selector.SelectionChanged event was initiated by a user

Is it possible to determine whether a Selector.SelectionChanged event was initiated by the user or programmatically?
I.e. I need something like a boolean "IsUserInitiated" property that is true only if the SelectionChanged event was raised because the user changed the selection using mouse or keyboard.
Simple work around:
You could create a method that temporarily disables the SelectionChanged event and call it when you need to change the selection programmatically.
private void SelectGridRow( int SelectedIndex )
{
myDataGrid.SelectionChanged -= myDataGrid_SelectionChanged;
myDataGrid.SelectedIndex = SelectedIndex;
// other work ...
myDataGrid.SelectionChanged += myDataGrid_SelectionChanged;
}
This should work in most scenarios:
private void cboStatus_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.cboStatus.IsDropDownOpen)
{
//OPTIONAL:
//Causes the combobox selection changed to not be fired again if anything
//in the function below changes the selection (as in my weird case)
this.cboStatus.IsDropDownOpen = false;
//now put the code you want to fire when a user selects an option here
}
}
This is a problem I have had to work around since WinForms. I was hoping that in WPF they would add a boolean to SelectionChangedEventArgs called something like IsUserInitiated as mentioned in the question. I have most commonly needed this when I want to ignore anything happening while the data is loading and binding to the screen. For example, say I am defaulting a field based on the new value in SelectionChanged BUT I want the user to be able to overwrite this default value, and I only want the user to overwrite it, NOT the application when the screen reloads. I still feel like what I have been doing is hacky, but I will post it because I don't see it mentioned. No fancy tricks, just simple and effective.
1) Create a class level boolean called _loading
private bool _loading;
2) Update the boolean in the method doing the loading
private async Task Load()
{
_loading = true;
//load some stuff
_loading = false;
}
3) Use the boolean whenever you need to
private void SetDefaultValue(object sender, SelectionChangedEventArgs e)
{
if (!_loading) {
//set a default value
}
}
Taken from http://social.msdn.microsoft.com where the user post the same question
I don't think we can distinguish whether a SelectionChanged event was initiated by the user input or programmatically. SelectionChanged event doesn't care that.
Generally, you can always now whether it is initiated programmatically because it's your code that initiates it.
If you use DataBinding to bind the SelectedItem, you can set the NotifyOnSourceUpdated and NotifyOnTargetUpdated properties to True. And you can handle the Binding.SourceUpdated and Binding.TargetUpdated events. In most cases, the change initiated by the user inputs goes from Target to Source. If the change is initiated programmatically, it goes from Source to Target.
I don't know if it can help...
You could use an custom routed event and hook up the appropriate handlers in an behavior like this:
public class UserSelectionChangedEventArgs : RoutedEventArgs
{
public UserSelectionChangedEventArgs( RoutedEvent id, SelectionChangedEventArgs args , bool changedByUser) :base(id)
{
SelectionChangedByUser = changedByUser;
RemovedItems = args.RemovedItems;
AddedItems = args.AddedItems;
}
public bool SelectionChangedByUser { get; set; }
public IList RemovedItems { get; set; }
public IList AddedItems { get; set; }
}
public delegate void UserSelectionChangedEventHandler( object sender, UserSelectionChangedEventArgs e );
public class UserSelectionChangedBehavior : Behavior<Selector>
{
private bool m_expectingSelectionChanged;
public static readonly RoutedEvent UserSelectionChangedEvent = EventManager.RegisterRoutedEvent( "UserSelectionChanged", RoutingStrategy.Bubble, typeof( UserSelectionChangedEventHandler ), typeof( Selector ) );
public static void AddUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
{
( (Selector) d ).AddHandler( UserSelectionChangedEvent, handler );
}
public static void RemoveUserSelectionChangedHandler( DependencyObject d, UserSelectionChangedEventHandler handler )
{
( (Selector) d ).RemoveHandler( UserSelectionChangedEvent, handler );
}
private void RaiseUserSelectionChangedEvent( UserSelectionChangedEventArgs args )
{
AssociatedObject.RaiseEvent( args );
}
protected override void OnAttached()
{
AssociatedObject.PreviewKeyDown += OnKeyDown;
AssociatedObject.PreviewKeyUp += OnKeyUp;
AssociatedObject.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
AssociatedObject.SelectionChanged += OnSelectionChanged;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.PreviewKeyDown -= OnKeyDown;
AssociatedObject.PreviewKeyUp -= OnKeyUp;
AssociatedObject.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp -= OnMouseLeftButtonUp;
AssociatedObject.SelectionChanged -= OnSelectionChanged;
base.OnDetaching();
}
private void OnMouseLeftButtonUp( object sender, MouseButtonEventArgs e )
{
m_expectingSelectionChanged = false;
}
private void OnKeyDown( object sender, KeyEventArgs e )
{
m_expectingSelectionChanged = true;
}
private void OnKeyUp( object sender, KeyEventArgs e )
{
m_expectingSelectionChanged = false;
}
private void OnMouseLeftButtonDown( object sender, MouseButtonEventArgs e )
{
m_expectingSelectionChanged = true;
}
private void OnSelectionChanged( object sender, SelectionChangedEventArgs e )
{
RaiseUserSelectionChangedEvent( new UserSelectionChangedEventArgs( UserSelectionChangedEvent, e, m_expectingSelectionChanged ) );
}
}
In XAML you could just subscribe to the UserSelectionChangedEvent like this:
<ListBox ItemsSource="{Binding Items}" b:UserSelectionChangedBehavior.UserSelectionChanged="OnUserSelectionChanged">
<i:Interaction.Behaviors>
<b:UserSelectionChangedBehavior/>
</i:Interaction.Behaviors>
Handler:
private void OnUserSelectionChanged( object sender, UserSelectionChangedEventArgs e )
{
if(e.SelectionChangedByUser)
{
Console.WriteLine( "Selection changed by user" );
}
else
{
Console.WriteLine( "Selection changed by code" );
}
}
This is just an idea. Probably you won't even need the behavior and just define the attached routed event. But then I have no idea where to store the m_expectingSelectionChanged flag. I also don't know if this works in all cases. But maybe it gives you a starting point.
Usually a Selector has it's selection set/changed when the control is loaded into view. When this happens the IsLoaded property is still false. When a user makes a selection manually the control obviously has to be visible and hence IsLoaded will be true. Try using this property to determine if a change is user initiated or due to the control being loaded.
Why do you want to know?
I have coded many dialogs where I had similar situations - I didn't really want to know that the user used the mouse or keyboard, but I did want a specific behaviour, and I did want effects from triggering some binding to behave the right way.
For most cases I have found that using the MVVM pattern - or at least separating logic from ui - you often avoid those problems.
So for your problem I would try to eliminate the selectionchanged handler and only use bindings - so your state of the gui is based on the model behind and not the wireing of events.
mvvm:
http://en.wikipedia.org/wiki/Model_View_ViewModel
You can check for AddedItems and RemovedItems. If it was initiated by user both properties has an item. If an item was just added via code the RemovedItems list should be empty. So
if (e.AddedItems.Count>0 && e.RemovedItems.Count > 0)
//Initiated by user

How to stop the execution of DialogResult based on a condition?

I'm having a issue with the following scenario on Windows Forms:
I created a Form with two buttons, each button have been assigned with the behaviour DialogResult OK and DialogResult Cancel, respectively.
But based on certain conditions, I need to stop the execution of the OK button. The problem is that if I just made a return like this:
private void btnOk_Click(object sender, EventArgs e)
{
foreach(Control control in tblTable.Controls)
{
if (control.GetType() == typeof(TextBox))
{
if (control.Text.Trim() == "")
{
control.Focus(); return;
}
}
else
{
}
}
}
The dialog result keeps returning the OK answer to the parent form.
I need to stop the execution of the event and not return any answer until the user corrects the info on the form. In other words, the user should be taken back to the form to correct any missing or wrong data.
As Hans Passant mentions in an comment, just set the DialogResult to None!
Like this:
private void btnOk_Click(object sender, EventArgs e)
{
if(ValidationFailed())
{
this.DialogResult = DialogResult.None;
return;
}
//...
}
Personally I wouldn't use DialogResults on buttons in this scenario. I only tend to set the DialogResult when there's only distinct options that do not require any additional logic (i.e. making a custom MessageBox).
What I would do is to just send the DialogResult yourself on success:
private void btnOk_Click(object sender, EventArgs e)
{
if (allIsOK())
{
this.DialogResult = DialogResult.OK;
}
}
Consider tapping into the Forms's Closing event, and use the Cancel property of the event args to cancel form closing.
Here's a web page that discusses the idea; it's VB, but you'll get the idea:
http://www.vbinfozine.com/t_wfdlg.shtml
In Case you have used DialogResult on a button in a child Form (Which is a dialog), and want to return to form with no dialog result use a function on the form_closing() event:
private void ChildForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (notValidated)
{
e.Cancel = true;
}
}

Resources