[How To] Establecer foco en XAML desde MVVM
Introducción
Cuando trabajamos con el patrón MVVM, no tenemos acceso directo a la interfaz de usuario, por lo tanto, establecer el foco de un control visual se complica un poco. En esta pequeña entrada muestro como poder hacerlo. Simplemente utilizamos una propiedad para tal efecto :)
Utilizando el código
Realizaremos los siguientes pasos para lograr nuestro objetivo:
- Creamos un Modelo que se llama Club.
- Creamos el ViewModel con una propiedad Foco, que enviará las notificaciones a la Vista.
- En la Vista agregamos un Trigger al estilo.
Primero creamos nuestro modelo de esta manera:
public class Club
{
int orden;
public int Orden
{
get
{
return orden;
}
set
{
orden = value;
}
}
string nombre;
public string Nombre
{
get
{
return nombre;
}
set
{
nombre = value;
}
}
}
Ahora, procedemos a crear nuestro ViewModel tal como se demuestra a continuación:
public class ClubViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
ObservableCollection clubes;
public ObservableCollection Clubes
{
get
{
if (clubes == null)
this.clubes = new ObservableCollection();
return this.clubes;
}
set
{
this.clubes = value;
OnPropertyChanged("Clubes");
}
}
public ClubViewModel()
{
var lista = new List();
lista.Add(new Club()
{
Orden = 1, Nombre = "Manchester United"
});
lista.Add(new Club()
{
Orden = 2, Nombre = "Liverpool"
});
lista.Add(new Club()
{
Orden = 3, Nombre = "Chelsea"
});
lista.Add(new Club()
{
Orden = 4, Nombre = "Manchester City"
});
lista.Add(new Club()
{
Orden = 5, Nombre = "Arsenal"
});
Clubes = new ObservableCollection(lista);
Foco = true;
}
bool foco_;
public bool Foco
{
get
{
return foco_;
}
set
{
foco_ = value;
OnPropertyChanged("Foco");
}
}
}
El XAML sin nuestro Trigger quedaría de la siguiente manera:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:datos="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<datos:ClubViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Filtro" Margin="2" />
<TextBox Name="texto" Margin="2" Text="{Binding ElementName=lista, Path=SelectedItem.Nombre}" />
</StackPanel>
<ListView Name="lista" Grid.Row="1" Margin="5" ItemsSource="{Binding Clubes}"
DisplayMemberPath="Nombre" SelectedValue="Orden" SelectedItem="{Binding Clubes[0]}" />
</Grid>
</Window>
Lo que debemos agregar a nuestro Grid es el Trigger correspondiente:
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Foco}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=texto}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
El XAML final quedaría de la siguiente manera:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:datos="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<datos:ClubViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Foco}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=texto}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Filtro" Margin="2" />
<TextBox Name="texto" Margin="2" Text="{Binding ElementName=lista, Path=SelectedItem.Nombre}" />
</StackPanel>
<ListView Name="lista" Grid.Row="1" Margin="5" ItemsSource="{Binding Clubes}"
DisplayMemberPath="Nombre" SelectedValue="Orden" SelectedItem="{Binding Clubes[0]}" />
</Grid>
</Window>
Conclusión
De manera muy sencilla podemos colocar el foco en nuestro control mediante una propiedad que enlazamos al Trigger de un estilo.