Подтвердить что ты не робот

Почему я не могу выбрать нулевое значение в ComboBox?

В WPF кажется невозможным выбрать (с помощью мыши) значение "null" из ComboBox. Изменить. Чтобы уточнить, это .NET 3.5 SP1.

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

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, один из которых имеет значение null. Я привязал окно к экземпляру Foo, и ComboBox отображает значение его свойства Bar.

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

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

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

4b9b3361

Ответ 1

Нулевой элемент не выбирается клавиатурой вообще - скорее, предыдущий элемент не выбран и не будет выбран следующий элемент. Вот почему после "выбрав" нулевой элемент с клавиатурой ", вы не сможете повторно выбрать ранее выбранный элемент (" 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 и увидеть, что "выбор нулевого элемента" фактически дает пустой массив добавленных элементов в его SelectionChangedEventArgs и "отменяет выбор нулевого элемента, выбирая" Hello "с мышь" дает пустой массив RemovedItems.

Ответ 2

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

  • Для свойства ItemsSource: он заменяет нулевые значения в коллекции любым значением, переданным внутри параметра преобразователя:

    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();
        }
    }
    
  • Для свойства 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 в коллекции.

Ответ 3

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

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

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

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

Ответ 4

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

введите описание изображения здесь

введите описание изображения здесь

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

Спасибо.

Ответ 5

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

  • Установили ли вы SP1?

Из журнала Scott Gu:

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

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

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

Описание NullValueDataSet

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

Ответ 6

Я потратил один день, чтобы найти решение этой проблемы, выбрав нулевое значение в combobox и, наконец, наконец, я нашел решение в статье, написанной на этом 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" />

Ответ 8

У меня была такая же проблема, с которой мы работали, например, добавив свойство 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" } 
        };

И вместо selectitem я привязываю его к selectedvalue:

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

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

Ответ 9

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

bar1.Car.Color

Поэтому он не может получить значение из

null.Car.Color

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

Ответ 10

Просто догадайтесь, но я думаю, что это звучит разумно.

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

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

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

lcv.MoveCurrentToNext();

или

lcv.MoveCurrentToPrevious();

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

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

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

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

    lcv.MoveCurrentTo(SelectedItem);

"selectedItem", который будет null, не является приемлемым параметром, я думаю.

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