2010-06-02 71 views
13

我有一个典型的MVVM方案: 我有一个列表框绑定到StepsViewModels列表。 我定义了一个DataTemplate,使StepViewModel呈现为StepViews。 StepView UserControl有一组标签和文本框。设置ListBoxItem.IsSelected当子TextBox聚焦

我想要做的是选择ListBoxItem,当一个文本框被聚焦时,它包装StepView。我试图创建一个样式为我TextBoxs具有以下触发:

<Trigger Property="IsFocused" Value="true"> 
    <Setter TargetName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Property="IsSelected" Value="True"/> 
</Trigger> 

但我得到一个错误,告诉我,TextBoxs没有一个IsSelected属性。我现在说,但目标是一个ListBoxItem。 我该如何使它工作?

+0

你可以给描述了整个结构的XAML代码(文本框,列表框) – Amsakanna 2010-06-02 18:04:15

+0

我?刚刚发布的解决方案,为我工作:http://stackoverflow.com/questions/15366806/wpf-setting-isselected-for-listbox-when-textbox-has-focus-without-losing-selec/37942357#37942357 – 2016-06-21 10:53:41

回答

27

有一个只读属性IsKeyboardFocusWithin,如果有任何孩子被聚焦,它将被设置为true。如果您创建一个用户控件

<ListBox ItemsSource="{Binding SomeCollection}" HorizontalAlignment="Left"> 
    <ListBox.ItemContainerStyle> 
     <Style TargetType="{x:Type ListBoxItem}"> 
      <Style.Triggers> 
       <Trigger Property="IsKeyboardFocusWithin" Value="True"> 
        <Setter Property="IsSelected" Value="True" /> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </ListBox.ItemContainerStyle> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <TextBox Width="100" Margin="5" Text="{Binding Name}"/> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 
+0

非常感谢!这正是我所期待的。 – jpsstavares 2010-06-04 09:16:12

+12

这种方法有一个非常大的“陷阱” - 当你的应用程序本身失去焦点时,IsSelected将被设置为false。也就是说,我单击列表框内的文本框,但切换到另一个应用程序(比如说在我的浏览器中回答StackOverflow问题),然后切换回您的应用程序... IsSelected属性将设置为true,false,true,而不是只保留整个时间。如果您将行为从ListBox的SelectedItem属性中驱除,这可能会造成非常大的问题。 – Jordan0Day 2011-05-13 21:27:50

+0

@Jordan0Day是的,这也钉住了我。如果其他任何东西都获得焦点(即使WPF应用程序中有其他控件),ListBoxItem也会被取消选中。这个答案解决了它:http://stackoverflow.com/a/15383435/466011 – epalm 2013-12-10 20:29:10

2

实现该目的的一种方法是通过使用附加属性实现自定义行为。基本上,附加的财产将应用于ListBoxItem使用一种风格,并将挂钩到他们的GotFocus事件。如果控制的后代获得焦点,甚至会触发,所以它适用于此任务。在事件处理程序中,IsSelected设置为true

我写了一个小例子给你:

的行为等级:

public class MyBehavior 
{ 
    public static bool GetSelectOnDescendantFocus(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(SelectOnDescendantFocusProperty); 
    } 

    public static void SetSelectOnDescendantFocus(
     DependencyObject obj, bool value) 
    { 
     obj.SetValue(SelectOnDescendantFocusProperty, value); 
    } 

    public static readonly DependencyProperty SelectOnDescendantFocusProperty = 
     DependencyProperty.RegisterAttached(
      "SelectOnDescendantFocus", 
      typeof(bool), 
      typeof(MyBehavior), 
      new UIPropertyMetadata(false, OnSelectOnDescendantFocusChanged)); 

    static void OnSelectOnDescendantFocusChanged(
     DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     ListBoxItem lbi = d as ListBoxItem; 
     if (lbi == null) return; 
     bool ov = (bool)e.OldValue; 
     bool nv = (bool)e.NewValue; 
     if (ov == nv) return; 
     if (nv) 
     { 
      lbi.GotFocus += lbi_GotFocus; 
     } 
     else 
     { 
      lbi.GotFocus -= lbi_GotFocus; 
     } 
    } 

    static void lbi_GotFocus(object sender, RoutedEventArgs e) 
    { 
     ListBoxItem lbi = sender as ListBoxItem; 
     lbi.IsSelected = true; 
    } 
} 

的窗口XAML:

<Window x:Class="q2960098.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:sys="clr-namespace:System;assembly=mscorlib" 
     Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:q2960098"> 
    <Window.Resources> 
     <DataTemplate x:Key="UserControlItemTemplate"> 
      <Border BorderBrush="Black" BorderThickness="5" Margin="10"> 
       <my:UserControl1/> 
      </Border> 
     </DataTemplate> 
     <XmlDataProvider x:Key="data"> 
      <x:XData> 
       <test xmlns=""> 
        <item a1="1" a2="2" a3="3" a4="4">a</item> 
        <item a1="a" a2="b" a3="c" a4="d">b</item> 
        <item a1="A" a2="B" a3="C" a4="D">c</item> 
       </test> 
      </x:XData> 
     </XmlDataProvider> 
     <Style x:Key="MyBehaviorStyle" TargetType="ListBoxItem"> 
      <Setter Property="my:MyBehavior.SelectOnDescendantFocus" Value="True"/> 
     </Style> 
    </Window.Resources> 
    <Grid> 
     <ListBox ItemTemplate="{StaticResource UserControlItemTemplate}" 
       ItemsSource="{Binding Source={StaticResource data}, XPath=//item}" 
       HorizontalContentAlignment="Stretch" 
       ItemContainerStyle="{StaticResource MyBehaviorStyle}"> 

     </ListBox> 
    </Grid> 
</Window> 

用户控件XAML:

<UserControl x:Class="q2960098.UserControl1" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300"> 
    <UniformGrid> 
     <TextBox Margin="10" Text="{Binding [email protected]}"/> 
     <TextBox Margin="10" Text="{Binding [email protected]}"/> 
     <TextBox Margin="10" Text="{Binding [email protected]}"/> 
     <TextBox Margin="10" Text="{Binding [email protected]}"/> 
    </UniformGrid> 
</UserControl> 
+0

谢谢对于你的答案,但鲍恩的答案用更少的代码来完成这项工作。非常感谢您的帮助! – jpsstavares 2010-06-04 09:17:17

+0

事实上,我并没有意识到这个属性,有很多:) +1他的回答以及 – 2010-06-04 12:17:16

1

,然后用它作为DataTemplate中看来工作吸尘器:您可以使用此设置ListBoxItem.IsSelected在触发器。 然后,您不必使用不能100%工作的脏风格触发器。

5

正如Jordan0Day正确指出的那样,使用IsKeyboardFocusWithin解决方案确实存在大问题。在我的情况下,关于ListBox的工具栏中的Button也不再工作。重点相同的问题。当单击按钮时,ListBoxItem会松开焦点,并且按钮更新其CanExecute方法,导致在执行按钮单击命令之前关闭按钮。

对我来说,一个更好的解决方案是使用一个ItemContainerStyle EventSetter在这篇文章中描述:ListboxItem selection when the controls inside are used

XAML:

<Style x:Key="MyItemContainer.Style" TargetType="{x:Type ListBoxItem}"> 
    <Setter Property="Background" Value="LightGray"/> 
    <EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" /> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
       <Border x:Name="backgroundBorder" Background="White"> 
        <ContentPresenter Content="{TemplateBinding Content}"/> 
       </Border> 
      <ControlTemplate.Triggers> 
       <Trigger Property="IsSelected" Value="True"> 
        <Setter TargetName="backgroundBorder" Property="Background" Value="#FFD7E6FC"/> 
       </Trigger> 
      </ControlTemplate.Triggers> 
     </ControlTemplate> 
    </Setter.Value> 
</Setter> 
</Style> 

事件处理程序中的代码视图的背后:

private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e) 
{ 
    (sender as ListBoxItem).IsSelected = true; 
} 
+0

这是做到这一点的正确方法。也应该从Dr.WPF看链接的social.MSDN帖子。 – Indy9000 2013-05-16 12:37:23

1

编辑:其他人在另一个问题上已经有相同的答案:https://stackoverflow.com/a/7555852/2484737

继续上Maexs的回答,用一个EventTrigger代替EventSetter的消除了对代码背后需要:

<Style.Triggers> 
    <EventTrigger RoutedEvent="GotKeyboardFocus"> 
     <BeginStoryboard> 
      <Storyboard > 
       <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected" > 
        <DiscreteBooleanKeyFrame Value="True" KeyTime="0:0:0"/> 
       </BooleanAnimationUsingKeyFrames> 
      </Storyboard> 
     </BeginStoryboard> 
    </EventTrigger> 
</Style.Triggers>