Pourquoi ne puis-je pas sélectionner une valeur nulle dans un ComboBox?

dans WPF, il semble impossible de sélectionner (avec la souris) une valeur "nulle" à partir d'un ComboBox. Modifier pour clarifier, C'est .NET 3.5 SP1.

voici quelques codes pour montrer ce que je veux dire. Premièrement, les déclarations C#:

public class Foo
{
    public Bar Bar { get; set; }
}

public class Bar 
{
    public string Name { get; set; }
}

Ensuite, mon Window1 XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" 
                  DisplayMemberPath="Name" 
                  Height="21" 
                  SelectedItem="{Binding Bar}"
                  />
    </StackPanel>
</Window>

et enfin, ma classe Window1:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        bars.ItemsSource = new ObservableCollection<Bar> 
        {
            null, 
            new Bar { Name = "Hello" }, 
            new Bar { Name = "World" } 
        };
        this.DataContext = new Foo();
    }
}

Avec moi? J'ai un ComboBox dont les éléments sont liés à une liste d'instances, dont l'une est nulle. J'ai attaché la fenêtre à une instance de Foo, et le ComboBox affiche la valeur de sa propriété Bar.

quand je lance cette application, le ComboBox commence avec un affichage vide car Foo.La barre est nulle par défaut. C'est très bien. Si j'utilise la souris pour déposer le ComboBox vers le bas et sélectionner l'élément "Hello", cela fonctionne aussi. Mais si j'essaie de re-sélectionner l'article vide en haut de la liste, le ComboBox se ferme et retourne à sa valeur précédente de "Hello"!

sélectionner la valeur nulle avec la flèche les touches fonctionnent comme prévu, et le paramétrage programmatique fonctionne aussi. C'est seulement sélectionner avec une souris qui ne fonctionne pas.

je sais qu'une solution facile est d'avoir une instance de barre qui représente null et de l'exécuter à travers un IValueConverter, mais quelqu'un peut-il expliquer pourquoi la sélection de null avec la souris ne fonctionne pas dans le ComboBox de WPF?

38
demandé sur Matt Hamilton 2009-02-06 03:03:44

10 réponses

null "élément" n'est pas sélectionné par le clavier du tout - plutôt l'élément précédent est désactivée et aucun autre élément (pu être sélectionné. C'est pourquoi, après avoir "sélectionné" l'item nul avec le clavier, vous ne pourrez plus Sélectionner l'item précédemment sélectionné ("Hello")-sauf via la souris!

en bref, vous ne pouvez ni sélectionner ni désélectionner un élément nul dans un ComboBox. Quand vous pensez que vous le faites, vous êtes plutôt désélectionner ou sélectionner l'article précédent ou un nouvel article.

la meilleure façon de le voir est peut-être d'ajouter un arrière-plan aux éléments du ComboBox. Vous remarquerez le fond coloré dans le ComboBox lorsque vous sélectionnez "Hello", mais lorsque vous le désélectionnez via le clavier, la couleur de fond disparaît. Nous savons que ce n'est pas l'item null, parce que l'item null a en fait la couleur de fond lorsque nous déposons la liste via la souris!

le XAML suivant, modifié de cela dans la question originale, mettra un arrière-plan LightBlue derrière les éléments afin que vous puissiez voir ce comportement.

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <Grid Background="LightBlue" Width="200" Height="20">
                        <TextBlock Text="{Binding Name}" />
                    </Grid>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </StackPanel>
</Window>

si vous voulez plus de validation, vous pouvez gérer l'événement SelectionChanged sur le ComboBox et voir que "sélectionner l'item null" donne en fait un tableau vide D'AddedItems dans sa SelectionChangedEventArgs, et "désélectionner l'item null en sélectionnant 'Hello' avec la souris" donne un tableau vide de RemovedItems.

16
répondu Tim Erickson 2009-12-14 23:12:38

eh Bien j'ai récemment rencontré le même problème avec null valeur ComboBox. Je l'ai résolu en utilisant deux convertisseurs:

  1. ItemsSource propriété: il remplace null valeurs dans la collection par n'importe quelle valeur passée dans le paramètre du convertisseur:

    class EnumerableNullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var collection = (IEnumerable)value;
    
            return
                collection
                .Cast<object>()
                .Select(x => x ?? parameter)
                .ToArray();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
  2. SelectedValue propriété: celle-ci fait la même chose mais pour la seule valeur et en deux façons:

    class NullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value ?? parameter;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value.Equals(parameter) ? null : value;
        }
    }
    

Exemple d'utilisation:

<ComboBox 
    ItemsSource="{Binding MyValues, Converter={StaticResource EnumerableNullReplaceConverter}, ConverterParameter='(Empty)'}" 
    SelectedValue="{Binding SelectedMyValue, Converter={StaticResource NullReplaceConverter}, ConverterParameter='(Empty)'}"
    />

Résultat:

enter image description here

Note: Si vous liez à collecte observable alors vous perdrez les notifications de changement. Aussi, vous ne voulez pas avoir plus d'un null valeur de la collection.

10
répondu Andrew Mikhailov 2015-04-29 07:52:54

j'ai une nouvelle solution pour cette question. "USING Mahapps"

  xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"


  <ComboBox x:Name="bars"  **controls:TextBoxHelper.ClearTextButton="True"**
              DisplayMemberPath="Name" 
              Height="21" 
              SelectedItem="{Binding Bar}"/>

enter image description here

enter image description here

Vous pouvez utiliser le bouton fermer pour effacer le contenu.

Merci.

6
répondu Anurag 2017-04-18 07:39:02

je sais que cette réponse n'est pas ce que vous avez demandé( une explication de pourquoi elle ne fonctionne pas avec la souris), mais je pense que la prémisse est défectueuse:

de mon point de vue en tant que programmeur et Utilisateur (pas .NET), choisir une valeur nulle est une mauvaise chose. "null" est censé être l'absence de valeur, pas quelque chose que vous sélectionnez.

si vous avez besoin de la capacité explicitement de ne pas choisir quelque chose, je suggérerais soit le travail que vous avez mentionné ( " - " , "N. A. " ou "Aucun" comme valeur), ou mieux

  • envelopper le combobox avec une case à cocher qui peut être désactivée pour désactiver le combobox. Cela me frappe comme la conception la plus propre à la fois du point de vue de l'utilisateur et programmatically.
4
répondu Galghamon 2009-02-17 08:08:03

http://remyblok.tweakblogs.net/blog/7237/wpf-combo-box-with-empty-item-using-net-4-dynamic-objects.html

public class ComboBoxEmptyItemConverter : IValueConverter 
{ 
/// <summary> 
/// this object is the empty item in the combobox. A dynamic object that 
/// returns null for all property request. 
/// </summary> 
private class EmptyItem : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
        // just set the result to null and return true 
        result = null; 
        return true; 
    } 
} 

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    // assume that the value at least inherits from IEnumerable 
    // otherwise we cannot use it. 
    IEnumerable container = value as IEnumerable; 

    if (container != null) 
    { 
        // everything inherits from object, so we can safely create a generic IEnumerable 
        IEnumerable<object> genericContainer = container.OfType<object>(); 
        // create an array with a single EmptyItem object that serves to show en empty line 
        IEnumerable<object> emptyItem = new object[] { new EmptyItem() }; 
        // use Linq to concatenate the two enumerable 
        return emptyItem.Concat(genericContainer); 
    } 

    return value; 
} 

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    throw new NotImplementedException(); 
} 

}

 <ComboBox ItemsSource="{Binding  TestObjectCollection, Converter={StaticResource ComboBoxEmptyItemConverter}}" 
      SelectedValue="{Binding SelectedID}" 
      SelectedValuePath="ID" 
      DisplayMemberPath="Name" />
2
répondu John 2013-07-27 22:52:29

ce ne serait pas l'adresse de votre réponse, mais j'espère que sa un coup dans le bon sens:

  1. avez-vous installé SP1?

à Partir de Scott Gu Blog:

  • NET 3.5 SP1 inclut plusieurs de liaison de données et l'édition des améliorations à

    WPF. Elles comprennent:
  • StringFormat de soutien au sein de {{ Binding }} les expressions pour faciliter le formatage des valeurs liées
  • nouvelles lignes alternantes support dans les contrôles dérivés de ItemsControl, qui fait il est plus facile de régler les propriétés alternantes sur les lignes (par exemple: alterner les couleurs de fond)
  • meilleure prise en charge de la manipulation et de la conversion des valeurs nulles au niveau des éléments de contrôle modifiables validation qui applique les règles de validation à un élément lié entier
  • MultiSelector de soutien pour poignée multi-sélection et vrac scénarios d'édition
  • IEditableCollectionView de soutien à l'interface de contrôles de données aux sources de données et activer la modification/ajout/suppression d'éléments de façon transactionnelle
  • amélioration des performances lors de la liaison à des données innombrables sources

Désolé si j'ai perdu votre temps et ce n'était même pas proche..mais je pense que le problème est hérité de:

les contraintes de l'ensemble de données fortement typé

NullValueDataSet Expliqué ici

mais maintenant la SP1 pour .Net 3.5 aurait dû traiter cette question..

1
répondu Ric Tokyo 2009-02-06 00:49:06
0
répondu rudigrobler 2009-02-06 13:32:13

j'ai eu le même genre de problème nous avons fait quelques travaux autour comme ajouter une propriété de valeur à l'élément de collection comme ceci :

 public class Bar

   {
      public string Name { get; set; }
      public Bar Value
      {
         get { return String.IsNullOrEmpty(Name) ?  null :  this; } // you can define here your criteria for being null
      }
   }

puis en ajoutant des éléments au lieu de null j'utilise le même objet:

  comboBox1.ItemsSource=  new ObservableCollection<Bar> 
        {
            new Bar(),
            new Bar { Name = "Hello" }, 
            new Bar { Name = "World" } 
        };

Et au lieu de selecteditem je la lie selectedvalue :

<ComboBox Height="23" Margin="25,40,133,0" DisplayMemberPath="Name"
              SelectedValuePath="Value" 
              SelectedValue="{Binding Bar}"
              Name="comboBox1" VerticalAlignment="Top" />

je sais que Ce n'est pas une solution complète, juste une solution de contournement j'utilise

0
répondu Dincer Uyav 2009-02-09 09:01:14

ComboBox a besoin d'un DataTemplate pour afficher l'article, peu importe sa simplicité. DataTemplate fonctionne comme ceci: obtenez une valeur à partir de l'instance.[chemin], par exemple,

bar1.Car.Color

Donc il ne peut pas obtenir une valeur de

null.Car.Color

il lancera une exception de référence nulle. Ainsi, l'instance nulle ne sera pas affichée. Mais la couleur - si c'est un type de référence - est autorisée à être nulle parce qu'il n'y aura pas d'exception dans ce cas.

0
répondu redjackwong 2009-02-17 07:41:47

juste une supposition, mais je pense que cela semble raisonnable.

supposons que combobox utilise "ListCollectionView" (lcv comme son instance) comme sa collection d'Articles, ce qui devrait être le cas. Si vous êtes un programmeur, ce que tu vas faire?

je répondrai au clavier et à la souris.

une fois que j'obtiens L'entrée de clavier, j'utilise

lcv.MoveCurrentToNext();

ou

lcv.MoveCurrentToPrevious();

donc, bien sûr que le clavier fonctionne bien.

alors je travaille sur les entrées de la souris respons. Et il vient le problème.

  1. je veux écouter "MouseClick" événement de mon article. Mais probablement, mon article n'est pas généré, c'est juste un emplacement. Donc quand l'utilisateur clique sur ce paramètre, Je n'obtiens rien.

  2. si j'obtiens l'événement avec succès, ce qui est prochain. Je vais appeler

    utilitaires.MoveCurrentTo (selectedItem);

le" selectedItem " qui serait null n'est pas un paramètre acceptable ici I penser.

de toute façon, c'est juste deviner. Je n'ai pas le temps de déboguer même si j'en suis capable. J'ai un tas de défauts à corriger. Bonne Chance. :)

0
répondu redjackwong 2009-02-17 09:01:44