Using event in WPF - wpf

I have two textboxes in WPF. named txt1 and txt2.
In the lostFocus of txt1 I write
If txt1.Text is nothing then
txt1.Focus
End If
In the lostFocus event of txt2 I write
If txt2.Text is nothing then
txt2.Focus
End If
Now, If txt1 and txt2 are both empty and user presses TAB key in txt1 the problem occurs. Program goes in infinite loop. I mean cursor comes to txt1 and goes to txt2 infinite times.I know This is normal behavior according to my code.
So I want to have validating event to avoid the problems like above. But I cannot find one in WPF.
So which event should I use?

I am not a VB coder so can't write exact code for you but here is what you should do. Add event handler for event PreviewLostKeyboardFocus. inside the event handler set e.Handled to true if the text is empty.
Sample C# code. I have writter a generic handler.
private void TextBox_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (string.IsNullOrEmpty((sender as TextBox).Text))
{
e.Handled = true;
}
}

A better solution might be to allow the user to navigate away from the empty/null text box but either revert the text to the initial value (if there was one) or provide a validation error. Providing validation errors is relatively easy with IDataErrorInfo.
As a software user myself I get annoyed when an application prevents me from navigating away from a field.
Reset Value Approach
See this stackoverflow appraoch for how to maintain and get the previous value. In the LostFocus event you can set your member variable back to _oldValue if the current value is invalid.
determine a textbox's previous value in its lost focused event? WPF
Validation Approach
Those two dates are stored in a model or a view model class. In that class implement IDataErrorInfo (http://msdn.microsoft.com/en-us/library/system.componentmodel.idataerrorinfo(v=vs.95).aspx). Then in your xaml you can show the validation errors.
//This is your model/viewmodel validation logic
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "FirstName")
{
if (string.IsNullOrEmpty(FirstName))
result = "Please enter a First Name";
}
if (columnName == "LastName")
{
if (string.IsNullOrEmpty(LastName))
result = "Please enter a Last Name";
}
if (columnName == "Age")
{
if (Age < = 0 || Age >= 99)
result = "Please enter a valid age";
}
return result;
}
}
//Here is a sample of a xaml text block
<textbox x:Name="tbFirstName" Grid.Row="0" Grid.Column="1" Validation.Error="Validation_Error" Text="{Binding UpdateSourceTrigger=LostFocus, Path=FirstName, ValidatesOnDataErrors=true, NotifyOnValidationError=true}" />
You can also look at these other StackOverflow posts-
What is IDataErrorInfo and how does it work with WPF?
IDataErrorInfo notification

Related

WPF datagrid validation inconsistency

I've got a DataGrid that I'm binding to an ObservableCollection of "Customer" classes, implementing IDataErrorInfo. One of the properties on the Customer class is an int, and in my IDataErrorInfo implementation I check that it's within a valid range, e.g.:-
public class Customer : IDataErrorInfo
{
public int PercentDiscount { get; set; }
... other properties & methods removed for clarity
public string this[columnName]
{
get
{
if (PercentDiscount < 0 || PercentDiscount > 10)
return "Percent Discount is invalid";
}
}
}
In my XAML code-behind I handle a couple of events. In the PreparingCellForEdit event I store a reference to the row being edited:-
private void DataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
_rowBeingEdited = e.Row;
}
Then in the RowEditEnding event, I take some action if the row is in an invalid state (in my case I revert the Customer properties back to their previous "good" values):-
private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
if (_rowBeingEdited != null)
{
var errors = Validation.GetErrors(_rowBeingEdited);
if (errors.Count > 0)
{
.. do something
}
}
}
This works fine if the user enters a numeric value that fails my validation rule, but if the user enters a non-numeric value then the RowEditEnding event never fires and the cell remains in an edit state. I assume it's because WPF fails to bind the non-numeric value to the int property. Is there any way I can detect/handle when this happens?
Last resort is to change the PercentDiscount property to a string, but I'm trying to avoid going down this route.
Edit - I've just found that I can successfully handle both types of error using the CellEditEnding event instead of RowEditEnding. A new problem has appeared though - if I enter an invalid value into the cell then press Enter, the underlying property doesn't get updated, so when CellEditEnding fires Validation.GetErrors is empty. The end result is that the row leaves edit mode but still shows the invalid value in the cell with red border. Any idea what's going on now?
This may not be much of an answer especially since you already mentioned it, but I've fought with DataGrid validation for a while and ended up resorting to making my backing values be strings. You'll notice in the output window of the debugger that a binding or conversion exception happens when you type an alpha character into a DataGridColumn bound to an int.
You can get different behavior by changing the UpdateSourceTrigger, or by putting a converter in between the binding and the property, but I never got exactly what I needed until I backed the values with strings.
I suppose you could also try creating your own DataGridNumericColumn derived from DataGridTextColumn and maybe you'd have more control over the binding/validation behavior.
I struggled to find a good solution for this, but I saw some other people messing with the CellEditEnding event and I ended up, coming up with this code to revert values if they fail conversion:
private void CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (e.EditingElement is TextBox)
{
var cellTextBox = (TextBox)e.EditingElement;
var cellTextBoxBinding = cellTextBox.GetBindingExpression(TextBox.TextProperty);
if (cellTextBoxBinding != null && !cellTextBoxBinding.ValidateWithoutUpdate())
{
cellTextBoxBinding.UpdateTarget();
}
}
}
Calling ValidateWithoutUpdate on the editing elements binding returns false if the conversion of the value fails, then calling the UpdateTarget forces the value to be reverted to the current 'model' value.

WPF DataGrid - how to stop user proceeding if there are invalid cells? (MVVM)

I'm implementing edit functionality in my DataGrid. The CellEditingTemplate of a typical cell looks something like this:-
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Grid.Column="0"
Text="{Binding Concentration, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=LostFocus}"
Validation.ErrorTemplate="{StaticResource errorTemplate}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
In this example there is a TextBox bound to a property called "Concentration", which is a property of type double, with validation to ensure that it falls within a given range. The models that are being bound to the grid implement IDataErrorInfo by the way.
The problem is, although the error template highlights the cell when an invalid value is entered, there is nothing to stop me from moving focus away from that cell (or row). What's the preferred approach for handling validation with data grid editing? I wondered if I could prevent the user from leaving the cell until a valid value was entered, but that could be dangerous - they wouldn't be able to do anything (even close the app) until a valid value had been entered.
Another option might be to prevent the user from proceeding if the datagrid contains any invalid rows. But what's the best way to test this? IDataErrorInfo doesn't provide an "IsValid" property that I can examine on each of the rows' models.
Lastly I could just revert the model's property back to its previous "good" value (or a default value) if the user enters something invalid.
Any suggestions? I'm using MVVM by the way.
I use this to see if IDataerrorInfo has any errors for the object, a small snippet of the implementation:
protected Dictionary<string, string> _propertyErrors = new Dictionary<string, string>();
public bool HasErrors {
get { return (_propertyErrors.Count) > 0; }
}
Then I can handle the logic for what to do after evaluating this property. Do you want to prevent navigation, closing app, etc. Then you need to evaluate for errors from that code and then cancel that action.
I've used this method in the past to determine if a datagrid has errors:
private bool HasError(DataGrid dg,)
{
bool errors = (from c in
(from object i in dg.ItemsSource
select dg.ItemContainerGenerator.ContainerFromItem(i))
where c != null
select Validation.GetHasError(c)
).FirstOrDefault(x => x);
return errors;
}
Then it's only a matter of preventing the next action if the method returns true.

How to validate textboxes in C#.net winforms

I have a form where I need to validate the textboxes like Firstname, Middlename, Lastname, emailId, date, mobilenumber. The validation should happen when a user starts typing in the textbox. errorprovider should show message under textbox if a user enters numbers in place of text and text in place of numbers. I got to know about implicit validation and explicit validation but I feel better to use implicit validation only because its on time error provider when user looses focus on text box or if he shifts to another textbox. I've posed this kind of question with a explicit validation code but no one responded me. So Im making it simple to get help. Do not think I havent done enough research before posting this question.
If you have a very specific validation to do, Marc's answer is correct. However, if you only ensure the "enter number instead of letters" or "enter letters instead of numbers" thing, a MaskedTextBox would do the job better than you (user wouldn't be able to answer incorrect data, and you can still warn them by handling the MaskInputRejected event)
http://msdn.microsoft.com/en-us/library/kkx4h3az(v=vs.100).aspx
You should take a look to the TextChanged Event in of your textbox.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.textchanged.aspx
This event is raised if the Text property is changed by either a
programmatic modification or user interaction.
I would do something like this
private void TextBoxExample_TextChanged(object sender, EventArgs e)
{
TextBox box = sender as TextBox;
if (box.Text.Contains("Example"))
{
LabelError.Text = "Error";
}
else
{
LabelError.Text = string.Empty;
}
}
Hope it helps :)
You can also use keyPressEvent to Avoid the entering the numerical values in the textboxes
it will not allow the numerical chars in the text box
private void textboxName_KeyPress(object sender, KeyPressEventArgs e)
{
//not allowing the non character values
if (!char.IsLetter(e.KeyChar) && !char.IsControl(e.KeyChar) && !(e.KeyChar == (char)Keys.Back) && !(e.KeyChar == (char)Keys.Left) && !(e.KeyChar == (char)Keys.Right) && !(e.KeyChar == (char)Keys.Space) && !char.IsPunctuation(e.KeyChar))
{
e.Handled = true;
}
}

How can I undo a TextBox's text changes caused by a binding?

I have a TextBox to which i bound a string, if i now edit the text manually i will be able to undo those changes via TextBox.Undo(), if however i change the string and the TextBox's text is updated, i cannot undo those changes and the TextBox.CanUndo property will always be false.
I suppose this might have to do with the complete replacement of the text rather than a modification of it.
Any ideas on how i can get this to work?
I was facing the same issue (needed to accept input upon Enter and revert to original value upon Escape) and was able to handle it this way:
Set UpdateSourceTrigger of your TextBox.Text binding to Explicit.
Handle KeyDown event of your TextBox and put the following code in there:
if (e.Key == Key.Enter || e.Key == Key.Escape)
{
BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
if (e.Key == Key.Enter)
{
if (be != null) be.UpdateSource();
}
else if (e.Key == Key.Escape)
{
if (be != null) be.UpdateTarget(); //cancels newly supplied value and reverts to the original value
}
}
I found this solution to be very elegant because it can be used in DataTemplates too. For example in my case I used it to allow in-place editing of ListBox items.
OK, started to leave a comment and realized it was an answer :)
TextBox.Undo() is intended to undo a user's interaction with the text box not a value change in the property it's bound to. A change in the property the text box is bound to will just update the value of the TextBox, this is a different change than a user edit via focus/keyboard. If you need to Undo changes to your bound properties you probably need to investigate adding an Undo/Redo stack to your application.
Assign directly to the TextBox:
textBox.SelectAll();
textBox.SelectedText = newText;
The TextBox will apply the changes to the internal undo stack if they are applied in such a way that they appear to have come from the user, like so:
Clipboard.SetText("NewTextHere");
TextBox.Paste();
It's a terrible workaround, as it kills whatever the user has on the clipboard (the restoring of which is pessimistically discussed here: How do I backup and restore the system clipboard in C#?) but I thought it might be worth having posted nonetheless.
So, I think the ViewModel Undo/Redo article is a good one, but it's as much as about the ViewModel pattern as it is about how to write custom Undo/Redo functionality. Also, in response to confusedGeek, I think there could be examples where undoing changes in your model, not just in your individual controls is appropriate (say you had a textbox and a slider both bound to the sample property, you want to undo a change regardless of which control made it, so we're talking about app level undo instead of control level).
So given that, here is a simple, if not somewhat kludgey example of doing precisely what you ask using a CommandBinding and a simplistic undo stack:
public partial class MainWindow : Window
{
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(String), typeof(MainWindow), new UIPropertyMetadata(""));
// The undo stack
Stack<String> previousStrings = new Stack<String>();
String cur = ""; // The current textbox value
Boolean ignore = false; // flag to ignore our own "undo" changes
public String MyString
{
get { return (String)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public MainWindow()
{
InitializeComponent();
this.LayoutRoot.DataContext = this;
// Using the TextChanged event to add things to our undo stack
// This is a kludge, we should probably observe changes to the model, not the UI
this.Txt.TextChanged += new TextChangedEventHandler(Txt_TextChanged);
// Magic for listening to Ctrl+Z
CommandBinding cb = new CommandBinding();
cb.Command = ApplicationCommands.Undo;
cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
};
cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e)
{
if (previousStrings.Count > 0)
{
ignore = true;
this.Txt.Text = previousStrings.Pop();
ignore = false;
}
e.Handled = true;
};
this.CommandBindings.Add(cb);
}
void Txt_TextChanged(object sender, TextChangedEventArgs e)
{
if (!ignore)
{
previousStrings.Push(cur);
}
cur = this.Txt.Text;
}
private void SetStr_Click(object sender, RoutedEventArgs e)
{
this.MyString = "A Value";
}
}
And here is the XAML:
<Window x:Class="TestUndoBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Name="LayoutRoot">
<TextBox Name="Txt" Text="{Binding Path=MyString, Mode=TwoWay}" />
<Button Name="SetStr" Click="SetStr_Click">Set to "A Value"</Button>
</StackPanel>
</Window>
In this example the behavior is slightly different than typical TextBox undo behavior because 1) I'm ignoring selection, and 2) I'm not grouping multiple keystrokes into a single undo step, both of which are things you would want to consider in a real app, but should be relatively straightforward to implement yourself.

WPF Databind Before Saving

In my WPF application, I have a number of databound TextBoxes. The UpdateSourceTrigger for these bindings is LostFocus. The object is saved using the File menu. The problem I have is that it is possible to enter a new value into a TextBox, select Save from the File menu, and never persist the new value (the one visible in the TextBox) because accessing the menu does not remove focus from the TextBox. How can I fix this? Is there some way to force all the controls in a page to databind?
#palehorse: Good point. Unfortunately, I need to use LostFocus as my UpdateSourceTrigger in order to support the type of validation I want.
#dmo: I had thought of that. It seems, however, like a really inelegant solution for a relatively simple problem. Also, it requires that there be some control on the page which is is always visible to receive the focus. My application is tabbed, however, so no such control readily presents itself.
#Nidonocu: The fact that using the menu did not move focus from the TextBox confused me as well. That is, however, the behavior I am seeing. The following simple example demonstrates my problem:
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider x:Key="MyItemProvider" />
</Window.Resources>
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Save" Click="MenuItem_Click" />
</MenuItem>
</Menu>
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA}" />
<TextBox Text="{Binding ValueB}" />
</StackPanel>
</DockPanel>
</Window>
using System;
using System.Text;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication2
{
public partial class Window1 : Window
{
public MyItem Item
{
get { return (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance as MyItem; }
set { (FindResource("MyItemProvider") as ObjectDataProvider).ObjectInstance = value; }
}
public Window1()
{
InitializeComponent();
Item = new MyItem();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(string.Format("At the time of saving, the values in the TextBoxes are:\n'{0}'\nand\n'{1}'", Item.ValueA, Item.ValueB));
}
}
public class MyItem
{
public string ValueA { get; set; }
public string ValueB { get; set; }
}
}
I found that removing the menu items that are scope depended from the FocusScope of the menu causes the textbox to lose focus correctly. I wouldn't think this applies to ALL items in Menu, but certainly for a save or validate action.
<Menu FocusManager.IsFocusScope="False" >
Assuming that there is more than one control in the tab sequence, the following solution appears to be complete and general (just cut-and-paste)...
Control currentControl = System.Windows.Input.Keyboard.FocusedElement as Control;
if (currentControl != null)
{
// Force focus away from the current control to update its binding source.
currentControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
currentControl.Focus();
}
This is a UGLY hack but should also work
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox != null)
{
focusedTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
This code checks if a TextBox has focus... If 1 is found... update the binding source!
Suppose you have a TextBox in a window, and a ToolBar with a Save button in it. Assume the TextBox’s Text property is bound to a property on a business object, and the binding’s UpdateSourceTrigger property is set to the default value of LostFocus, meaning that the bound value is pushed back to the business object property when the TextBox loses input focus. Also, assume that the ToolBar’s Save button has its Command property set to ApplicationCommands.Save command.
In that situation, if you edit the TextBox and click the Save button with the mouse, there is a problem. When clicking on a Button in a ToolBar, the TextBox does not lose focus. Since the TextBox’s LostFocus event does not fire, the Text property binding does not update the source property of the business object.
Obviously you should not validate and save an object if the most recently edited value in the UI has not yet been pushed into the object. This is the exact problem Karl had worked around, by writing code in his window that manually looked for a TextBox with focus and updated the source of the data binding. His solution worked fine, but it got me thinking about a generic solution that would also be useful outside of this particular scenario. Enter CommandGroup…
Taken from Josh Smith’s CodeProject article about CommandGroup
Simple solution is update the Xaml code as shown below
<StackPanel DataContext="{Binding Source={StaticResource MyItemProvider}}">
<Label Content="Enter some text and then File > Save:" />
<TextBox Text="{Binding ValueA, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding ValueB, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
I've run into this issue and the best solution I've found was to change the focusable value of the button (or any other component such as MenuItem) to true:
<Button Focusable="True" Command="{Binding CustomSaveCommand}"/>
The reason it works, is because it forces the button to get focused before it invokes the command and therefore makes the TextBox or any other UIElement for that matter to loose their focus and raise lost focus event which invokes the binding to be changed.
In case you are using bounded command (as I was pointing to in my example), John Smith's great solution won't fit very well since you can't bind StaticExtension into bounded property (nor DP).
Have you tried setting the UpdateSourceTrigger to PropertyChanged? Alternatively, you could call the UpdateSOurce() method, but that seems like a bit overkill and defeats the purpose of TwoWay databinding.
Could you set the focus somewhere else just before saving?
You can do this by calling focus() on a UI element.
You could focus on whatever element invokes the "save". If your trigger is LostFocus then you have to move the focus somewhere. Save has the advantage that it isn't modified and would make sense to the user.
Since I noticed this issue is still a pain in the ass to solve on a very generic way, I tried various solutions.
Eventually one that worked out for me:
Whenever the need is there that UI changes must be validated and updated to its sources (Check for changes upon closeing a window, performing Save operations, ...), I call a validation function which does various things:
- make sure a focused element (like textbox, combobox, ...) loses its focus which will trigger default updatesource behavior
- validate any controls within the tree of the DependencyObject which is given to the validation function
- set focus back to the original focused element
The function itself returns true if everything is in order (validation is succesful) -> your original action (closeing with optional asking confirmation, saveing, ...) can continue. Otherwise the function will return false and your action cannot continue because there are validation errors on one or more elements (with the help of a generic ErrorTemplate on the elements).
The code (validation functionality is based on the article Detecting WPF Validation Errors):
public static class Validator
{
private static Dictionary<String, List<DependencyProperty>> gdicCachedDependencyProperties = new Dictionary<String, List<DependencyProperty>>();
public static Boolean IsValid(DependencyObject Parent)
{
// Move focus and reset it to update bindings which or otherwise not processed until losefocus
IInputElement lfocusedElement = Keyboard.FocusedElement;
if (lfocusedElement != null && lfocusedElement is UIElement)
{
// Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions)
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Keyboard.ClearFocus();
}
if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible)
return true;
// Validate all the bindings on the parent
Boolean lblnIsValid = true;
foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent))
{
if (BindingOperations.IsDataBound(Parent, aDependencyProperty))
{
// Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated
BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty);
if (lbindingExpressionBase != null)
{
lbindingExpressionBase.ValidateWithoutUpdate();
if (lbindingExpressionBase.HasError)
lblnIsValid = false;
}
}
}
if (Parent is Visual || Parent is Visual3D)
{
// Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs)
Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent);
for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++)
if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex)))
lblnIsValid = false;
}
if (lfocusedElement != null)
lfocusedElement.Focus();
return lblnIsValid;
}
public static List<DependencyProperty> GetAllDependencyProperties(DependencyObject DependencyObject)
{
Type ltype = DependencyObject.GetType();
if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName))
return gdicCachedDependencyProperties[ltype.FullName];
List<DependencyProperty> llstDependencyProperties = new List<DependencyProperty>();
List<FieldInfo> llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList();
foreach (FieldInfo aFieldInfo in llstFieldInfos)
llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty);
gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties);
return llstDependencyProperties;
}
}
The easiest way is to set the focus somewhere.
You can set the focus back immediately, but setting the focus anywhere will trigger the LostFocus-Event on any type of control and make it update its stuff:
IInputElement x = System.Windows.Input.Keyboard.FocusedElement;
DummyField.Focus();
x.Focus();
Another way would be to get the focused element, get the binding element from the focused element, and trigger the update manually. An example for TextBox and ComboBox (you would need to add any control type you need to support):
TextBox t = Keyboard.FocusedElement as TextBox;
if ((t != null) && (t.GetBindingExpression(TextBox.TextProperty) != null))
t.GetBindingExpression(TextBox.TextProperty).UpdateSource();
ComboBox c = Keyboard.FocusedElement as ComboBox;
if ((c != null) && (c.GetBindingExpression(ComboBox.TextProperty) != null))
c.GetBindingExpression(ComboBox.TextProperty).UpdateSource();
What do you think about this? I believe I've figured out a way to make it a bit more generic using reflection. I really didn't like the idea of maintaining a list like some of the other examples.
var currentControl = System.Windows.Input.Keyboard.FocusedElement;
if (currentControl != null)
{
Type type = currentControl.GetType();
if (type.GetMethod("MoveFocus") != null && type.GetMethod("Focus") != null)
{
try
{
type.GetMethod("MoveFocus").Invoke(currentControl, new object[] { new TraversalRequest(FocusNavigationDirection.Next) });
type.GetMethod("Focus").Invoke(currentControl, null);
}
catch (Exception ex)
{
throw new Exception("Unable to handle unknown type: " + type.Name, ex);
}
}
}
See any problems with that?
Using BindingGroup will help to understand and mitigate this kind of problem.
Sometimes we consider to apply MVVM model against WPF data bindings.
For example, we consider about mail's subject property:
<TextBox x:Name="SubjectTextBox" Text="{Binding Subject}" />
TextBox SubjectTextBox is on side of View.
The bound property like ViewModel.Subject will belong to ViewModel.
The problem is that changes remain to View in this case.
When we close the WPF window, WPF TextBox won't loose focus on window close.
It means data binding won't perform writing back, and then changes are lost silently.
Introducing of BindingGroup helps to control whether we should apply changes: from View to ViewModel.
BindingGroup.CommitEdit(); will ensure apply changes of direction View → ViewModel
BindingGroup.CancelEdit(); will ensure to discard changes on View.
If you don't call neither, changes are lost silently!
In the following sample, we attach RibbonWindow_Closing event handler so that we can deal with this case of problem.
XAML:
<R:RibbonWindow Closing="RibbonWindow_Closing" ...>
<FrameworkElement.BindingGroup>
<BindingGroup />
</FrameworkElement.BindingGroup>
...
</R:RibbonWindow>
C#
private void RibbonWindow_Closing(object sender, CancelEventArgs e) {
e.Cancel = !NeedSave();
}
bool NeedSave() {
if (!BindingGroup.CommitEdit()) {
// There may be validation error.
return false; // changes this to true to allow closing.
}
// Insert your business code to check modifications.
// return true; if Saved/DontSave/NotChanged
// return false; if Cancel
}
It should work.

Resources