web-dev-qa-db-fra.com

La sélection d'un élément de zone de texte dans une zone de liste ne modifie pas l'élément sélectionné de la liste

J'ai une wpf Listbox qui affiche une liste de zones de texte. Lorsque je clique sur la zone de texte, la sélection de la liste ne change pas. Je dois cliquer à côté de la zone de texte pour sélectionner l'élément de la liste. Dois-je définir une propriété pour que la zone de texte transfère l'événement click à la zone de liste?

43
Zack

Veillez à utiliser le type de cible approprié: ListViewItem, ListBoxItem ou TreeViewItem. 

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

Nous utilisons le style suivant pour définir un PreviewGotKeyboardFocus qui gère tous les événements du contrôle TextBox et des ComboBox et autres:

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

Et puis on sélectionne la ligne dans le code derrière:

    protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
    {
        ListViewItem item = (ListViewItem) sender;
        item.IsSelected = true;
    }
36
Arcturus

Je n'ai pas assez de représentants pour commenter, alors je poste mon commentaire comme réponse. La solution de Grazer ci-dessus ne fonctionne pas dans les cas où vous avez un autre contrôle, tel que Button, qui nécessite SelectedItem. En effet, conformément au Style Trigger, la IsKeyboardFocusWithin devient fausse lorsque vous cliquez sur cette Button et la SelectedItem devient nulle.

6
dotnetzen

J'ai utilisé similaire à la solution de Robert, mais sans code derrière (en utilisant le comportement attaché).

Faire cela,

Premier. Créez une classe distincte 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
    }
}

Seconde. Ajouter un style dans la section des ressources (mon style est noir arrondi au premier plan). Outil de notification pour la propriété FocusBehaviour.IsListBoxItemSelected. Vous devriez le référencer dans 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>

`

Troisième. (facultatif, pour tâche inverse)

Vous rencontrerez, sinon aucune tâche inversée - en vous concentrant sur TextBox lorsque ListBoxItem sera sélectionné. Je recommande d'utiliser une autre propriété de la classe Behavior, IsFocused. Voici un exemple de modèle pour ListBoxItem, notez svp Property="behave:FocusBehaviour.IsFocused" et 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

J'utilise un gestionnaire de classe pour définir ce comportement. En procédant de cette façon, toutes les vues de liste de l’application seront corrigées. Je ne sais pas pourquoi ce n'est pas le comportement par défaut.

Dans votre App.xaml.cs, ajoutez les éléments suivants à OnStartup:

protected override void OnStartup(StartupEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof (ListViewItem), 
                                          ListViewItem.PreviewGotKeyboardFocusEvent,
                                          new RoutedEventHandler((x,_) => (x as ListViewItem).IsSelected = true));
    }
3
oillio

Dois-je définir une propriété pour que la zone de texte transfère l'événement click à la zone de liste?

Ce n'est pas une simple propriété, mais vous pouvez gérer l'événement GotFocus sur votre TextBox, puis utilisez VisualTreeHelper pour rechercher le ListBoxItem et le sélectionner:

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

Ce qui suit est une simplification de la réponse de @ Ben sans avoir à remplacer le DataTemplate. Il peut même être appliqué en tant que style statique. Testé avec un ListView contenant un GridView > GridViewColumn > TextBox.

Exemple:

<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

Le moyen le plus simple que j'ai pu trouver pour ce faire consiste à utiliser l'événement PreviewMouseDown et à définir la propriété IsSelected du parent modèle. Comme les événements de prévisualisation diminuent, le ListBoxItem traite l'événement dès que l'utilisateur clique sur la zone de texte, la liste déroulante ou tout autre contrôle sur lequel vous définissez l'événement.

Une bonne chose à ce sujet est que vous pouvez utiliser le même événement pour tous les types de contrôles car ils dérivent tous de l’élément Framework. En outre, si vous définissez IsSelected (au lieu de définir SelectedItem), plusieurs éléments seront sélectionnés lorsque vous définissez le SelectionMode de la zone de liste sur "Extended", ce qui peut être ou ne pas être ce que vous recherchez.

c'est à dire:

c # code

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

Vieille discussion, mais peut-être que ma réponse aidera les autres ...

La solution de Ben a le même problème que la solution de Grazer. La mauvaise chose est que la sélection dépend du focus [clavier] de la zone de texte. Si vous avez un autre contrôle sur votre boîte de dialogue (un bouton, par exemple), le focus est perdu lorsque vous cliquez sur le bouton et l'élément de liste est désélectionné (SelectedItem == null). Vous avez donc un comportement différent en cliquant sur l'élément (en dehors de la zone de texte) et en cliquant dans la zone de texte. Ceci est très fastidieux à gérer et semble très étrange.

Je suis presque sûr qu'il n'y a pas de solution XAML pure pour cela. Nous avons besoin de code-behind pour cela. La solution est proche de celle suggérée par Mark.

(Dans mon exemple, j'utilise ListViewItem au lieu de ListBoxItem, mais la solution fonctionne pour les deux).

Code-behind:

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

avec FindParent (extrait de 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);
    }

Dans mon DataTemplate:

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

Je ne suis pas tout à fait sûr que vous souhaitiez définir directement la sélection comme décrit dans la réponse précédente, car je pense que cela romprait la multisélection et certains autres scénarios

. Vous voudrez peut-être essayer de restyler un bouton comme ci-dessous et voir ce qui se passe.

<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

Essayez ce code:

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

La zone de liste gère la sélection d'éléments, mais ne connaît pas le focus de la zone de texte incorporée à l'intérieur. Si vous souhaitez modifier la sélection chaque fois qu'une zone de texte devient active, vous devez modifier la sélection manuellement, si vous le souhaitez.

0
Joey

Votre pas très spécifique sur votre situation initiale. Mais je suppose que vous utilisez DataBinding et un ItemTemplate. C’est un moyen facile de le faire, même si vous débutez sur ce sujet. Cela devrait fonctionner:

<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