I'm trying to bind focus for my control to a property on the view model, like this:
public class Focus
{
public static readonly DependencyProperty HasFocusProperty = DependencyProperty.RegisterAttached("HasFocus",
typeof(bool),
typeof(Focus),
new PropertyMetadata(false, HandleHasFocusChanged),
null
);
private static void HandleHasFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uiElement = d as UIElement;
var value = (bool)e.NewValue;
if (value)
{
FocusManager.SetFocusedElement(uiElement, uiElement);
}
}
public static bool GetHasFocus(UIElement obj)
{
return (bool)obj.GetValue(HasFocusProperty);
}
public static void SetHasFocus(UIElement obj, bool value)
{
obj.SetValue(HasFocusProperty, value);
}
}
This works for the first focus, but then after that it seems to not have any affect at all
Anybody know what I'm doing wrong, or even a better way to do what I'm trying to achieve?
Probably the difference between logical focus and keyboardfocus is biting you. You can read about it here. Pay particular attention to the part about focus scope.
You have implemented code to set the logical focus to your user control when the HasFocus property is set to true, but you do nothing when it is set to false. In that case the logical focus will stay where it is.
On the other hand, you have not hooked up an event handler for the user control's lostfocus event. That means that your HasFocus property will once more be unaffected when the control loses focus.
Related
I have a textbox on my wpfgrid that I need to perform some tekst inserting and such on. To do this, the textbox is referenced into the presentationmodel from the view, ReferenceToTextBox (we do MVP with Prism). Also, the textbox in the view TextDescription is bound to the Description-property on the model.
We also have a dropdown-list containing some predefined text-blobs (adresses, VAT-numbers and such). When you choose one of these, they should be inserted into the textbox at the carets current position. Since you can't bind on CaretIndex, the above mentioned workaround is made. The dropdown-list is bound on SelectedItem to a property on the model, so when the SelectedItem changes, the property changes, and in the setter on the property a method is called to insert the text of the selected-item into the ReferenceToTextBox "virtual" textbox in the model (which should be just a reference to the textbox in the view).
However, if I delete all the text from the textbox in the view and add a new predefined text-blob. The ReferenceToTextBox.Text property still contains all the text that I deleted. It seems like the ReferenceToTextBox is no longer just a reference, but a whole own textbox. Which makes it even weirder when updates to ReferenceToTextBox.Text actually updates the "visual" textbox on the view.
What is actually happening here?
Not a direct answer to your question, but what about a derived TextBox class that actually allows binding to its CaretIndex property:
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty CaretIndexProperty = DependencyProperty.Register(
"CaretIndex", typeof(int), typeof(TextBoxEx),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CaretIndexChanged));
public new int CaretIndex
{
get { return (int)GetValue(CaretIndexProperty); }
set { SetValue(CaretIndexProperty, value); }
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
CaretIndex = base.CaretIndex;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
CaretIndex = base.CaretIndex;
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
CaretIndex = base.CaretIndex;
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
CaretIndex = base.CaretIndex;
}
private static void CaretIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj is TextBox)
{
((TextBox)obj).CaretIndex = (int)e.NewValue;
}
}
}
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 have created a custom TextBox control (but not derived from TextBox) that contains a Dependency Property "Text".
I have added an instance of this and bound it to a property on my view model using a TwoWay binding.
From within my custom TextBox control, how do I update the Text property in such a way that the change is propagated to the property on the view model?
If I set the "Text" property on my custom control, that replaces the binding leaving the property on the view model as null.
I would have thought this would be simple but I can't see how to do it (the standard TextBox control must do it!)
Cheers
Edit:
Custom Control:
public class SampleCustomControl : CustomControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(SampleCustomControl), new PropertyMetadata(null));
public void Update()
{
// This replaces my binding, I want it to pass the new value
// through to the "SomeProperty" two way binding.
Text = "some value";
}
}
Usage:
<Controls:SampleCustomControl Text="{Binding SomeProperty, Mode=TwoWay}" />
You need to add a Property Changed callback in the metadata of your dependency property.
This callback will be fired when the Text property changes (from either side). You can use the value passed in from this to update your custom UI that you've built to display the text.
Update:
Responding to your comment about what this is about. Since your example code is too vague to test, here is what I used to test your problem.
public class TestControl : ContentControl
{
private TextBlock _tb;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_tb = new TextBlock();
_tb.Text = Text;
this.Content = _tb;
_tb.MouseLeftButtonDown += new MouseButtonEventHandler(_tb_MouseLeftButtonDown);
}
void _tb_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Update();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TestControl), new PropertyMetadata(string.Empty, OnTextChanged));
public void Update()
{
// This replaces my binding, I want it to pass the new value
// through to the "SomeProperty" two way binding.
Text = "some value";
}
public static void OnTextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
((TestControl)sender).UpdateText((string)e.NewValue);
}
protected void UpdateText(string text)
{
if (_tb != null) _tb.Text = text;
}
}
I then bound the Text property on my control to the view model using a two way binding. When I click the text in the view both the view and the viewmodel get updated with the new text "some value". If I update the value in the viewmodel (and raise the property changed event) the value gets updated in the view and the control so the binding is still valid.
There must be some other missing pieces in your example.
As long as your binding property is set to TwoWay and you have exposed the getter and the setter, than the text you enter in the TextBox is sent to the ViewModel. I believe the actual send occurs when you lose focus of that control however, i believe.
I've created a custom control with, amongst others, the following:
public partial class MyButton : UserControl
{
public bool Enabled
{
get { return (bool)GetValue(EnabledProperty); }
set {
SetValue(EnabledProperty, value);
SomeOtherStuff();
}
}
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool), typeof(MyButton), new PropertyMetadata(true));
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static bool GetEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(EnabledProperty);
}
}
In my XAML, I (try to) use binding to set the Enabled property:
<MyButton x:Name="myButtom1" Enabled="{Binding CanEnableButton}"/>
I know the bind between my control and the underlying data model is valid and working as I can bind 'IsEnabled' (a native property of the underlying UserControl) and it works as expected. However, my Enabled property is never set via the above binding. I've put breakpoints on my property set/get and they never get hit at all.
I can only imaging I've missed something relating to binding in my custom control. Can anyone see what?
I've tried implementing INotifyPropertyChanged on my control (and calling the PropertyChanged event from my Enabled setter) ... but that didn't fix it.
[ BTW: In case you are wondering "Why?": I can't intercept changes to the IsEnabled state of the base control, so I decided to implement and use my own version of a Enable/disable property (which I called Enabled) - one where I could plug my own code into the property setter ]
First of all drop the SetEnabled and GetEnabled pair, these only make sense for an attached property which is not what you are doing.
Now your main problem is that you are under the false assumption that the get/set members of your propery get called during binding, they don't.
What you need is to pass a call back method in the property meta data, it's here that you intercept changes and take other actions like so:-
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(true, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyButton source = d as MyButton;
source.SomeOtherStuff();
}
private void SomeOtherStuff()
{
// Your other stuff here
}
With this in place regardless of how the propery is changed the SomeOtherStuff procedure will execute.
I'd suggest using the IsEnabledChanged event which is part of every Control/UserControl.
That would allow you to hook up to the event and do whatever actions you want to take.
public MainPage()
{
InitializeComponent();
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(MainPage_IsEnabledChanged);
}
void MainPage_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Do SomeStuff
}
all. I have a usercontrol "NumericTextBox" that only allows numeric entries. I need to exhibit another specialized behaviour, that is, I need it to be able to bind it to a VM value OneWayToSource and only have the VM value update when I press enter while focusing the textbox. I already have the an EnterPressed event that fires when I press the key, I'm just having a hard time figuring out a way to cause that action to update the binding...
In your binding expression, set the UpdateSourceTrigger to Explicit.
Text="{Binding ..., UpdateSourceTrigger=Explicit}"
Then, when handling the EnterPressed event, call UpdateSource on the binding expression, this will push the value from the textbox to the actual bound property.
BindingExpression exp = textBox.GetBindingExpression(TextBox.TextProperty);
exp.UpdateSource();
Here is a complete version of the idea provided by Anderson Imes:
public static readonly DependencyProperty UpdateSourceOnKeyProperty =
DependencyProperty.RegisterAttached("UpdateSourceOnKey",
typeof(Key), typeof(TextBox), new FrameworkPropertyMetadata(Key.None));
public static void SetUpdateSourceOnKey(UIElement element, Key value) {
element.PreviewKeyUp += TextBoxKeyUp;
element.SetValue(UpdateSourceOnKeyProperty, value);
}
static void TextBoxKeyUp(object sender, KeyEventArgs e) {
var textBox = sender as TextBox;
if (textBox == null) return;
var propertyValue = (Key)textBox.GetValue(UpdateSourceOnKeyProperty);
if (e.Key != propertyValue) return;
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null) bindingExpression.UpdateSource();
}
public static Key GetUpdateSourceOnKey(UIElement element) {
return (Key)element.GetValue(UpdateSourceOnKeyProperty);
}
If you are using MVVM you can use a combination of decastelijau's approach along with a custom attached property that calls UpdateSource on the textbox when PreviewKeyUp.
public static readonly DependencyProperty UpdateSourceOnKey = DependencyProperty.RegisterAttached(
"UpdateSourceOnKey",
typeof(Key),
typeof(TextBox),
new FrameworkPropertyMetadata(false)
);
public static void SetUpdateSourceOnKey(UIElement element, Key value)
{
//TODO: wire up specified key down event handler here
element.SetValue(UpdateSourceOnKey, value);
}
public static Boolean GetUpdateSourceOnKey(UIElement element)
{
return (Key)element.GetValue(UpdateSourceOnKey);
}
Then you can do:
<TextBox myprops:UpdaterProps.UpdateSourceOnKey="Enter" ... />