'Select ListViewItem when child element has focus UWP
I'm writing a Universal Windows App and I have a ListView where the ListViewItems contain a TextBox and a Button. When I click in the text box I would like that ListViewItem to become selected. I've found solutions for WPF but Style.Triggers isn't available in UWP. Can anyone point me to the correct way to do this?
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:CycleStreetsUniversal.Controls"
xmlns:common="using:CycleStreetsUniversal.Common"
xmlns:utils="using:CycleStreetsUniversal.Utils"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:converters="using:CycleStreetsUniversal.Converters"
x:Class="CycleStreetsUniversal.Pages.HomePage"
mc:Ignorable="d" FontWeight="Light">
<Page.Resources>
<DataTemplate x:Key="DirectionItem">
<Grid Padding="8,6,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<AutoSuggestBox x:Name="autoSuggestBox" PlaceholderText="{Binding Watermark}" QueryIcon="Find" Text="{Binding LocationName}" />
<Button Grid.Column="2" Visibility="{Binding ShowAddButton, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Button Grid.Column="1" Visibility="{Binding ShowMinusButton, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="Directions" HorizontalAlignment="Left" Margin="0" Width="346" DataContext="{Binding DirectionPlanner, Mode=OneWay, Source={StaticResource Locator}}">
<Grid.Background>
<SolidColorBrush Color="{ThemeResource SystemAltHighColor}"/>
</Grid.Background>
<StackPanel VerticalAlignment="Top">
<ListView x:Name="DirectionEntryList" ItemTemplate="{StaticResource DirectionItem}" ItemsSource="{Binding Entries}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Button x:Name="crosshairButton" VerticalAlignment="Top" d:LayoutOverrides="LeftPosition, RightPosition" Margin="20,0" HorizontalAlignment="Stretch" Padding="0" Click="crosshairButton_Click">
<Grid Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image x:Name="image" Source="ms-appx:///Assets/crosshair.png"/>
<TextBlock Text="Set Location to Crosshair" Grid.Column="1" VerticalAlignment="Center" MaxLines="2" TextWrapping="Wrap"/>
</Grid>
</Button>
</StackPanel>
</Grid>
</Grid>
</Page>
The AutoSuggestBox in the data template needs to set the selected item in DirectionEntryList to the List View item that the AutoSuggestBox is a child of.
Solution 1:[1]
Code behind solution
What you can do is to subscribe to the AutoSuggestBox
's GotFocus
event.
<AutoSuggestBox x:Name="autoSuggestBox" GotFocus="autoSuggestBox_GotFocus" />
Then, you just need to use the ListView.ContainerFromItem
method to locate the actual ListViewItem
and set its IsSelected
property to true
.
private void autoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
{
var item = ((AutoSuggestBox)sender).DataContext;
var container = (ListViewItem)DirectionEntryList.ContainerFromItem(item);
container.IsSelected = true;
}
Blend friendly solution (no code behind)
Let's improve this answer a bit by encapsulating the logic into a Behavior
.
First, you need to add Behaviors SDK(XAML) (version 12.0 atm) from the Reference Manager > Universal Windows > Extensions.
Then basically you just need to create a dependency property to obtain a reference of the DirectionEntryList
and handle the GotFocus
event exactly the same way as in the code behind.
public class SelectListViewItemWhenElementGotFocusBehavior : DependencyObject, IBehavior
{
private UIElement _element;
public DependencyObject AssociatedObject { get; set; }
#region ListView reference
public ListView ListView
{
get { return (ListView)GetValue(ListViewProperty); }
set { SetValue(ListViewProperty, value); }
}
public static readonly DependencyProperty ListViewProperty =
DependencyProperty.Register("ListView", typeof(ListView), typeof(SelectListViewItemWhenElementGotFocusBehavior), new PropertyMetadata(null));
#endregion
public void Attach(DependencyObject associatedObject)
{
AssociatedObject = associatedObject;
_element = this.AssociatedObject as UIElement;
if (_element != null)
{
_element.GotFocus += OnElementGotFocus;
}
}
private void OnElementGotFocus(object sender, RoutedEventArgs e)
{
var item = ((AutoSuggestBox)sender).DataContext;
var container = (ListViewItem)ListView.ContainerFromItem(item);
container.IsSelected = true;
}
public void Detach()
{
if (_element != null)
{
_element.GotFocus -= OnElementGotFocus;
}
}
}
To use it, just open Blend, go to the DataTemplate
and attach it to your AutoSuggestBox
.
<AutoSuggestBox x:Name="autoSuggestBox">
<Interactivity:Interaction.Behaviors>
<local:SelectListViewItemWhenElementGotFocusBehavior ListView="{Binding ElementName=DirectionEntryList}" />
</Interactivity:Interaction.Behaviors>
</AutoSuggestBox>
Solution 2:[2]
When I click in the text box I would like that ListViewItem to become selected. I've found solutions for WPF but Style.Triggers isn't available in UWP.
In UWP, you can set the styles by using the ViewState.Setters and trigger the state change through GotFocus & LostFocus events.
For example:
<Page
x:Class="UWPApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="container"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ValueStates">
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Target="button.Background" Value="Red"></Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="UnSelected">
<VisualState.Setters>
<Setter Target="button.Background" Value="Blue"></Setter>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel>
<TextBox x:Name="inputbox" GotFocus="inputbox_GotFocus" LostFocus="inputbox_LostFocus"></TextBox>
<Button x:Name="button">Click Me</Button>
</StackPanel>
</Grid>
</Page>
C# Code:
private void inputbox_GotFocus(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
VisualStateManager.GoToState(this, "Selected", false);
}
private void inputbox_LostFocus(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this, "UnSelected", false);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Jeffrey Chen |