2010-06-02 83 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> 
相關問題