Выбор элемента текстового поля в списке не изменяет выбранный элемент списка - PullRequest
43 голосов
/ 17 марта 2009

У меня есть список wpf, который отображает список текстовых полей. Когда я нажимаю на Textbox, выбор Listbox не меняется. Я должен нажать рядом с TextBox, чтобы выбрать элемент списка. Нужно ли установить какое-либо свойство для Textbox для пересылки события click в Listbox?

Ответы [ 14 ]

38 голосов
/ 14 мая 2009

Мы используем следующий стиль, чтобы установить PreviewGotKeyboardFocus, который обрабатывает все события элемента управления TextBox и ComboBox и тому подобное:

    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
        </Style>
    </ListView.ItemContainerStyle>

И затем мы выбираем строку в коде позади:

    protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
    {
        ListViewItem item = (ListViewItem) sender;
        item.IsSelected = true;
    }
37 голосов
/ 03 ноября 2011

Обязательно используйте соответствующий TargetType: ListViewItem, ListBoxItem или TreeViewItem.

<Style TargetType="ListViewItem">
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="true">
            <Setter Property="IsSelected" Value="true" />
        </Trigger>
    </Style.Triggers>
</Style>
6 голосов
/ 23 декабря 2013

Мне не хватает представителей, чтобы комментировать, поэтому я публикую свой комментарий как ответ. Приведенное выше решение Grazer не работает в тех случаях, когда у вас есть другой элемент управления, например Button, для которого требуется SelectedItem. Это потому, что согласно Style Trigger, IsKeyboardFocusWithin становится ложным, когда вы нажимаете на Button, а SelectedItem становится нулевым.

3 голосов
/ 16 июня 2011

Я использую обработчик класса, чтобы установить это поведение. Делая это таким образом, вы исправите все представления списка в приложении. Я не знаю, почему это не стандартное поведение.

В файле App.xaml.cs добавьте в OnStartup следующее:

protected override void OnStartup(StartupEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof (ListViewItem), 
                                          ListViewItem.PreviewGotKeyboardFocusEvent,
                                          new RoutedEventHandler((x,_) => (x as ListViewItem).IsSelected = true));
    }
3 голосов
/ 10 ноября 2010

Я использовал аналогичное решение Роберта, но без кода позади (используя прикрепленное поведение).

Для этого

Во-первых. Создайте отдельный класс FocusBehaviour:


using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyBehaviours
{
    public class FocusBehaviour
    {
        #region IsFocused
        public static bool GetIsFocused(Control control)
        {
            return (bool) control.GetValue(IsFocusedProperty);
        }

        public static void SetIsFocused(Control control, bool value)
        {
            control.SetValue(IsFocusedProperty, value);
        }

        public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached(
            "IsFocused", 
            typeof(bool),
            typeof(FocusBehaviour), 
            new UIPropertyMetadata(false, IsFocusedPropertyChanged));

        public static void IsFocusedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = sender as Control;
            if (control == null || !(e.NewValue is bool))
                return;
            if ((bool)e.NewValue && !(bool)e.OldValue)
                control.Focus();
        }

        #endregion IsFocused

        #region IsListBoxItemSelected

        public static bool GetIsListBoxItemSelected(Control control)
        {
            return (bool) control.GetValue(IsListBoxItemSelectedProperty);
        }

        public static void SetIsListBoxItemSelected(Control control, bool value)
        {
            control.SetValue(IsListBoxItemSelectedProperty, value);
        }

        public static readonly DependencyProperty IsListBoxItemSelectedProperty = DependencyProperty.RegisterAttached(
            "IsListBoxItemSelected", 
            typeof(bool),
            typeof(FocusBehaviour), 
            new UIPropertyMetadata(false, IsListBoxItemSelectedPropertyChanged));

        public static void IsListBoxItemSelectedPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var control = sender as Control;
            DependencyObject p = control;
            while (p != null && !(p is ListBoxItem))
            {
                p = VisualTreeHelper.GetParent(p);
            } 

            if (p == null)
                return;

            ((ListBoxItem)p).IsSelected = (bool)e.NewValue;
        }

        #endregion IsListBoxItemSelected
    }
}

Во-вторых. Добавьте стиль в разделе ресурсов (мой стиль округлен на черном фоне). Установщик уведомлений для свойства FocusBehaviour.IsListBoxItemSelected. Вы должны ссылаться на это в xmlns:behave="clr-namespace:MyBehaviours"

`

    <Style x:Key="PreviewTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="1"/>
        <Setter Property="AllowDrop" Value="true"/>
        <Setter Property="Background" Value="White"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Border
                        Margin="6,2,0,4"
                        BorderBrush="#FFBDBEBD"
                        BorderThickness="1"
                        CornerRadius="8"
                        Background="White"
                        VerticalAlignment="Stretch"
                        HorizontalAlignment="Stretch"
                        MinWidth="100"
                        x:Name="bg">
                        <ScrollViewer 
                            x:Name="PART_ContentHost" 
                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsKeyboardFocusWithin" Value="True">
                            <Setter Property="Background" TargetName="bg" Value="Black"/>
                            <Setter Property="Background" Value="Black"/><!-- we need it for caret, it is black on black elsewise -->
                            <Setter Property="Foreground" Value="White"/>
                            <Setter Property="behave:FocusBehaviour.IsListBoxItemSelected" Value="True"/>
                        </Trigger>

                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

`

В-третьих. (необязательно, для обратной задачи)

Вы встретитесь, если не когда-либо, с обратной задачей - сосредоточиться на TextBox, когда будет выбран ListBoxItem. Я рекомендую использовать другое свойство класса Behavior, IsFocused. Вот пример шаблона для ListBoxItem, обратите внимание Property="behave:FocusBehaviour.IsFocused" и FocusManager.IsFocusScope="True"

    <DataTemplate x:Key="YourKey" DataType="{x:Type YourType}">
            <Border
            Background="#FFF7F3F7"
            BorderBrush="#FFBDBEBD"
            BorderThickness="0,0,0,1"
            FocusManager.IsFocusScope="True"
            x:Name="bd"
            MinHeight="40">
                <TextBox
                    x:Name="textBox"
                    Style="{StaticResource PreviewTextBox}"
                    Text="{Binding Value}" />
        </Border>
        <DataTemplate.Triggers>
            <DataTrigger
                Binding="{Binding IsSelected,RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                Value="True">
                <Setter
                    TargetName="textBox"
                    Property="behave:FocusBehaviour.IsFocused" 
                    Value="True" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
2 голосов
/ 17 марта 2009

Нужно ли установить какое-либо свойство для текстового поля, чтобы перенаправить событие щелчка в список?

Это не простое свойство, но вы можете обработать событие GotFocus на вашем TextBox, а затем использовать VisualTreeHelper , чтобы найти ListBoxItem и выбрать его:

private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
    TextBox myTextBox = sender as TextBox;
    DependencyObject parent = VisualTreeHelper.GetParent(myTextBox);
    while (!(parent is ListBoxItem))
    {
        parent = VisualTreeHelper.GetParent(parent);
    }
    ListBoxItem myListBoxItem = parent as ListBoxItem;
    myListBoxItem.IsSelected = true;
}
1 голос
/ 31 января 2017

Ниже приведено упрощение ответа @ Ben без необходимости переопределять шаблон данных. Это может даже быть применено как статический стиль. Протестировано с ListView, содержащим GridView > GridViewColumn > TextBox.

Пример:

<ListView.Resources>
    <Style TargetType="{x:Type ListViewItem}">
        <Style.Triggers>
            <Trigger Property="IsKeyboardFocusWithin" Value="True">
                <Setter Property="IsSelected" Value="True"></Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
</ListView.Resources>
1 голос
/ 27 октября 2009
1 голос
/ 04 апреля 2009

Самый простой способ, который мне удалось найти, - это использовать событие PreviewMouseDown и установить свойство IsSelected шаблонного родителя. Так как события предварительного просмотра всплывают вниз, ListBoxItem обработает событие, как только пользователь щелкнет текстовое поле, комбинированный список или любой другой элемент управления, для которого вы установили событие.

Одна приятная вещь в этом заключается в том, что вы можете использовать одно и то же событие для всех типов элементов управления, поскольку все они происходят от элемента Framework. Кроме того, установка IsSelected (вместо установки SelectedItem) приведет к тому, что несколько элементов будут выбраны, когда вы установите для SelectionMode списка значение «Extended», которое может или не может быть тем, что вы ищете.

есть:

c # код

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    ((sender as FrameworkElement).TemplatedParent as ListBoxItem).IsSelected = true;
}

1010 * XAML *

    ...
    <ComboBox PreviewMouseDown="Element_PreviewMouseDown"/>
    <TextBox PreviewMouseDown="Element_PreviewMouseDown"/>
    ...
0 голосов
/ 09 января 2015

Старая дискуссия, но, возможно, мой ответ поможет другим ....

Решение Бена имеет ту же проблему, что и решение Грейзера. Плохо то, что выбор зависит от фокуса [клавиатуры] текстового поля. Если в вашем диалоговом окне есть другой элемент управления (то есть кнопка), при нажатии кнопки фокус теряется, а элемент списка становится невыбранным (SelectedItem == null). Таким образом, вы можете по-разному щелкнуть элемент (вне текстового поля) и щелкнуть в текстовом поле. Это очень утомительно и выглядит очень странно.

Я совершенно уверен, что для этого не существует чистого решения XAML. Нам нужен код для этого. Решение близко к тому, что предложил Марк.

(в моем примере я использую ListViewItem вместо ListBoxItem, но решение работает для обоих).

Код-за:

private void Element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        var frameworkElement = sender as FrameworkElement;
        if (frameworkElement != null)
        {
            var item = FindParent<ListViewItem>(frameworkElement);
            if (item != null)
                item.IsSelected = true;
        }
    }

с FindParent (взято из http://www.infragistics.com/community/blogs/blagunas/archive/2013/05/29/find-the-parent-control-of-a-specific-type-in-wpf-and-silverlight.aspx):

public static T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        //get parent item
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        T parent = parentObject as T;
        if (parent != null)
            return parent;

        return FindParent<T>(parentObject);
    }

В моем шаблоне данных:

<TextBox Text="{Binding Name}"
        PreviewMouseDown="Element_PreviewMouseDown"/>
...