One of the things WPF developers often notice is inability to SetFocus on a particular control from the ViewModel. This problem can be however easily resolved using an attached property. Let’s examine the code.
We will begin by writing an attached property, for the purpose of example, named as IsFocused.
public class FocusExtension : DependencyObject { public static bool GetIsFocused(DependencyObject dependencyObject) => (bool)dependencyObject.GetValue(IsFocusedProperty); public static void SetIsFocused(DependencyObject dependencyObject, bool value) => dependencyObject.SetValue(IsFocusedProperty, value); public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new PropertyMetadata(false, IsFocusChanged)); private static void IsFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { d as UIElement).Focus(); } } }
The sole objective of the attached property is to call the Focus() method of the attached UI element when the IsFocused Property Changes. Since the attached property is bindable from ViewModel, we can now set Focus to any control from our View Model. Am using Caliburn.Micro for my MVVM Implementation
View
<Grid> <Grid.RowDefinitions> <RowDefinition Height="20"/> <RowDefinition Height="20"/> <RowDefinition Height="20"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Button Grid.Row="0" Grid.Column="0" Content="Set Focus" x:Name="SetFocusOnTextBox1"/> <TextBox Grid.Row="0" Grid.Column="1" Text="TextBox 1" ap:FocusExtension.IsFocused="{Binding IsTextBox1Focused}" /> <Button Grid.Row="1" Grid.Column="0" Content="Set Focus" x:Name="SetFocusOnTextBox2"/> <TextBox Grid.Row="1" Grid.Column="1" Text="TextBox 2" ap:FocusExtension.IsFocused="{Binding IsTextBox2Focused}" /> <Button Grid.Row="2" Grid.Column="0" Content="Set Focus" x:Name="SetFocusOnTextBox3"/> <TextBox Grid.Row="2" Grid.Column="1" Text="TextBox 3" ap:FocusExtension.IsFocused="{Binding IsTextBox3Focused}" /> <Button Grid.Row="3" Grid.Column="0" Content="Set Focus" x:Name="SetFocusOnTextBox4"/> </Grid>
ViewModel
public bool IsTextBox1Focused { get; set; } public bool IsTextBox2Focused { get; set; } public bool IsTextBox3Focused { get; set; } public void SetFocusOnTextBox1() { IsTextBox1Focused = true; NotifyOfPropertyChange(nameof(IsTextBox1Focused)); } public void SetFocusOnTextBox2() { IsTextBox2Focused = true; NotifyOfPropertyChange(nameof(IsTextBox2Focused)); } public void SetFocusOnTextBox3() { IsTextBox3Focused = true; NotifyOfPropertyChange(nameof(IsTextBox3Focused)); }