代码之家  ›  专栏  ›  技术社区  ›  jpsstavares

如何同时使用IsKeyboardFocusWithin和IsSelected?

  •  5
  • jpsstavares  · 技术社区  · 16 年前

    ListBoxItems 使用触发器设置背景色 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>
    

    即使在 ListBox ListBoxItem 失去焦点,在我看来这是绝对必须的。 问题是我也想要 ListBoxItem项目 当其中一个 TextBox 我当选了 什么时候变为真 IsKeyboardFocusWithin 是真的:

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

    当我添加这个触发器时,当焦点在一个子对象上时,项目被选中 ,但第一个行为消失了。现在当我在外面点击 列表框 ,则取消选中该项。

    我如何保持这两种行为?

    3 回复  |  直到 16 年前
        1
  •  6
  •   Wallstreet Programmer    16 年前

    当您的列表框失去焦点时,它会因为您的触发器而将所选项目设置为null。您可以使用一些代码来选择焦点,这些代码在您失去焦点时不会取消选择。

    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; 
    }
    
        2
  •  5
  •   Mark A. Donohoe    9 年前

    “当我添加此触发器时,当焦点在子文本框上时,项目被选中,但第一个行为消失。现在,当我在列表框外单击时,该项被取消选中。“

    事实上,我不认为它已经失去了原来的行为。我怀疑发生的事情是你直接从其他地方点击文本框,所以底层的ListBoxItem实际上从未被选中。但是,如果是这样的话,您会看到选择在您想离开时仍然保留。

    你可以通过直接点击ListBoxItem来测试它(旁注:你应该给它一个背景,即使只是“透明”的,这样它就可以接收鼠标点击,如果它是空的,它就不会),或者仅仅点击“Shift Tab”来设置焦点,从文本框返回。

    但是,这并不能解决您的问题,即TextBox获得焦点,但不让底层ListBoxItem知道它。

    您可以使用两种方法来实现这一点:事件触发器或附加的行为。

    以下是附带的行为。注意:如果您想实现这个功能,您还需要处理multi-select。还要注意的是,虽然我将行为附加到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);
        }
    
    }
    

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

    当然,这假设您已经导入了一个名为“behaviors”的XML名称空间,该名称空间指向包含类的名称空间。您可以将类本身放入一个共享的“Helper”库中,我们就是这么做的。这样,无论我们在哪里需要它,它都是XAML中设置的一个简单属性,行为会处理其他所有事情。

        3
  •  4
  •   Spontifixus    13 年前

    IsKeyboardFocusWithin

    在本例中,我所做的是在用作数据模板的所有控件上设置样式,以发送 GotFocus -要在代码隐藏中处理的事件。然后,在代码隐藏中,我搜索了可视化树(使用 VisualTreeHelper )找到 ListViewItem 并设置 IsSelected true . 这样,它就不会“接触”DataContext,只处理视图元素。

    <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;
    }