2010-06-11 16 views
5

我已經爲我的ListBoxItems定義一個觸發器的風格來設置背景顏色,當IsSelected爲真:如何一起使用IsKeyboardFocusWithin和IsSelected?

<Style x:Key="StepItemStyle" TargetType="{x:Type ListBoxItem}"> 
     <Setter Property="SnapsToDevicePixels" Value="true"/> 
     <Setter Property="OverridesDefaultStyle" Value="true"/> 
     <Setter Property="Template"> 
      <Setter.Value> 
       <ControlTemplate TargetType="ListBoxItem"> 
        <Border Name="Border" Padding="0" SnapsToDevicePixels="true"> 
         <ContentPresenter /> 
        </Border> 
        <ControlTemplate.Triggers> 
         <Trigger Property="IsSelected" Value="True"> 
          <Setter TargetName="Border" Property="Background" Value="#40a0f5ff"/> 
         </Trigger> 
        </ControlTemplate.Triggers> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 

這種風格保持所選項目即使ListBoxListBoxItem失去焦點,這在我的情況是絕對必須。 問題是我還希望的TextBox的一個孩子專注。要做到這一點我想補充一個觸發器,它設定IsSelected爲真時IsKeyboardFocusWithin是正確的:

<Trigger Property="IsKeyboardFocusWithin" Value="True"> 
    <Setter Property="IsSelected" Value="True" /> 
</Trigger> 

當我添加此觸發時,重點是對孩子TextBox的項目被選中,但第一個行爲消失。現在當我點擊ListBox之外時,該項目被取消選擇。

我該如何保持這兩種行爲?

+0

美麗的僅限XAML解決方案:https://stackoverflow.com/a/15383435/419761 – l33t 2018-02-25 21:57:05

回答

6

當您的列表框失去焦點時,它會將您選擇的項目設置爲空,因爲您的觸發器。您可以使用後面的一些代碼來選擇焦點,在焦點鬆動時不會取消選擇。

XAML:

<Window x:Class="SelectedTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Height="300" Width="300"> 

    <StackPanel> 
     <TextBox Text="Loose focus here" /> 
     <ListBox Name="_listBox" ItemsSource="{Binding Path=Items}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel Orientation="Horizontal" GotFocus="OnChildGotFocus"> 
         <TextBox Text="{Binding .}" Margin="10" /> 
         <TextBox Text="{Binding .}" Margin="10" /> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
      <ListBox.ItemContainerStyle> 
       <Style TargetType="{x:Type ListBoxItem}"> 
        <Setter Property="SnapsToDevicePixels" Value="true"/> 
        <Setter Property="OverridesDefaultStyle" Value="true"/> 
        <Setter Property="Template"> 
         <Setter.Value> 
          <ControlTemplate TargetType="ListBoxItem"> 
           <Border Name="Border" SnapsToDevicePixels="true" Background="Transparent"> 
            <ContentPresenter /> 
           </Border> 
           <ControlTemplate.Triggers> 
            <Trigger Property="IsSelected" Value="True"> 
             <Setter TargetName="Border" Property="Background" Value="Red"/> 
            </Trigger>         
           </ControlTemplate.Triggers> 
          </ControlTemplate> 
         </Setter.Value> 
        </Setter> 
       </Style> 
      </ListBox.ItemContainerStyle> 
     </ListBox> 
    </StackPanel> 
</Window> 

後面的代碼:

private void OnChildGotFocus(object sender, RoutedEventArgs e) 
{ 
    _listBox.SelectedItem = (sender as StackPanel).DataContext; 
} 
+0

非常感謝!這正是我所期待的。 – jpsstavares 2010-06-11 13:45:55

4

我想通了,IsKeyboardFocusWithin是不是最好的解決方案。

我在這種情況下做的是在所有用作DataTemplate的控件上設置樣式,以發送GotFocus -event在代碼後面處理。然後,在後面的代碼中,我搜索了可視化樹(使用VisualTreeHelper)查找ListViewItem並將IsSelected設置爲true。這樣它就不會「觸摸」DataContext並只與View元素一起工作。

<Style TargetType="{x:Type Control}" x:Key="GridCellControlStyle"> 
... 
<EventSetter Event="GotFocus" Handler="SelectListViewItemOnControlGotFocus"/> 
... 

private void SelectListViewItemOnControlGotFocus(object sender, RoutedEventArgs e) 
{ 
var control = (Control)sender; 
FocusParentListViewItem(control); 
} 

private void FocusParentListViewItem(Control control) 
{ 
var listViewItem = FindVisualParent<ListViewItem>(control); 
if (listViewItem != null) 
    listViewItem.IsSelected = true; 
} 

public static T FindVisualParent<T>(UIElement element) where T : UIElement 
{ 
UIElement parent = element; 

while (parent != null) 
{ 
    var correctlyTyped = parent as T; 

    if (correctlyTyped != null) 
    { 
     return correctlyTyped; 
    } 

    parent = VisualTreeHelper.GetParent(parent) as UIElement; 
} 

return null; 
} 
4

「當我加入這個觸發時,重點是對孩子的TextBox的項目被選中,但第一個行爲消失。現在,當我點擊列表框外,該項目將被取消選擇。」

其實,我不認爲它已經失去了原來的行爲。我懷疑發生的是你直接從其他地方的文本框中點擊,所以底層ListBoxItem實際上從未被選中。但是,如果確實如此,您會看到選擇在您離開後仍然保留。

您可以通過強制ListBoxItem通過直接點擊來選擇它(側注意:您應該始終給它一個背景,即使只是'透明',因此它可以接受鼠標點擊,它贏得'如果它爲空),或者甚至只是點擊「Shift-Tab」來在那裏設置焦點,從文本框返回。

但是,這並不能解決您的問題,即TextBox獲得焦點但不會讓底層的ListBoxItem知道它。

您可以使用的兩種方法是事件觸發器或附加行爲。

第一個是IsKeyboardFocusWithinChanged事件的事件觸發器,如果​​鍵盤焦點更改爲true,則將'IsSelected'設置爲true。 (注意:Sheridan的答案會執行人爲更改通知,但不應在可以在列表中多選的情況下使用,因爲所有內容都會被選中。)但即使是事件觸發器也會導致問題,因爲您失去了多選行爲如切換或範圍點擊等

其他(我的首選方法)是編寫一個附加的行爲,您可以直接在ListBoxItem上設置,或者如果您願意,可以通過樣式設置。

下面是附加的行爲。注意:如果你想實現這個,你又需要處理多選的東西。另外請注意,雖然我將行爲附加到ListBoxItem,但在我投入到UIElement中。這樣你也可以在ComboBoxItem,TreeViewItem等中使用它。基本上,基於選擇器的控件中的任何ContainerItem。

public class AutoSelectWhenAnyChildGetsFocus 
{ 
    public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached(
     "Enabled", 
     typeof(bool), 
     typeof(AutoSelectWhenAnyChildGetsFocus), 
     new UIPropertyMetadata(false, Enabled_Changed)); 

    public static bool GetEnabled(DependencyObject obj){ return (bool)obj.GetValue(EnabledProperty); } 
    public static void SetEnabled(DependencyObject obj, bool value){ obj.SetValue(EnabledProperty, value); } 

    private static void Enabled_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     var attachEvents = (bool)e.NewValue; 
     var targetUiElement = (UIElement)sender; 

     if(attachEvents) 
      targetUiElement.IsKeyboardFocusWithinChanged += TargetUiElement_IsKeyboardFocusWithinChanged; 
     else 
      targetUiElement.IsKeyboardFocusWithinChanged -= TargetUiElement_IsKeyboardFocusWithinChanged; 
    } 

    static void TargetUiElement_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     var targetUiElement = (UIElement)sender; 

     if(targetUiElement.IsKeyboardFocusWithin) 
      Selector.SetIsSelected(targetUiElement, true); 
    } 

} 

...你只需添加這是你的ListBoxItem中的風格

<Setter Property="behaviors:AutoSelectWhenAnyChildGetsFocus.Enabled" Value="True" /> 

這當然是一個屬性setter假定您已經導入所謂的「行爲」的XML命名空間中指向命名空間包含類的地方。你可以把這個類放在一個共享的'Helper'庫中,這就是我們所做的。這樣,無論我們想要什麼,它都是在XAML中設置的一個簡單屬性,並且該行爲負責處理所有其他事情。

相關問題