Почему я не могу выбрать нулевое значение в ComboBox? - PullRequest
41 голосов
/ 06 февраля 2009

В WPF кажется невозможным выбрать (с помощью мыши) «нулевое» значение в ComboBox. Редактировать Для пояснения, это .NET 3.5 SP1.

Вот код, чтобы показать, что я имею в виду. Во-первых, объявления C #:

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

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

Далее мой 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>

И, наконец, мой класс 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();
    }
}

Со мной? У меня есть ComboBox, элементы которого связаны со списком экземпляров Bar, один из которых является нулевым. Я привязал окно к экземпляру Foo, и ComboBox отображает значение его свойства Bar.

Когда я запускаю это приложение, ComboBox запускается с пустым дисплеем, потому что Foo.Bar по умолчанию равен нулю. Все в порядке. Если я использую мышь, чтобы опустить ComboBox и выбрать элемент «Hello», это тоже работает. Но затем, если я попытаюсь повторно выбрать пустой элемент в верхней части списка, ComboBox закроется и вернется к своему предыдущему значению «Hello»!

Выбор нулевого значения с помощью клавиш со стрелками работает, как ожидалось, и установка его программно также работает. Это только с помощью мыши, которая не работает.

Я знаю, что простой обходной путь - это создать экземпляр Bar, который представляет значение null, и запустить его через IValueConverter, но кто-то может объяснить, почему выбор нуля с помощью мыши не работает в ComboBox WPF?

Ответы [ 10 ]

15 голосов
/ 15 декабря 2009

Нулевой «элемент» вообще не выбирается клавиатурой - скорее, предыдущий элемент не выбирается и не выбирается ни один последующий элемент. Именно поэтому после «выбора» «нулевой элемент с помощью клавиатуры, после этого вы не сможете повторно выбрать ранее выбранный элемент (« Hello ») - кроме как с помощью мыши!

Короче говоря, вы не можете ни выбрать, ни отменить выбор нулевого элемента в ComboBox. Когда вы думаете, что делаете это, вы скорее отменяете выбор или выбираете предыдущий или новый элемент.

Возможно, это лучше всего увидеть, добавив фон к элементам в ComboBox. Вы заметите цветной фон в ComboBox, когда выберете «Hello», но когда вы отмените выбор с помощью клавиатуры, цвет фона исчезнет. Мы знаем, что это не нулевой элемент, потому что нулевой элемент на самом деле имеет цвет фона, когда мы перетаскиваем список мышкой!

Следующий XAML, модифицированный по сравнению с исходным вопросом, поместит фон LightBlue позади элементов, чтобы вы могли видеть это поведение.

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

Если вам нужна дополнительная проверка, вы можете обработать событие SelectionChanged в ComboBox и увидеть, что «выбор нулевого элемента» фактически дает пустой массив AddedItems в его SelectionChangedEventArgs, и «отменить выбор нулевого элемента, выбрав« Hello »с помощью мышь "выдает пустой массив RemovedItems.

11 голосов
/ 28 апреля 2015

Ну, я недавно столкнулся с той же проблемой со значением null для ComboBox . Я решил это с помощью двух преобразователей:

  1. Для Свойство ItemsSource : оно заменяет значения null в коллекции на любое значение, переданное в параметре конвертера:

    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 свойство: это делает то же самое, но для одного значения и двумя способами:

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

Пример использования:

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

Результат:

enter image description here

Примечание: Если вы свяжетесь с ObservableCollection , вы потеряете уведомления об изменениях. Также вы не хотите иметь более одного значения null в коллекции.

8 голосов
/ 18 апреля 2017

Я получил новое решение для этого вопроса. "ИСПОЛЬЗОВАНИЕ 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

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

Спасибо.

5 голосов
/ 17 февраля 2009

Я знаю, что этот ответ не тот, о котором вы просили (объяснение, почему он не работает с мышью), но я думаю, что предпосылка ошибочна:

С моей точки зрения, как программиста и пользователя (не .NET), выбор нулевого значения - плохая вещь. «null» - это отсутствие значения, а не то, что вы выбираете.

Если вам нужна возможность явно не выбирать что-то, я бы предложил либо упомянутый вами обходной путь ("-", "n.a." или "none" в качестве значения), либо лучше

  • оберните комбинированный список флажком, который можно снять, чтобы отключить комбинированный список. Это кажется мне самым чистым дизайном как с точки зрения пользователя, так и программно.
2 голосов
/ 28 июля 2013

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

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" />
1 голос
/ 06 февраля 2009

это может не полностью ответить на ваш ответ, но, надеюсь, это хит в правильном направлении:

  1. Вы установили SP1?

Из блога Скотта Гу:

  • NET 3.5 с пакетом обновления 1 (SP1) включает несколько улучшений связывания и редактирования данных для
    WPF. К ним относятся:
  • Поддержка StringFormat в выражениях {{Binding}} для легкого включения форматирование связанных значений
  • Поддержка новых чередующихся строк в производных элементах управления из ItemsControl, который делает проще устанавливать чередующиеся свойства в строках (например, чередующиеся цвета фона)
  • Лучшая поддержка обработки и преобразования для нулевых значений в редактируемых элементах управления проверка, которая применяет правила проверки ко всему связанному элементу
  • Поддержка MultiSelector для обработки множественного выбора и массовых редактирование сценариев
  • IEditableCollectionView поддержка интерфейса управления данными к источникам данных и позволяют редактировать / добавлять / удалять элементы транзакционным способом
  • Улучшения производительности при привязке к IEnumerable данным источники

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

ограничения строго типизированного набора данных

NullValueDataSet Объяснено здесь

Но теперь SP1 для .Net 3.5 должен был решить эту проблему.

0 голосов
/ 17 февраля 2009

Просто предположение, но я думаю, что это звучит разумно.

Предположим, что combobox использует "ListCollectionView" (в качестве экземпляра lcv) в качестве коллекции элементов, что и должно быть. Если ты программист, что ты собираешься делать?

Я отвечу как на клавиатуру, так и на мышь.

Как только я получаю ввод с клавиатуры, я использую

lcv.MoveCurrentToNext();

или

lcv.MoveCurrentToPrevious();

Итак, клавиатура работает хорошо.

Тогда я работаю над ответными входами мыши. И это проблема.

  1. Я хочу прослушать событие MouseClick моего предмета. Но, вероятно, мой Предмет не сгенерирован, это просто заполнитель. Поэтому, когда пользователь нажимает на этот заполнитель, я ничего не получаю.

  2. Если я получу событие успешно, что дальше. Я призову

    lcv.MoveCurrentTo (SelectedItem);

«selectedItem», который был бы нулевым, здесь не является приемлемым параметром.

Во всяком случае, это только догадки. У меня нет времени на отладку, хотя я могу. У меня есть куча дефектов, которые нужно исправить. Удачи. :)

0 голосов
/ 17 февраля 2009

ComboBox нужен DataTemplate для отображения элемента, независимо от того, насколько он прост. DataTemplate работает так: получить значение из экземпляра. [Путь], например,

bar1.Car.Color

Таким образом, он не может получить значение из

null.Car.Color

Будет выдано исключение нулевой ссылки. Таким образом, нулевой экземпляр не будет отображаться. Но цвет - если это ссылочный тип - может быть пустым, потому что в этом случае не будет исключений.

0 голосов
/ 09 февраля 2009

У меня была такая же проблема, как мы работали, например, добавление свойства value к элементу коллекции:

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

Затем при добавлении элементов вместо нуля я использую тот же объект:

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

И вместо выбранного элемента я связываю его с выбранным значением:

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

Я знаю, что это не полное решение, я использую только один обходной путь

0 голосов
/ 06 февраля 2009
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...