Почему я не могу выбрать нулевое значение в 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?
Ответы
Ответ 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
это может не полностью ответить на ваш ответ, но, надеюсь, его удар в правильном направлении:
Из журнала 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" />
Ответ 7
Попробуйте Binding.FallbackValue
От 6 вещей, которые вы не знали о привязке данных в WPF
Ответ 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, не является приемлемым параметром, я думаю.
Во всяком случае, это просто догадывается. У меня нет времени отлаживать его, хотя я могу. У меня есть куча дефектов, которые нужно исправить. Удачи.:)