Framework: Silverlight 4
I have a simple ChildWindow with Username TextBox and Password PasswordBox. I have attached an event handler to the window's KeyDown event.
private void onKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
cancelButtonClick(null, null);
if (e.Key == Key.Enter)
okButtonClick(null, null);
}
What I'm trying to achieve is that when the user presses Enter key on the keyboard, the program will behave as if the user had clicked the OK button.
The problem is the validation.
The default behavior for Silverlight's PasswordBox is to perform validation when the control loses focus. My PasswordBox is bound to some User object. When I click the Enter button, the event handler gets called, which in turn calls the okButtonClick(null, null). The problem is that the PasswordBox has not yet lost the focus at that time, so the user.Password property, which the PasswordBox is bound to, is still empty.
I've tried to place btnOK.Focus() before the okButtonClick(null, null) but to no avail.
How to set the binding so that the control will update binding on every text change instead of on the LostFocus event? What is the right way to achieve what I need?
Use PasswordChanged event for Passwordbox.
Textbox has TextboxChanged event.
I have solved the problem by stumbling upon this solution.
It basicly uses attached property to subscribe to the TextChanged/PasswordChnaged events, and then in the event it updates the binding source. After adapting the solution to my needs, here is what I got (and it works flawlessly):
public class BindingHelper
{
public static readonly DependencyProperty RefreshOnChangeProperty =
DependencyProperty.RegisterAttached("RefreshOnChange", typeof(bool), typeof(BindingHelper),
new PropertyMetadata(false, OnRefreshOnChangeChanged));
public static void SetRefreshOnChange(DependencyObject o, bool value)
{
o.SetValue(RefreshOnChangeProperty, value);
}
public static bool GetRefreshOnChange(DependencyObject o)
{
return (bool)o.GetValue(RefreshOnChangeProperty);
}
private static void OnRefreshOnChangeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if ((obj as TextBox) != null)
{
if ((bool)e.NewValue)
(obj as TextBox).TextChanged += textBox_TextChanged;
else
(obj as TextBox).TextChanged -= textBox_TextChanged;
}
if ((obj as PasswordBox) != null)
{
if ((bool)e.NewValue)
(obj as PasswordBox).PasswordChanged += passwordBox_PasswordChanged;
else
(obj as PasswordBox).PasswordChanged -= passwordBox_PasswordChanged;
}
}
static void passwordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox != null)
{
BindingExpression binding = passwordBox.GetBindingExpression(PasswordBox.PasswordProperty);
if (binding != null)
binding.UpdateSource();
}
}
static void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
BindingExpression binding = textBox.GetBindingExpression(TextBox.TextProperty);
if (binding != null)
binding.UpdateSource();
}
}
}
Related
I am having a master window in which there are plenty of user control. and using navigation i am able to access the user controls. But by question is how to set focus on the first text box when ever the user control is opened.
I tried with dependency property and boolean flags, i was able to succeeded a bit. When i first render the UserControl i was able to focus but when i open for the second time i was not able to set focus on the TextBox.
And one more thing, i have validation for TextBoxes, if the validation fails then the textbox should be emptied and the focus should be on the respective text box.
How can i achieve this using MVVM in WPF (CLR 3.5, VS2008)
thanks in advance.
If you have a UserControl then you also have CodeBehind.
Place this inside your codebehind and you will do fine.
this.Loaded += (o, e) => { Keyboard.Focus(textBox1) }
Place this inside your UserControl XAML if you wish to listen to validation errors.
<UserControl>
<Grid Validation.Error="OnValidationError">
<TextBox Text{Binding ..., NotifyOnValidationError=true } />
</Grid>
<UserControl>
Inside the CodeBehind of your UserControl you will have something like this:
public void OnValidationError(o , args)
{
if(o is TextBox)
{
(TextBox)o).Text = string.Empty;
}
}
You should use AttachedProperty to stick to MVVM pattern it'll keep your view model independent of UI code and fully unit testable. Following attached property binds a boolean property to focus and highlight the TextBox, if you do not want the highlighting then you can remove the highlighting code and just work with focus code.
public class TextBoxBehaviors
{
#region HighlightTextOnFocus Property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus", typeof (bool), typeof (TextBoxBehaviors),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool) obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var uie = sender as UIElement;
if (uie == null) return;
if ((bool) e.NewValue)
{
uie.GotKeyboardFocus += OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
uie.GotKeyboardFocus -= OnKeyboardFocusSelectText;
uie.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
textBox.SelectAll();
}
private static void OnMouseLeftButtonDownSetFocus(object sender, MouseButtonEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
if (!textBox.IsKeyboardFocusWithin)
{
textBox.Focus();
e.Handled = true;
}
}
#endregion
}
You can use this attached property in on your TextBox which you want to focus/highlight...
<TextBox ... local:TextBoxBehaviors.HighlightTextOnFocus="{Binding IsScrolledToEnd}" ... />
You can also try using FocusManager
<UserControl>
<Grid FocusManager.FocusedElement="{Binding Path=FocusedTextBox, ElementName=UserControlName}">
<TextBox x:Name="FocusedTextBox" />
</Grid>
<UserControl>
It is convenient to have an "Accept Button" (in WPF: IsDefault="True") on a Form.
In the Windows Forms world, I used to read the data from the UI to the object(s) in the corresponding Click event of the button.
But with WPF, data binding ought to be used. In the constructor of the Window, I set this.DataContext = test;
And here comes the problem: the user entered some text in TextBox2, and hits the Enter key. Now, the command bound to the OK button gets executed, the data are saved.
But it is not the correct data! Why? TextBox2 has not yet lost focus, and consequently the ViewModel has not yet been updated.
Changing the UpdateSourceTrigger to PropertyChanged is not always appropriate (e.g. formatted numbers), I am looking for a general solution.
How do you overcome such a problem?
Typically I use a custom Attached Property to tell WPF to update the binding source when the Enter key is pressed
It is used in the XAML like this:
<TextBox Text="{Binding SomeProperty}"
local:TextBoxProperties.EnterUpdatesTextSource="True" />
And the code for the attached property is below:
public class TextBoxProperties
{
// When set to True, Enter Key will update Source
public static readonly DependencyProperty EnterUpdatesTextSourceProperty =
DependencyProperty.RegisterAttached("EnterUpdatesTextSource", typeof(bool),
typeof(TextBoxProperties),
new PropertyMetadata(false, EnterUpdatesTextSourcePropertyChanged));
// Get
public static bool GetEnterUpdatesTextSource(DependencyObject obj)
{
return (bool)obj.GetValue(EnterUpdatesTextSourceProperty);
}
// Set
public static void SetEnterUpdatesTextSource(DependencyObject obj, bool value)
{
obj.SetValue(EnterUpdatesTextSourceProperty, value);
}
// Changed Event - Attach PreviewKeyDown handler
private static void EnterUpdatesTextSourcePropertyChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (obj != null)
{
if ((bool)e.NewValue)
{
sender.PreviewKeyDown += OnPreviewKeyDownUpdateSourceIfEnter;
}
else
{
sender.PreviewKeyDown -= OnPreviewKeyDownUpdateSourceIfEnter;
}
}
}
// If key being pressed is the Enter key, and EnterUpdatesTextSource is set to true, then update source for Text property
private static void OnPreviewKeyDownUpdateSourceIfEnter(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (GetEnterUpdatesTextSource((DependencyObject)sender))
{
var obj = sender as UIElement;
BindingExpression textBinding = BindingOperations.GetBindingExpression(
obj, TextBox.TextProperty);
if (textBinding != null)
textBinding.UpdateSource();
}
}
}
}
I'm using the WPF TabControl in my application in order to switch between different areas/functions of the program.
One thing annoys me though. I have hidden the tabs so I can control the selectedtab, instead of the user. The user however, can still switch between tabs using the arrow-keys.
I have tried using the KeyboardNavigation-attribute, but I can't get this working.
Can this be disabled?
You can hook on to the TabControl.PreviewKeyDown event for this one. Check to see if it's the left or right arrow and say that you've handled it.
private void TabControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Left || e.Key == Key.Right)
e.Handled = true;
}
if you're using a pure view model application you could apply the above as an attached property.
XAMl to use the below attached property.
<TabControl local:TabControlAttached.IsLeftRightDisabled="True">
<TabItem Header="test"/>
<TabItem Header="test"/>
</TabControl>
TabControlAttached.cs
public class TabControlAttached : DependencyObject
{
public static bool GetIsLeftRightDisabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsLeftRightDisabledProperty);
}
public static void SetIsLeftRightDisabled(DependencyObject obj, bool value)
{
obj.SetValue(IsLeftRightDisabledProperty, value);
}
// Using a DependencyProperty as the backing store for IsLeftRightDisabled. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsLeftRightDisabledProperty =
DependencyProperty.RegisterAttached("IsLeftRightDisabled", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(false, new PropertyChangedCallback((s, e) =>
{
// get a reference to the tab control.
TabControl targetTabControl = s as TabControl;
if (targetTabControl != null)
{
if ((bool)e.NewValue)
{
// Need some events from it.
targetTabControl.PreviewKeyDown += new KeyEventHandler(targetTabControl_PreviewKeyDown);
targetTabControl.Unloaded += new RoutedEventHandler(targetTabControl_Unloaded);
}
else if ((bool)e.OldValue)
{
targetTabControl.PreviewKeyDown -= new KeyEventHandler(targetTabControl_PreviewKeyDown);
targetTabControl.Unloaded -= new RoutedEventHandler(targetTabControl_Unloaded);
}
}
})));
static void targetTabControl_Unloaded(object sender, RoutedEventArgs e)
{
TabControl targetTabControl = sender as TabControl;
targetTabControl.PreviewKeyDown -= new KeyEventHandler(targetTabControl_PreviewKeyDown);
targetTabControl.Unloaded -= new RoutedEventHandler(targetTabControl_Unloaded);
}
static void targetTabControl_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Left || e.Key == Key.Right)
e.Handled = true;
}
}
I have a user control that is nested inside a window that is acting as a shell for a dialog display. I ignore focus in the shell window, and in the hosted user control I use the FocusManager to set the initial focus to a named element (a textbox) as shown below.
This works, setting the cursor at the beginning of the named textbox; however I want all text to be selected.
The TextBoxSelectionBehavior class (below) usually does exactly that, but not in this case. Is there an easy xaml fix to get the text in the named textbox selected on initial focus?
Cheers,
Berryl
TextBox Selection Behavior
// in app startup
TextBoxSelectionBehavior.RegisterTextboxSelectionBehavior();
/// <summary>
/// Helper to select all text in the text box on entry
/// </summary>
public static class TextBoxSelectionBehavior
{
public static void RegisterTextboxSelectionBehavior()
{
EventManager.RegisterClassHandler(typeof(TextBox), UIElement.GotFocusEvent, new RoutedEventHandler(OnTextBox_GotFocus));
}
private static void OnTextBox_GotFocus(object sender, RoutedEventArgs e)
{
var tb = (sender as TextBox);
if (tb != null)
tb.SelectAll();
}
}
The hosted UserControl
<UserControl
<DockPanel KeyboardNavigation.TabNavigation="Local"
FocusManager.FocusedElement="{Binding ElementName=tbLastName}" >
<TextBox x:Name="tbLastName" ... />
stop gap solution
Per comments with Rachel below, I ditched the FocusManger in favor of some code behind:
tbLastName.Loaded += (sender, e) => tbLastName.Focus();
Still would love a declarative approach for a simple and common chore though...
I usually use an AttachedProperty to make TextBoxes highlight their text on focus. It is used like
<TextBox local:HighlightTextOnFocus="True" />
Code for attached property
public static readonly DependencyProperty HighlightTextOnFocusProperty =
DependencyProperty.RegisterAttached("HighlightTextOnFocus",
typeof(bool), typeof(TextBoxProperties),
new PropertyMetadata(false, HighlightTextOnFocusPropertyChanged));
[AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetHighlightTextOnFocus(DependencyObject obj)
{
return (bool)obj.GetValue(HighlightTextOnFocusProperty);
}
public static void SetHighlightTextOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(HighlightTextOnFocusProperty, value);
}
private static void HighlightTextOnFocusPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (sender != null)
{
if ((bool)e.NewValue)
{
sender.GotKeyboardFocus += OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown += OnMouseLeftButtonDownSetFocus;
}
else
{
sender.GotKeyboardFocus -= OnKeyboardFocusSelectText;
sender.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDownSetFocus;
}
}
}
private static void OnKeyboardFocusSelectText(
object sender, KeyboardFocusChangedEventArgs e)
{
var textBox = e.OriginalSource as TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
private static void OnMouseLeftButtonDownSetFocus(
object sender, MouseButtonEventArgs e)
{
TextBox tb = FindAncestor<TextBox>((DependencyObject)e.OriginalSource);
if (tb == null)
return;
if (!tb.IsKeyboardFocusWithin)
{
tb.Focus();
e.Handled = true;
}
}
static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
Edit
Based on comments below, what about just getting rid of the FocusManager.FocusedElement and setting tb.Focus() and tb.SelectAll() in the Loaded event of your TextBox?
As stated above, you can add an event handler for the Loaded event to set focus and select all text:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
base.DataContext = new Person { FirstName = "Joe", LastName = "Smith" };
base.Loaded += delegate
{
this._firstNameTextBox.Focus();
this._firstNameTextBox.SelectAll();
};
}
}
I want keyboard focus to be set to a TextBox when I click a Button on my view. I don't want to use any codebehind, so wondered if anyone had written an attached property or similar solution?
Try this out:
public static class FocusBehavior
{
public static readonly DependencyProperty ClickKeyboardFocusTargetProperty =
DependencyProperty.RegisterAttached("ClickKeyboardFocusTarget", typeof(IInputElement), typeof(FocusBehavior),
new PropertyMetadata(OnClickKeyboardFocusTargetChanged));
public static IInputElement GetClickKeyboardFocusTarget(DependencyObject obj)
{
return (IInputElement)obj.GetValue(ClickKeyboardFocusTargetProperty);
}
public static void SetClickKeyboardFocusTarget(DependencyObject obj, IInputElement value)
{
obj.SetValue(ClickKeyboardFocusTargetProperty, value);
}
private static void OnClickKeyboardFocusTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var button = sender as ButtonBase;
if (button == null)
return;
if (e.OldValue == null && e.NewValue != null)
button.Click += OnButtonClick;
else if (e.OldValue != null && e.NewValue == null)
button.Click -= OnButtonClick;
}
private static void OnButtonClick(object sender, RoutedEventArgs e)
{
var target = GetKeyboardClickFocusTarget((ButtonBase)sender);
Keyboard.Focus(target);
}
}
Then to use it,
<TextBox x:Name="TargetTextBox"/>
<Button b:FocusBehavior.ClickKeyboardFocusTarget="{Binding ElementName=TargetTextBox}"/>