it-swarm.com.ru

Выбор элемента текстового поля в списке не изменяет выбранный элемент списка

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

43
Zack

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

<Style TargetType="ListViewItem">
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="true">
            <Setter Property="IsSelected" Value="true" />
        </Trigger>
    </Style.Triggers>
</Style>
37
Grazer

Мы используем следующий стиль, чтобы установить 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;
    }
36
Arcturus

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

6
dotnetzen

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

Для этого

Первый. Создайте отдельный класс 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>
3
Ben

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

В вашем 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
oillio

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

Это не простое свойство, но вы можете обработать событие 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;
}
2
Robert Macnee
1
Natrium

Ниже приведено упрощение ответа @ 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
Graeme Wicksted

Простейший способ, который мне удалось найти, - это использовать событие 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;
}

xAML

    ...
    <ComboBox PreviewMouseDown="Element_PreviewMouseDown"/>
    <TextBox PreviewMouseDown="Element_PreviewMouseDown"/>
    ...
1
Mark Synowiec

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

Решение Бена имеет ту же проблему, что и решение Грейзера. Плохо то, что выбор зависит от фокуса [клавиатуры] текстового поля. Если у вас есть другой элемент управления в вашем диалоговом окне (то есть кнопка), при нажатии кнопки фокус теряется, и элемент списка становится невыбранным (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);
    }

В моем DataTemplate:

<TextBox Text="{Binding Name}"
        PreviewMouseDown="Element_PreviewMouseDown"/>
0
Sven Bardos

Я не совсем уверен, что вы захотите установить выбор непосредственно, как описано в предыдущем ответе, потому что я думаю, что это нарушит множественный выбор и некоторые другие сценарии

, Возможно, вы захотите попробовать рестайлинг кнопки, как показано ниже, и посмотрите, что произойдет.

<Button ClickMode="Pressed" Focusable="False">
<Button.Template>
    <ControlTemplate>  // change the template to get rid of all the default chrome 
        <Border Background="Transparent"> // Button won't be clickable without some kind of background set
            <ContentPresenter />
        </Border>
    </ControlTemplate>
</Button.Template>
<TextBox />

0
Steven

Попробуйте этот код:

foreach (object item in this.listBox1.Items) {
    if (textbox1.text.equals(item.toString())) {
        //show error message; break
    }
}
0
lincy oommen

Listbox обрабатывает выбор элемента, но не знает о фокусе текстового поля, встроенного в него. Если вы хотите изменить выбор всякий раз, когда текстовое поле получает фокус ввода, то вам нужно изменить выбор списка вручную, afaik.

0
Joey

Вы не очень конкретны в своей первоначальной ситуации. Но я предполагаю, что вы используете DataBinding и ItemTemplate. Это imho простой способ сделать это, а также, если ваш новичок в этой теме. Это должно работать:

<ListBox ItemsSource="{Binding someDataCollection}" Name="myListBox">
   <ListBox.ItemTemplate>
      <DataTemplate>
         <TextBox Text="{Binding datafield}" Tag="{Binding .}"
                  GotFocus="TextBox_GotFocus"/>
      </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
   myListBox.SelectedItem = (sender as TextBox).Tag; /* Maybe you need to cast to the type of the objects contained in the collection(bound as ItemSource above) */
}
0
Marcel B