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>
Related
I have a TextBox whose text can be changed programmatically via binding from its Text property to a viewmodel property. This may for example happen as a result of a keypress (e.g. ↑ or ↓), but can also happen without any user input whatsoever. When this happens, it seems that any existing selection in the text box is removed. The behavior I desire is: If a text box has focus and all of the text is selected before the programmatic change (or if the text is empty), I want all of the text to be selected after the change. The text should however not be selected after a change caused by the user typing, since that would mean the user would just be replacing one character over and over again.
I have not found a way to accomplish this. Is it possible?
To be specific: I have set up a global event handler to select all text when a TextBox is focused, in order to allow users to more easily edit existing text in the TextBox if desired:
EventManager.RegisterClassHandler(
typeof(TextBox),
UIElement.GotFocusEvent,
new RoutedEventHandler((s, _) => (s as TextBox)?.SelectAll()));
However, in one of my views, tabbing out of TextBox A triggers an asynchronous action that changes the text in TextBox B (which is next in the tab order). This happens very quickly, but TextBox B gets focus before the text change happens, and thus the text is not selected. I would like the text that arrives in TextBox B to be selected so the user can more easily change it if desired.
I prefer implementing this kind of functionality in a Behavior that can be added in XAML; this requires the System.Windows.Interactivity.WPF NuGet Package.
I haven't tested this fully because I'm not exactly sure how to replicate your "asynchronous action", but it seems to work for the "normal" programmatic value changes that I've tried.
If you you really don't want the Behavior aspect of it, it should be fairly trivial to extract the event handling logic from it to use in whatever method you prefer.
Here is a short gif of it in action:
public class KeepSelectionBehavior : Behavior<TextBox>
{
private bool _wasAllTextSelected = false;
private int inputKeysDown = 0;
protected override void OnAttached()
{
base.OnAttached();
CheckSelection();
AssociatedObject.TextChanged += TextBox_TextChanged;
AssociatedObject.SelectionChanged += TextBox_SelectionChanged;
AssociatedObject.PreviewKeyDown += TextBox_PreviewKeyDown;
AssociatedObject.KeyUp += TextBox_KeyUp;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= TextBox_TextChanged;
AssociatedObject.SelectionChanged -= TextBox_SelectionChanged;
AssociatedObject.PreviewKeyDown -= TextBox_PreviewKeyDown;
AssociatedObject.KeyUp -= TextBox_KeyUp;
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_wasAllTextSelected && inputKeysDown == 0)
{
AssociatedObject.SelectAll();
}
CheckSelection();
}
private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
CheckSelection();
}
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (IsInputKey(e.Key))
{
inputKeysDown++;
}
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
if (IsInputKey(e.Key))
{
inputKeysDown--;
}
}
private bool IsInputKey(Key key)
{
return
key == Key.Space ||
key == Key.Delete ||
key == Key.Back ||
(key >= Key.D0 && key <= Key.Z) ||
(key >= Key.Multiply && key <= Key.Divide) ||
(key >= Key.Oem1 && key <= Key.OemBackslash);
}
private void CheckSelection()
{
_wasAllTextSelected = AssociatedObject.SelectionLength == AssociatedObject.Text.Length;
}
}
You can use it like this:
<Window
x:Class="ScriptyBot.Client.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="TestWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<StackPanel>
<TextBox Name="TextBox1" Margin="20">
<i:Interaction.Behaviors>
<behaviors:KeepSelectionBehavior />
</i:Interaction.Behaviors>
</TextBox>
</StackPanel>
</Window>
I'm testing it with a simple DispatchTimer that updates the text every second:
public partial class TestWindow : Window
{
private DispatcherTimer timer;
public TestWindow()
{
InitializeComponent();
timer = new DispatcherTimer(DispatcherPriority.Normal);
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += (sender, e) => { TextBox1.Text = DateTime.Now.ToString(); };
timer.Start();
}
}
By default, a Behavior has to be applied to every control manually in XAML, which can be very annoying. If you instead use this base class for your Behavior, you will be able to add it using a Style. This also works with implicit Styles too, so you can set it once in app.xaml, instead of manually for every control.
public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent>
where TComponent : System.Windows.DependencyObject
where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior>, new()
{
public static readonly DependencyProperty IsEnabledForStyleProperty =
DependencyProperty.RegisterAttached(name: "IsEnabledForStyle",
propertyType: typeof(bool),
ownerType: typeof(AttachableForStyleBehavior<TComponent, TBehavior>),
defaultMetadata: new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged));
public bool IsEnabledForStyle
{
get => (bool)GetValue(IsEnabledForStyleProperty);
set => SetValue(IsEnabledForStyleProperty, value);
}
private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement uiElement)
{
var behaviors = Interaction.GetBehaviors(uiElement);
var existingBehavior = behaviors.FirstOrDefault(b => b.GetType() == typeof(TBehavior)) as TBehavior;
if ((bool)e.NewValue == false && existingBehavior != null)
{
behaviors.Remove(existingBehavior);
}
else if ((bool)e.NewValue == true && existingBehavior == null)
{
behaviors.Add(new TBehavior());
}
}
}
}
The declaration of the Behavior class changes to look like this:
public class KeepSelectionBehavior : AttachableForStyleBehavior<TextBox, KeepSelectionBehavior>
And is applied like this (It can even be bound to a bool and dynamically turned on and off!):
<Style TargetType="TextBox">
<Setter Property="KeepSelectionBehavior.IsEnabledForStyle" Value="True" />
</Style>
Personally, I prefer using the Style based method anyway, even when adding it to a single, one-off, control. It is significantly less typing, and I don't have to remember how to define the xmlns for the Interactions or Behaviors namespaces.
I would like the text that arrives in TextBox B to be selected so the user can more easily change it if desired.
Handle the TextChanged event then. This event is raised whenever the Text property is changed. Yoy may want to add a delay so the user can type without the text being selected on each key stroke:
private DateTime _last;
private void txt2_TextChanged(object sender, TextChangedEventArgs e)
{
if (DateTime.Now.Subtract(_last) > TimeSpan.FromSeconds(3))
{
TextBox tb = (TextBox)sender;
if (Keyboard.FocusedElement == tb)
tb.SelectAll();
}
_last = DateTime.Now;
}
In I have created a control that has a text box and a text changed event handler attached to it - this is in xaml.
The problem: when control is loaded the text changed event is fired, I do not want it to happen when the control is loaded only when I make actually make it change on the control by typing something.
What do you pros suggest I do? :)
All you have to do is check the textbox's IsLoaded property inside the event handler before handling it.
Attach Your EventHandler after the InitializeComponent Method in your constructor not in the Xaml.
i.e.
public MainWindow()
{
InitializeComponent();
textBox1.TextChanged+=new TextChangedEventHandler(textBox1_TextChanged);
}
I noticed that you are talking about an usercontrol, the only thing I can think of off the top of my head is to to create a property that can be used to inhibit the TextChanged Event until the Parent Form finishes loading. See if something like this works.
MainForm Xaml:
<my:UserControl1 setInhibit="True" HorizontalAlignment="Left" Margin="111,103,0,0" x:Name="userControl11" VerticalAlignment="Top" Height="55" Width="149" setText="Hello" />
MainForm CS
private void Window_Loaded(object sender, RoutedEventArgs e)
{
userControl11.setInhibit = false;
}
UserControl:
public UserControl1()
{
InitializeComponent();
textBox1.TextChanged += new TextChangedEventHandler(textBox1_TextChanged);
}
public string setText
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
public bool setInhibit { get; set; }
void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
if (setInhibit) return;
// Do your work here
}
UserControl1.xaml:
<Grid>
<TextBox Text="{Binding MyText, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged"/>
</Grid>
where TextChanged is the original event for TextBox
UserControl1.xaml.cs:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
_isFirstTime = true;
DataContext = this;
InitializeComponent();
}
public event TextChangedEventHandler TextBoxTextChanged;
bool _isFirstTime;
//MyText Dependency Property
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(UserControl1), new UIPropertyMetadata(""));
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (TextBoxTextChanged != null)
if (!_isFirstTime)
{
TextBoxTextChanged(sender, e);
}
_isFirstTime = false;
}
}
where TextBox_TextChanged is the customized eventHandler for original TextChanged
and TextBoxTextChanged is more like a wrapper for the original TextChanged
Window.xaml:
<Grid>
<c:UserControl1 TextBoxTextChanged="TextBoxValueChanged"/>
</Grid>
as you see you can add an eventHandler to the event wrapper (TextBoxTextChanged)
Window.xaml.cs:
private void TextBoxValueChanged(object sender, TextChangedEventArgs e)
{
MessageBox.Show("asd");
}
finally TextBoxValueChanged won't be fired the first time Text is changed
private void TextBoxValueChanged(object sender, TextChangedEventArgs e)
{
if (Textbox1.IsFocused)
{
App.Current.Properties["TextChanged"] = "1"; // Set Flag
}
}
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
if (App.Current.Properties["TextChanged"] == "1")
{
// Do Your Wor Here
App.Current.Properties["TextChanged"] = "0"; // Clear Flag
}
}
On your XAML:
<TextBox xName="TextBox1" LostFocus="TextBoxLostFocus" TextChanged="TextBoxValueChanged"/>
(This is a very rudimentary, dirty, codebehind hack... checking the IsLoaded property as stated by Brent I found to be efficient)
Here since on textbox control creation it's not focused, the TextChanged event will fire but the flag "1" is NOT set...
Later when user leaves field after editing it, since it had focus the Flag is set... the LostFocus is fired, but only runnig code if textbox was changed.
I found a way of preventing this behavior across multiple inputs without having to create a unique bool for each input...
private void TextChanged_UpdateItem(object sender, TextChangedEventArg e)
{
TextBox txtBox = sender as TextBox;
if (!txtBox.IsFocused)
return;
//The rest of your code here
}
So basically, if the text field doesn't have focus (like on initialization) it just returns. This also prevents it from firing if the data is changed elsewhere. :)
Alternatively, as mentioned by Brent, you can just look for "IsLoaded":
private void TextChanged_UpdateItem(object sender, TextChangedEventArg e)
{
TextBox txtBox = sender as TextBox;
if (!txtBox.IsLoaded)
return;
//The rest of your code here
}
I want to copy the content of one text box to another text box by clicking the mouse.
How do I bind a mouse click event?
This sample is for RightClick, but you can adjust the event according to your needs:
<TextBox>
<TextBox.InputBindings>
<MouseBinding Gesture="RightClick" Command="{Binding YourCommand}" />
</TextBox.InputBindings>
</TextBox>
Edit: I uploaded on my SkyDrive a sample app that illustrates how to use this method in order to achieve exactly what you need. Please be advised that it will only work for .NET Framework 4+
Want to add a behavior to a control ? Just use the Ramora pattern !
Hope this helps
Use this code for TreeView
<TreeView commandBehaviors:MouseDoubleClick.Command="{Binding YourCommand}"
commandBehaviors:MouseDoubleClick.CommandParameter="{Binding}"
.../>
Use this code for TreeViewItem
<TreeView ItemsSource="{Binding Projects}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="commandBehaviors:MouseDoubleClick.Command"
Value="{Binding YourCommand}"/>
<Setter Property="commandBehaviors:MouseDoubleClick.CommandParameter"
Value="{Binding}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Use this code to create a new behavior MouseDoubleClick
public class MouseDoubleClick
{
public static DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command",
typeof(ICommand),
typeof(MouseDoubleClick),
new UIPropertyMetadata(CommandChanged));
public static DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter",
typeof(object),
typeof(MouseDoubleClick),
new UIPropertyMetadata(null));
public static void SetCommand(DependencyObject target, ICommand value)
{
target.SetValue(CommandProperty, value);
}
public static void SetCommandParameter(DependencyObject target, object value)
{
target.SetValue(CommandParameterProperty, value);
}
public static object GetCommandParameter(DependencyObject target)
{
return target.GetValue(CommandParameterProperty);
}
private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Control control = target as Control;
if (control != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
control.MouseDoubleClick += OnMouseDoubleClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
control.MouseDoubleClick -= OnMouseDoubleClick;
}
}
}
private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
Control control = sender as Control;
ICommand command = (ICommand)control.GetValue(CommandProperty);
object commandParameter = control.GetValue(CommandParameterProperty);
command.Execute(commandParameter);
}
}
It sounds like you are inventing a new behaviour for your textbox :)
I would just consider if the users of your program understands and likes this behaviour.
Maybe it is easier to understand the funcionality if it is just a button you have to click - it is also faster to implement :)
I think you could bind mouse gestures to commands. Take a look at this: http://www.thejoyofcode.com/Invoking_a_Command_on_a_Double_Click_or_other_Mouse_Gesture.aspx
I'm not sure what exactly you're wanting to bind to.
There is no readily available MouseClick event as far as i'm aware.
the Click event as you'd find on a Button is inherited from ButtonBase and is not readily available on most controls.
MouseDoubleClick is inherited from Control and available on anythning deriving from it.
in your example it sounds like a simple Button with its Click event handled might do the trick.
To bind to the click event, you just need to specify the event handler for the event in the Button.
Something like:
XAML:
<TextBox Name=TextBoxOne />
<TextBox Name=TextBoxTwo />
<Button Click="CopyTextButton_Click"/>
And in your code behind:
void CopyTextButton_Click(object sender, RoutedEventArgs e)
{
//Copy the text and anything else you need done
}
Otherwise if this is a more specialised scenario, you might want to investigate using a UserControl or as AndrewS answered above, a Command.
Hope it helps.
You can easily do this by creating a new behavior.
<TextBox
MouseDoubleClick="SelectAddress"
GotKeyboardFocus="SelectAddress"
PreviewMouseLeftButtonDown="SelectivelyIgnoreMouseButton" />
Here's the code behind:
private void SelectAddress(object sender, RoutedEventArgs e)
{
TextBox tb = (sender as TextBox);
if (tb != null)
{
tb.SelectAll();
}
}
private void SelectivelyIgnoreMouseButton(object sender,
MouseButtonEventArgs e)
{
TextBox tb = (sender as TextBox);
if (tb != null)
{
if (!tb.IsKeyboardFocusWithin)
{
e.Handled = true;
tb.Focus();
}
}
}
Please update this snippet according to your need.
Working on a TouchScreen application which also has a keyboard attached, I have the following problem:
The WPF window has a TextBox, which should receive ALL keyboard input. There are also Buttons and a ListBox, which are solely used by the TouchScreen(=Mouse).
A very simple example looks like this:
<Window x:Class="KeyboardFocusTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<StackPanel>
<TextBox Text="{Binding Input, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus"/>
<Button Click="Button_Click">Add</Button>
<ListBox ItemsSource="{Binding Strings}" />
</StackPanel>
</Window>
To keep the TextBox always focused, I just do:
private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
So far so good - the problem now is, that I can't select items from the ListBox anymore. This only seems to work, if the ListBox has the keyboard focus. But if I loose the keyboard focus on the TextBox, I can't enter text anymore without clicking it first.
Any ideas, comments suggestions are welcome!
There might be a more elegant solution for this, but you could always handle the PreviewKeyDown event at the Window level, and pass focus to the TextBox if it doesn't already have it, instead of preventing it from losing focus in the first place. That way, the ListBox can use focus as is normal, but as soon as a key is pressed focus jumps right to the TextBox. In addition, you can filter out keys that you don't want to switch focus - the arrow keys come to mind, which could then be used to move up and down in the ListBox.
Adding an event handler like the following should do the trick:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (!textBox.IsFocused)
{
textBox.Focus();
}
}
Based on Nicholas' suggestion (thx!), here's a markup extension, which is used like:
<TextBox Helpers:KeyboardFocusAttractor.IsAttracted="true" />
It seems to work, and ANTS didn't show any memory leaks. But when it comes to WPF and especially events and bindings, you never know, so use with care!
public static class KeyboardFocusAttractor
{
public static readonly DependencyProperty IsAttracted = DependencyProperty.RegisterAttached("IsAttracted",
typeof (bool), typeof (KeyboardFocusAttractor), new PropertyMetadata(false, OnIsAttracted));
private static void OnIsAttracted(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var isAttracted = (bool) e.NewValue;
var controlWithInputFocus = d as Control;
if (controlWithInputFocus != null)
{
if (isAttracted)
{
new KeyboardInputFocusEventManager(controlWithInputFocus);
}
}
}
public static void SetIsAttracted(DependencyObject dp, bool value)
{
dp.SetValue(IsAttracted, value);
}
public static bool GetIsAttracted(DependencyObject dp)
{
return (bool) dp.GetValue(IsAttracted);
}
private class KeyboardInputFocusEventManager
{
private readonly Control _control;
private Window _window;
public KeyboardInputFocusEventManager(Control control)
{
_control = control;
_control.Loaded += ControlLoaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
_control.Unloaded += ControlUnloaded;
}
private void ControlLoaded(object sender, RoutedEventArgs e)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_control.Unloaded += ControlUnloaded;
_control.IsVisibleChanged += ControlIsVisibleChanged;
if (_control.IsVisible)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ControlUnloaded(object sender, RoutedEventArgs e)
{
_control.Unloaded -= ControlUnloaded;
_control.IsVisibleChanged -= ControlIsVisibleChanged;
}
private void ControlIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_window != null)
{
_window.PreviewKeyDown -= ParentWindowPreviewKeyDown;
}
if (_control.IsVisible)
{
_window = Window.GetWindow(_control);
if (_window != null)
{
_window.PreviewKeyDown += ParentWindowPreviewKeyDown;
}
}
}
private void ParentWindowPreviewKeyDown(object sender, KeyEventArgs e)
{
Keyboard.Focus(_control);
}
}
}
I have a Silverlight 2 application that validates data OnTabSelectionChanged. Immediately I began wishing that UpdateSourceTrigger allowed more than just LostFocus because if you click the tab without tabbing off of a control the LINQ object is not updated before validation.
I worked around the issue for TextBoxes by setting focus to another control and then back OnTextChanged:
Private Sub OnTextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
txtSetFocus.Focus()
sender.Focus()
End Sub
Now I am trying to accomplish the same sort of hack within a DataGrid. My DataGrid uses DataTemplates generated at runtime for the CellTemplate and CellEditingTemplate. I tried writing the TextChanged="OnTextChanged" into the TextBox in the DataTemplate, but it is not triggered.
Anyone have any ideas?
You can do it with a behavior applied to the textbox too
// xmlns:int is System.Windows.Interactivity from System.Windows.Interactivity.DLL)
// xmlns:behavior is your namespace for the class below
<TextBox Text="{Binding Description,Mode=TwoWay,UpdateSourceTrigger=Explicit}">
<int:Interaction.Behaviors>
<behavior:TextBoxUpdatesTextBindingOnPropertyChanged />
</int:Interaction.Behaviors>
</TextBox>
public class TextBoxUpdatesTextBindingOnPropertyChanged : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += new TextChangedEventHandler(TextBox_TextChanged);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= TextBox_TextChanged;
}
void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}
}
This blog post shows how to update the source of a textbox explicitly using attached property:
http://www.thomasclaudiushuber.com/blog/2009/07/17/here-it-is-the-updatesourcetrigger-for-propertychanged-in-silverlight/
You could easily modify it to work with other controls as well...
I ran into this same problem using MVVM and Silverlight 4. The problem is that the binding does not update the source until after the textbox looses focus, but setting focus on another control doesn't do the trick.
I found a solution using a combination of two different blog posts. I used the code from Patrick Cauldwell's DefaultButtonHub concept, with one "SmallWorkaround" from SmallWorkarounds.net
http://www.cauldwell.net/patrick/blog/DefaultButtonSemanticsInSilverlightRevisited.aspx
www.smallworkarounds.net/2010/02/elementbindingbinding-modes.html
My change resulted in the following code for the DefaultButtonHub class:
public class DefaultButtonHub
{
ButtonAutomationPeer peer = null;
private void Attach(DependencyObject source)
{
if (source is Button)
{
peer = new ButtonAutomationPeer(source as Button);
}
else if (source is TextBox)
{
TextBox tb = source as TextBox;
tb.KeyUp += OnKeyUp;
}
else if (source is PasswordBox)
{
PasswordBox pb = source as PasswordBox;
pb.KeyUp += OnKeyUp;
}
}
private void OnKeyUp(object sender, KeyEventArgs arg)
{
if (arg.Key == Key.Enter)
if (peer != null)
{
if (sender is TextBox)
{
TextBox t = (TextBox)sender;
BindingExpression expression = t.GetBindingExpression(TextBox.TextProperty);
expression.UpdateSource();
}
((IInvokeProvider)peer).Invoke();
}
}
public static DefaultButtonHub GetDefaultHub(DependencyObject obj)
{
return (DefaultButtonHub)obj.GetValue(DefaultHubProperty);
}
public static void SetDefaultHub(DependencyObject obj, DefaultButtonHub value)
{
obj.SetValue(DefaultHubProperty, value);
}
// Using a DependencyProperty as the backing store for DefaultHub. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultHubProperty =
DependencyProperty.RegisterAttached("DefaultHub", typeof(DefaultButtonHub), typeof(DefaultButtonHub), new PropertyMetadata(OnHubAttach));
private static void OnHubAttach(DependencyObject source, DependencyPropertyChangedEventArgs prop)
{
DefaultButtonHub hub = prop.NewValue as DefaultButtonHub;
hub.Attach(source);
}
}
This should be included in some sort of documentation for Silverlight :)
I know it's old news... but I got around this by doing this:
Text="{Binding Path=newQuantity, UpdateSourceTrigger=PropertyChanged}"