43 votes

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

Dans WPF, il semble impossible de sélectionner (avec la souris) une valeur "nulle" dans une ComboBox. Modifier Pour clarifier, il s'agit de .NET 3.5 SP1.

Voici un peu de code pour montrer ce que je veux dire. D'abord, les déclarations en C# :

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

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

Ensuite, mon XAML de Window1 :

<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 une ComboBox dont les éléments sont liés à une liste d'instances de Bar, dont l'une est nulle. J'ai lié la fenêtre à une instance de Foo, et la ComboBox affiche la valeur de sa propriété Bar.

Lorsque je lance cette application, la ComboBox démarre avec un affichage vide car Foo.Bar est nul par défaut. Cela ne pose aucun problème. Si j'utilise la souris pour faire descendre la ComboBox et sélectionner l'élément "Hello", cela fonctionne également. Mais si j'essaie de resélectionner l'élément vide en haut de la liste, la ComboBox se ferme et revient à sa valeur précédente, à savoir "Hello" !

La sélection de la valeur nulle à l'aide des touches fléchées fonctionne comme prévu, et son paramétrage par programme fonctionne également. C'est seulement la sélection avec la souris qui ne fonctionne pas.

Je sais qu'une solution de contournement facile consiste à avoir une instance de Bar qui représente null et à la faire passer par un IValueConverter, mais quelqu'un peut-il expliquer pourquoi la sélection de null avec la souris ne fonctionne pas dans la ComboBox de WPF ?

15voto

Tim Erickson Points 1387

L'"élément" nul n'est pas du tout sélectionné par le clavier - l'élément précédent est plutôt désélectionné et aucun élément suivant n'est (capable d'être) sélectionné. C'est pourquoi, après avoir "sélectionné" l'élément nul avec le clavier, vous ne pouvez plus sélectionner l'élément précédemment sélectionné ("Hello") - sauf avec la souris !

En bref, vous ne pouvez ni sélectionner ni désélectionner un élément nul dans une ComboBox. Lorsque vous pensez le faire, vous désélectionnez ou sélectionnez plutôt l'élément précédent ou un nouvel élément.

La meilleure façon de s'en rendre compte est peut-être d'ajouter un arrière-plan aux éléments de la ComboBox. Vous remarquerez l'arrière-plan coloré dans la ComboBox lorsque vous sélectionnez "Hello", mais lorsque vous le désélectionnez au moyen du clavier, la couleur d'arrière-plan disparaît. Nous savons qu'il ne s'agit pas de l'élément nul, car l'élément nul a en fait la couleur de fond lorsque nous abaissons la liste avec la souris !

Le XAML suivant, modifié par rapport à celui de la question initiale, mettra un arrière-plan bleu clair 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 une validation supplémentaire, vous pouvez gérer l'événement SelectionChanged de la ComboBox et voir que "la sélection de l'élément nul" donne en fait un tableau vide de AddedItems dans ses SelectionChangedEventArgs, et que "la désélection de l'élément nul en sélectionnant 'Hello' avec la souris" donne un tableau vide de RemovedItems.

5voto

Galghamon Points 1421

Je sais que cette réponse n'est pas ce que vous avez demandé (une explication de la raison pour laquelle cela ne fonctionne pas avec la souris), mais je pense que la prémisse est erronée :

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

Si vous avez besoin de la capacité de ne pas sélectionner explicitement quelque chose, je suggérerais soit la solution de contournement que vous avez mentionnée ("-", "n.a." ou "none" comme valeur), soit mieux encore

  • Enveloppez la combobox avec une case à cocher qui peut être décochée pour désactiver la combobox. Cela me semble être la conception la plus propre, tant du point de vue de l'utilisateur que du point de vue programmatique.

3voto

Gui Points 748

J'ai passé une journée à trouver une solution à ce problème de sélection d'une valeur nulle dans une combobox et finalement, oui finalement, j'ai trouvé une solution dans un article écrit à cette url :

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" />

1voto

Ric Tokyo Points 5465

Cela ne répond peut-être pas complètement à votre question, mais j'espère que c'est un pas dans la bonne direction :

  1. Avez-vous installé SP1 ?

Du blog de Scott Gu :

  • NET 3.5 SP1 comprend plusieurs améliorations de la liaison et de l'édition de données pour
    WPF. Il s'agit notamment de :
  • Prise en charge de StringFormat dans les expressions {{ Binding }} afin de faciliter la mise en œuvre de l'action. formatage des valeurs liées
  • Nouveau support de rangs alternés dans les limites de c d'ItemsControl, ce qui rend de définir plus facilement des propriétés alternées sur les rangées (par exemple, des couleurs de fond alternées).
  • Meilleure gestion et conversion des valeurs nulles dans les contrôles modifiables au niveau des éléments qui applique des règles de validation à l'ensemble d'un élément lié.
  • Prise en charge de MultiSelector pour gérer le multi-sele d'édition groupée
  • IEditableCollectionView aux sources de données et permettre l'édition, l'ajout et la suppression d'éléments de manière transactionnelle.
  • Amélioration des performances lors de la liaison sources

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

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

NullValueDataSet Expliqué ici

Mais maintenant le SP1 pour .Net 3.5 devrait avoir réglé ce problème .

0voto

rudigrobler Points 8883

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X