Update Viewmodel prop when the user control lost focus - c#

<UserControl UIHelper:FocusExtension.IsFocused="{Binding FocusUserControl,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Focusable="True" IsTabStop="True">
here I added an IsFocused prop to maintain the focus of usercontrol
public static class FocusExtension
{
public static bool GetIsFocused(DependencyObject obj)
{
return (bool)obj.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject obj, bool value)
{
obj.SetValue(IsFocusedProperty, value);
}
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached(
"IsFocused", typeof(bool), typeof(FocusExtension),
new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
}
this is my FocusExtension class
public bool FocusUserControl
{
get { return focusUserControl; }
set
{
focusUserControl = value;
OnPropertyChanged(new PropertyChangedEventArgs("FocusUserControl"));
}
}
this is my VM prop. which is binded, now in user control, I have a button. when I click on it a new user control will. open at that time I need to update the FocusUserControl to false. as the focus is not on that usercontrol anymore. I am stuck here please suggest something thanks for ur help.
**
Update i added something like this
**
private static void OnIsFocusedPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.LostKeyboardFocus += LostFocus;
uie.Focus();
}
}
static void LostFocus(object sender, RoutedEventArgs e)
{
uie.LostKeyboardFocus -= LostFocus;
// Here i need to update `FocusUserControl` to false
}
in LostFocus i just need to update FocusUserControl to false. how can i do it? really use some help.

Related

Attached property cannot be bound

We have a WPF application which has a query count result displayed on the screen. We initially defined the result as a button so that when it was clicked, the application would display a detailed list of the query results. However, for reasons unrelated to this question, we now need this to be a border (basically, just the template for the original button). So far, I have set up my attached property:
public static class AttachedCommandBehavior
{
#region Command
public static DependencyProperty PreviewMouseLeftButtonUpCommandProperty = DependencyProperty.RegisterAttached(
"PreviewMouseLeftButtonUpCommand",
typeof(ICommand),
typeof(AttachedCommandBehavior),
new FrameworkPropertyMetadata(PreviewPreviewMouseLeftButtonUpChanged));
public static void SetPreviewMouseLeftButtonUpChanged(DependencyObject target, ICommand value)
{
target.SetValue(PreviewMouseLeftButtonUpCommandProperty, value);
}
public static ICommand GetPreviewMouseLeftButtonUpChanged(DependencyObject target)
{
return (ICommand)target.GetValue(PreviewMouseLeftButtonUpCommandProperty);
}
private static void PreviewPreviewMouseLeftButtonUpChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
if (e.NewValue != null && e.OldValue == null)
{
element.PreviewMouseLeftButtonUp += element_PreviewMouseLeftButtonUp;
}
else if (e.NewValue == null && e.OldValue != null)
{
element.PreviewMouseLeftButtonUp -= element_PreviewMouseLeftButtonUp;
}
}
}
private static void element_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element)
{
if (element.GetValue(PreviewMouseLeftButtonUpCommandProperty) is ICommand command)
command.Execute(CommandParameterProperty);
}
}
#endregion
#region CommandParameter
public static DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
"CommandParameter",
typeof(object),
typeof(AttachedCommandBehavior),
new FrameworkPropertyMetadata(CommandParameterChanged));
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 CommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
element.SetValue(CommandParameterProperty, e.NewValue);
}
}
#endregion
}
And then in my XAML, I am trying to bind my command to the attached DependencyProperty:
<Border Background="{Binding BackgroundColor, Converter={StaticResource ColorNameToBrushConverter}}"
Cursor="{x:Static Cursors.Hand}"
local:AttachedCommandBehavior.PreviewMouseLeftButtonUpChanged="{Binding QueryClickedCommand}">
<Grid>...</Grid>
</Border>
However, my little blue squiggly line is telling me "A 'Binding' cannot be used within a 'Border' collection. A 'Binding' can only be set on a DependencyProperty of a DependencyObject." Being the daring programmer that I am, I boldly ignore the little blue squiggly and try to run anyway. At which point I get an exception:
System.Windows.Markup.XamlParseException: 'A 'Binding' cannot be set on the 'SetPreviewMouseLeftButtonUpChanged' property of type 'Viewbox'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
It turns out this was a naming convention problem. In copying/pasting/renaming/general indecision, I messed up the names of my getter and setter for the command property. Once I changed them all to match the correct pattern, my code runs.
#region Command
public static DependencyProperty PreviewMouseLeftButtonUpCommandProperty = DependencyProperty.RegisterAttached(
"PreviewMouseLeftButtonUpCommand",
typeof(ICommand),
typeof(AttachedCommandBehavior),
new FrameworkPropertyMetadata(PreviewMouseLeftButtonUpChanged));
public static void SetPreviewMouseLeftButtonUpCommand(DependencyObject target, ICommand value)
{
target.SetValue(PreviewMouseLeftButtonUpCommandProperty, value);
}
public static ICommand GetPreviewMouseLeftButtonUpCommand(DependencyObject target)
{
return (ICommand)target.GetValue(PreviewMouseLeftButtonUpCommandProperty);
}
private static void PreviewMouseLeftButtonUpChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
if (e.NewValue != null && e.OldValue == null)
{
element.PreviewMouseLeftButtonUp += element_PreviewMouseLeftButtonUp;
}
else if (e.NewValue == null && e.OldValue != null)
{
element.PreviewMouseLeftButtonUp -= element_PreviewMouseLeftButtonUp;
}
}
}
private static void element_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element)
{
if (element.GetValue(PreviewMouseLeftButtonUpCommandProperty) is ICommand command)
command.Execute(CommandParameterProperty);
}
}
#endregion

WPF execute ICommand using attached properties and behaviours

So I'm learning about attached properties and attached behaviours. I have a TextBox and everytime the text is changed, I want to execute some ICommand SomeCommand in my ViewModel which I would bind in the view like this:
<TextBox ... TextUpdateCommand="{Binding Path=SomeCommand}" MonitorTextBoxProperty=true />
MonitorTextBoxProperty is just a property that listens to TextChanged event and then executes ICommand SomeCommand when TextChanged is fired. The ICommand SomeCommand that is executed should come from TextUpdateCommandProperty. How do I link this ICommand SomeCommand to execute it from OnTextBoxTextChanged?
Code:
//Property and behaviour for TextChanged event
public static int GetMonitorTextBoxProperty(DependencyObject obj)
{
return (int)obj.GetValue(MonitorTextBoxProperty);
}
public static void SetMonitorTextBoxProperty(DependencyObject obj, int value)
{
obj.SetValue(MonitorTextBoxProperty, value);
}
public static readonly DependencyProperty MonitorTextBoxProperty =
DependencyProperty.RegisterAttached("MonitorTextBox", typeof(bool), typeof(this), new PropertyMetadata(false, OnMonitorTextBoxChanged));
private static void OnMonitorTextBoxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = (d as TextBox);
if (textBox == null) return;
textBox.TextChanged -= OnTextBoxTextChanged;
if ((bool)e.NewValue)
{
textBox.TextChanged += OnTextBoxTextChanged;
}
}
private static void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
// Execute ICommand by command.Execute()
}
// Property for attaching ICommand that is executed on TextChanged
public static int GetTextUpdateCommandProperty(DependencyObject obj)
{
return (int)obj.GetValue(TextUpdateCommandProperty);
}
public static void SetTextUpdateCommandProperty(DependencyObject obj, int value)
{
obj.SetValue(TextUpdateCommandProperty, value);
}
public static readonly DependencyProperty TextUpdateCommandProperty =
DependencyProperty.RegisterAttached("TextUpdateCommand", typeof(ICommand), typeof(this), new PropertyMetadata(null));
Just get the current value of the TextBox's TextUpdateCommand property using the attached property's get accessor. Try this:
private static void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
var command = GetTextUpdateCommandProperty(textBox);
if (command != null)
command.Execute(null);
}
You should also change the type of the accessors:
public static ICommand GetTextUpdateCommandProperty(DependencyObject obj)
{
return (ICommand)obj.GetValue(TextUpdateCommandProperty);
}
public static void SetTextUpdateCommandProperty(DependencyObject obj, ICommand value)
{
obj.SetValue(TextUpdateCommandProperty, value);
}
And typeof(this) should the typeof(TheClassName) in the call to DependencyProperty.RegisterAttached.

C# WPF Binding value does not trigger animation to in user control

Hello I have issue with binding to user control animation, after I bind data to user control(which is bool type) it sets correct values to user control data, but does not trigger animation, I tried to use PropertyChangedCallback but with no luck user control code below:
private static Switch_box AppWindow;
public Switch_box()
{
InitializeComponent();
AppWindow = this;
}
public static readonly DependencyProperty CheckboxStatusProperty = DependencyProperty.Register(nameof(CheckboxStatus), typeof(bool), typeof(Switch_box), new PropertyMetadata(false, new PropertyChangedCallback(OnCurrentReadingChanged)));//cant remove static otherwise throws error
public bool CheckboxStatus
{
get
{
return (bool)GetValue(CheckboxStatusProperty);
}
set
{
/* if (value == true)
{
((Storyboard)FindResource("OnChecking")).Begin(this);
}
else
{
((Storyboard)FindResource("OnUnchecking")).Begin(this);
}*/
SetValue(CheckboxStatusProperty, value);
}
}
private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)//cant remove static due to PropertyChangedCallBack requires static otherwise it throws error
{
AppWindow.OnChecking((bool)d.GetValue(CheckboxStatusProperty));
}
private void OnChecking(bool Status)
{
switch (Status)
{
case true:
{
((Storyboard)FindResource("OnChecking")).Begin(this);
break;
}
case false:
{
((Storyboard)FindResource("OnUnchecking")).Begin(this);
break;
}
}
}
And my usercontrol bind line:
<local:Switch_box Tag="{Binding Index,IsAsync=True}" Checked="Switch_box_Checked" Unchecked="Switch_box_Unchecked" CheckboxStatus="{Binding IsEnabled,IsAsync=True}"/>
How to trigger animation after CheckboxStatus variable is changed?
EDIT 1: updated code.
There is a naming convention. _StatusBox should be named CheckboxStatusProperty, and it should be public:
public static readonly DependencyProperty CheckboxStatusProperty =
DependencyProperty.Register(
nameof(CheckboxStatus), typeof(bool), typeof(Switch_box),
new PropertyMetadata(false, OnCurrentReadingChanged));
You must not call anything else than GetValueand SetValue in the CLR wrapper of a dependency property. And you call the methods on the current instance, not on a static field:
public bool CheckboxStatus
{
get { return (bool)GetValue(CheckboxStatusProperty); }
set { SetValue(CheckboxStatusProperty , value); }
}
In the PropertyChangedCallback it is pointless to set the property another time. And again, you should operate on the current DependencyObject instance, i.e. d, not on a static field:
private static void OnCurrentReadingChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Switch_box)d).OnChecking((bool)e.NewValue);
}
private void OnChecking(bool status)
{
if (status)
{
((Storyboard)FindResource("OnChecking")).Begin(this);
}
else
{
((Storyboard)FindResource("OnUnchecking")).Begin(this);
}
}

Loaded event and data-binding in WPF

Suppose we have a control with a dependency property:
public class MyControl : ContentControl
{
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
void DoSomething(object sender, RoutedEventArgs e)
{
//...
}
public MyControl()
{
this.Loaded += DoSomething;
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//some actions
}
public string Value
{
get { return (string)GetValue(MyControl.ValueProperty); }
set { SetValue(MyControl.ValueProperty, value); }
}}
And somewhere in XAML we bind ValueProperty to some property Val of some ViewModel.
My question is, at the moment when Loaded event is fired, will the value of MyControl.Value be already set to the value of Val which it has or not? So, am I able to use Val in DoSomething method which is executed when MyControl is loaded or not?
Thank you in advance for any answers.
My question is, at the moment when Loaded event is fired, will the value of MyControl.Value be already set to the value of Val which it has or not?
You cannot rely on this. The Loaded event is not a 'data-binding-has-completed' event. Also, the value of a dependency property can change at any time and the place to handle any changes to your Value property is in the OnValueChanged callback. You could check whether the control has been loaded in the callback if you want to:
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl ctrl = d as MyControl;
if (ctrl.IsLoaded)
{
//...
}
}
The point is that you should perform any action that depends upon the value of your dependency property in the callback and not in the Loaded event handler.
public class MyControl : ContentControl
{
public MyControl()
{
this.Loaded += (s,e) => DoSomething();
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (MyControl)d;
if ( c.IsLoaded )
c.DoSomething();
}
// this will be called only if the control is loaded
private void DoSomething()
{
var value = Value;
if ( value == null )
{
...
} else if ( value == string.Empty )
{
...
} else
{
}
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(string), typeof(MyControl),
new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnValueChanged));
public string Value
{
get { return (string)GetValue(MyControl.ValueProperty); }
set { SetValue(MyControl.ValueProperty, value); }
}
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
}

collection dependency properties

I have a custom control that has a DependencyProperty of type ObservableCollection that is bound to an observableCollection:
<MyControl MyCollectionProperty = {Binding MyObservableCollection} ...
Problem is adding to MyObservableCollection does not update MyCollectionProperty.
I need to completly replace the MyObservableCollection to make it work e.g.
MyObservableCollection = null;
MyObservableCollection = new ObservableCollection(){...}
Is there a better way to deal with this?
EDIT:
public ObservableCollection<string> Columns
{
get { return (ObservableCollection<string>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", typeof(ObservableCollection<string>), typeof(MyControl),
new PropertyMetadata(new ObservableCollection<string>(), OnChanged));
In addition to what grantz has answered, I would suggest to declare the property with type IEnumerable<string> and check at runtime if the collection object implements the INotifyCollectionChanged interface. This provides greater flexibility as to which concrete collection implementation may be used as property value. A user may then decide to have their own specialized implementation of an observable collection.
Note also that in the ColumnsPropertyChanged callback the CollectionChanged event handler is attached to the new collection, but also removed from the old one.
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register(
"Columns", typeof(IEnumerable<string>), typeof(MyControl),
new PropertyMetadata(null, ColumnsPropertyChanged));
public IEnumerable<string> Columns
{
get { return (IEnumerable<string>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
private static void ColumnsPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var control= (MyControl)obj;
var oldCollection = e.OldValue as INotifyCollectionChanged;
var newCollection = e.NewValue as INotifyCollectionChanged;
if (oldCollection != null)
{
oldCollection.CollectionChanged -= control.ColumnsCollectionChanged;
}
if (newCollection != null)
{
newCollection.CollectionChanged += control.ColumnsCollectionChanged;
}
control.UpdateColumns();
}
private void ColumnsCollectionChanged(
object sender, NotifyCollectionChangedEventArgs e)
{
// optionally take e.Action into account
UpdateColumns();
}
private void UpdateColumns()
{
...
}
Below is a working example that may help.
In this example, the method OnChanged is called immediately, when the Add button is clicked "Changed" is written to the console.
The Control
public class MyControl : Control
{
public ObservableCollection<string> ExtraColumns
{
get { return (ObservableCollection<string>)GetValue(ExtraColumnsProperty); }
set { SetValue(ExtraColumnsProperty, value); }
}
public static readonly DependencyProperty ExtraColumnsProperty =
DependencyProperty.Register("ExtraColumns", typeof(ObservableCollection<string>), typeof(MyControl),
new PropertyMetadata(new ObservableCollection<string>(), OnChanged));
static void OnChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as MyControl).OnChanged();
}
void OnChanged()
{
if ( ExtraColumns != null )
ExtraColumns.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ExtraColumns_CollectionChanged);
}
void ExtraColumns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Console.WriteLine("Changed");
}
}
The Window
<Window x:Class="WpfApplication18.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication18"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:MyControl ExtraColumns="{Binding Extras}"/>
<Button Click="Button_Click">Add</Button>
</StackPanel>
</Window>
Window Code Behind
public partial class MainWindow : Window
{
private ObservableCollection<string> _extras = new ObservableCollection<string>( );
public ObservableCollection<string> Extras
{
get { return _extras; }
set
{
if (value != _extras)
{
_extras = value;
}
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Extras.Add("Additional");
}
}

Categories

Resources