Why is there no IsReadOnlyChanged event on TextBox controls? - wpf

I was adding some workaround code to fix the bug outlined in Is this a bug in DotNet 4 WPF Spell Checking?, (When a WPF TextBox changes Enabled, Visible or ReadOnly states, any SpellCheck custom dictionaries get dropped off until you disable and re-enable SpellCheck) and the simplest fix seemed to be to handle the IsVisibleChanged, IsEnabledChanged, and IsReadOnlyChanged events.
Simple, right? Except there is no IsReadOnlyChanged event. Anybody know why and what the best way to trap a change to IsReadOnly in a WPF TextBox would be?

You can always follow dependency property change with DependencyPropertyDescriptor.AddValueChanged
DependencyPropertyDescriptor.FromProperty(TextBoxBase.IsReadOnlyProperty)
.AddValueChanged(ctrl, OnReadOnlyChanged)

Create a custom class and handle OnPropertyChanged event. Sth like this:
public class MyTextBox: TextBox
{
public MyTextBox() { }
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property.ToString() == "IsReadOnly")
{
// here you are sure that ContentPropertyhas changed
}
}
}

Related

Setting Default Keyboard Focus On Loading A UserControl

I have an MVVM setup with a mainwindow that contains a ContentControl.
I set this to a particular viewmodel which then maps to a view.
A view is a usercontrol.
I want to be able to set the default keyboard focus to a default element in the usercontrol(View) when it loads so the application can eventually be driven just by using up, down, left, right and enter.
Some of my failed attempts are setting
FocusManager.FocusedElement="{Binding ElementName=DefaultElement}"
in my content control tag. This sets the logical focus but not the keyboard focus
I'd rather keep the solution in xaml if possable but have tried placing the following in code behind.
Keyboard.Focus(DefaultElement);
This does not work but if I popup a message box first it does. I'm a little confused as to why.
MessageBox.Show(Keyboard.FocusedElement.ToString());
Keyboard.Focus(DefaultElement);
EDIT::::
I just placed this in my onloaded event of my user control. It seems to work but can anyone see any issues that might arrise at this priority level. I.E a circumstance when the action will never run?
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
Keyboard.Focus(DefaultElement);
}));
It seems that this wpf the you have to implement a workaround on a case by case basis. The solution that seemed to work best, most of the time for me was to insert the focus code inside the dispatcher when OnVisible was changed. This sets the focus not only when the View/Usercontrol loads but also if you a changing Views by way of Visibility. If you Hide and then Show a ContentControl that is mapped to your ViewModels then the Loaded event won't fire and you'll be forced to Mouse input, or tabbing (Not so good if you want to navigate your app with a remote control).
VisibilityChanged will always fire however. This is what I ended up with for my listbox.
private void ItemsFlowListBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
ItemsFlowListBox.Focus();
ItemsFlowListBox.ScrollIntoView(ItemsFlowListBox.SelectedItem);
}));
}
}
I had the same symptom for a WPF UserControl hosted in a Winforms application. Just wanted to note I was about to try this solution when I found a normal TabIndex in the Winforms app fixed it
Per How to set which control gets the focus on application start
"The one with the minimum tab index automatically gets the focus
(assuming the TabStop property is set to true). Just set the tab
indices appropriately."
It's a tricky one with no easy answer. I'm currently doing this, although I'm not sure I like it:
public MyView()
{
InitializeComponent();
// When DataContext changes hook the txtName.TextChanged event so we can give it initial focus
DataContextChanged +=
(sender, args) =>
{
txtName.TextChanged += OnTxtNameOnTextChanged;
};
}
private void OnTxtNameOnTextChanged(object o, TextChangedEventArgs eventArgs)
{
// Setting focus will select all text in the TextBox due to the global class handler on TextBox
txtName.Focus();
// Now unhook the event handler, since it's no longer required
txtName.TextChanged -= OnTxtNameOnTextChanged;
}
And in case you're wondering what the global class handler does, it's this:
protected override void OnStartup(StartupEventArgs e)
{
...
// Register a global handler for this app-domain to select all text in a textBox when
// the textBox receives keyboard focus.
EventManager.RegisterClassHandler(
typeof (TextBox), UIElement.GotKeyboardFocusEvent,
new RoutedEventHandler((sender, args) => ((TextBox) sender).SelectAll()));
which auto selects TextBox text when receiving keyboard focus.

Is there a way to make LostFocus fire when the keyboard focus leaves a focus scope?

My application's building a UI dynamically in which each ItemsControl in the view is a focus scope. The items displayed by each ItemsControl are controls in its focus scope. The user can tab through all of the controls in the view from start to end (i.e. the keyboard navigation mode is Continue). These controls are all bound to data source properties.
What seems to be happening is that when I'm on the last control in the first focus scope and press TAB, the keyboard focus moves to the second focus scope, but the previous control doesn't lose logical focus. So the bound property doesn't get updated.
I can make this problem go away (in theory, at least) by not making each ItemsControl a focus scope. But I didn't decide to implement logical focus capriciously: there are things the application needs to do when each ItemsControl loses logical focus, and if I get rid of the focus scopes it's going to be hard to make that happen.
This seems like a problem that should have a straightforward solution, but nothing in the documentation seems to suggest a way around it. Any ideas?
The problem is that you're trying to make logical focus in-line with keyboard focus which as the documentation shows is really not how it is supposed to be used. Logical focus provides a way to maintain what the previous control that had focus in a given focus scope so you can refocus again on it when you regain keyboard focus.
Looking at your question I think what you really want to do is pick up the event when your item contol, or one of the visual child elements, loses keyboard focus. This can
be achieved using IsKeyboardFocusedWithin property and you can trigger actions based on the associated event.
If you need this to be a routed event, then you'll need a custom control like follows which exposes a routing event for gaining and losing focus.
public partial class FocusManagingControl : UserControl
{
public static readonly RoutedEvent KeyboardLostFocusWithinEvent = EventManager.RegisterRoutedEvent("KeyboardLostFocusWithin",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FocusManagingControl));
public static readonly RoutedEvent KeyboardGotFocusWithinEvent = EventManager.RegisterRoutedEvent("KeyboardGotFocusWithin",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FocusManagingControl));
public event RoutedEventHandler KeyboardLostFocusWithin
{
add { AddHandler(KeyboardLostFocusWithinEvent, value); }
remove { RemoveHandler(KeyboardLostFocusWithinEvent, value); }
}
public event RoutedEventHandler KeyboardGotFocusWithin
{
add { AddHandler(KeyboardGotFocusWithinEvent, value); }
remove { RemoveHandler(KeyboardGotFocusWithinEvent, value); }
}
public FocusManagingControl()
{
this.InitializeComponent();
this.IsKeyboardFocusWithinChanged += FocusManagingControl_IsKeyboardFocusWithinChanged;
}
private void FocusManagingControl_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if((bool)e.OldValue && !(bool)e.NewValue)
RaiseEvent(new RoutedEventArgs(KeyboardLostFocusWithinEvent, this));
if(!(bool)e.OldValue && (bool)e.NewValue)
RaiseEvent(new RoutedEventArgs(KeyboardGotFocusWithinEvent, this));
}
}
Which you can use in your XAML with the entry
<local:FocusManagingControl>
<local:FocusManagingControl.Triggers>
<EventTrigger RoutedEvent="local:FocusManagingControl.KeyboardLostFocusWithin">
....

Update Source Without Losing Focus - WPF Telerik RADDatePicker

I have a Telerik RadDatePicker, which I am binding to the SelectedDate property. I want this control to show a validation error when the default date set in the control is removed/deleted. I was able to achieve this, but the problem was that the validation error occurs only when Enter is pressed or when we click outside the control.
Is there a way tell RadDatePicker to update the source without moving the focus? (Tried UpdateSourceTrigger=PropertyChanged, but still it wasnt working)
You can use a Behavior that catches whatever (keystrokes, value changes, etc.) and force databinding update.
I have written a similar one to use in Silverlight's TextBox, since in Silverlight you can't change the UpdateSourceTrigger like in WPF.
You can use the following code and make the required adjustments:
public class TextBoxUpdateBehavior : Behavior<TextBox>
{
public TextBoxUpdateBehavior()
{
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += AssociatedObjectOnTextChanged;
}
private void AssociatedObjectOnTextChanged(object sender, TextChangedEventArgs args)
{
var bindingExpr = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
bindingExpr.UpdateSource();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged;
}
}
I might be a bit late now, but you can use CurrentDateTimeText property of RadDatePicker
To extend zish's answer, I use the RadDatePicker's SelectedDate property bound to my VM's DateTime? SelectedDate property with no validation set in the binding. I use the RadDatePicker's CurrentDateTimeText property bound to my VM's string SelectedDateText property with ValidatesOnErrors=True in the binding. Then, in the VM, I validate that !String.IsNullOrEmpty(SelectedDateText). This gives me the behavior that I want without the need for a behavior.

MouseLeftButtonDown is not getting fired

I have a WPF user control that is dervied from UserControl class. MouseLeftButtonDown is not getting fired at all for the contol. I added event handler and also tried as follows.
I guess it is handled somewhere else, how to debug and find where is it getting hanlded.. Any help is appreciated!
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
}
Did you have the Background of the UserControl set to something? If not, try setting it to
"Background=Transparent" and see if that works. If it doesn't work, can you post the XAML for your UserControl, as well as the XAML for its usage?

WPF - CanExecute doesn't fire when raising Commands from a UserControl

I've got a button strip usercontrol that I want to have on most of my forms.
I've added the commands as follows ...
public ICommand Create
{
get
{
return buttonCreate.Command;
}
set
{
buttonCreate.Command = value;
}
}
I've set these as dependency properties so I can bind to them ...
public static readonly DependencyProperty CreateCommandProperty =
DependencyProperty.Register(
"Create",
typeof(ICommand),
typeof(StandardButtonStrip),
new PropertyMetadata((ICommand)null));
I'm then binding my usercontrol to a command ...
<commoncontrols:StandardButtonStrip HorizontalAlignment="Stretch" Create="{Binding CreateCommand}" />
I'm setting up the command as follows ...
_viewModel.CreateCommand = new DelegateCommand<object>(OnCreateCommand, CanCreate);
but despite the fact that i'm always returning true on my CanCreate method the button is disabled ... if I put a break point on return true it never fires!
public bool CanCreate(object parm)
{
return true;
}
I've tried this to see if it will refresh the binding, but no joy!
_viewModel.CreateCommand.RaiseCanExecuteChanged();
I think the problem is down to the user control and how I'm passing the Command up as a property, but not sure ...
The way this looks, you have a dependency property on a view model. If you are really using MVVM, that is definetly not the way to go about it (not because of religious adherence to a pattern, but because it's not the optimal way).
First of all, is your view model a DependencyObject?
If it is, you should downgrade it to a class which implements INotifyPropertyChanged. Why? Because the Button's Command property is a DependencyProperty itself (inherited from ButtonBase) and supports databinding already.
If it isn't, then a dependency property on it will not work, which is fine, because you shouldn't have dependency properties on your view model in the first place.
What you should do, is have the view model as the DataContext for your control (I'm guessing you already have that set up). Then change your view model's CreateCommand to a plain ICommand, and bind the createButton's Command property like this (in your default StandardButtonStrip style)
<Button Name="createButton" HorizontalAlignment="Stretch" Command="{Binding CreateCommand}" />
This way, it will still be reusable, you just need to ensure that any view model you associate with your user control has a property CreateCommand of type ICommand (and that view model will be inherited down to the button control by default - one of the nicest things the wpf guys thought up).
So to recap, you should do it the other way around, more or less.
Hope this was helpful, cheers.
One caveat to the accepted answer -
Using a delegate command I could only get this to work if I created a new
Command<T> : DelegateCommand<T> and hooked up the Command Manager.
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Have you overridden any of the user control's functionality?
One common problem is overriding a method without calling the base's implementation. For example, if you've overridden OnContentChanged() without calling base.OnContentChanged(), you may have accidentally supressed the firing of the ContentChanged event.

Resources