Хороший день!
Я хочу свой WPF ComboBox
отобразить некоторый альтернативный текст, когда его выбор с привязкой к данным null
.
Модель представления имеет ожидаемые свойства:
public ThingoSelectionViewModel : INotifyPropertyChanged {
public ThingoSelectionViewModel(IProvideThingos) {
this.Thingos = IProvideThingos.GetThingos();
}
public ObservableCollection<Thingo> Thingos { get; set; }
public Thingo SelectedThingo {
get { return this.selectedThingo; }
set { // set this.selectedThingo and raise the property change notification
}
// ...
}
Представление имеет XAML, связывающий с моделью представления ожидаемым способом:
<ComboBox x:Name="ComboboxDrive" SelectedItem="{Binding Path=SelectedThingo}"
IsEditable="false" HorizontalAlignment="Left" MinWidth="100"
IsReadOnly="false" Style="{StaticResource ComboboxStyle}"
Grid.Column="1" Grid.Row="1" Margin="5" SelectedIndex="0">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="False">Select a thingo</ComboBoxItem>
<CollectionContainer
Collection="{Binding Source={StaticResource Thingos}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
ComboBoxItem
втиснутый в вершину способ получить дополнительный объект наверху. Это - чистый хром: модель представления остается чистой и простой. Существует всего одна проблема: пользователи хотят "Выбор thingo", отображенный каждый раз, когда ComboBox' выбор является пустым.
Пользователи не хотят thingo, выбранный по умолчанию. Они хотят видеть, что сообщение говорит им выбирать thingo.
Я хотел бы избежать необходимости загрязнять viewmodel a ThingoWrapper
класс с a ToString
метод возвращая "Выбор thingo", если .ActualThingo
свойство является пустым, перенося каждого Thingo
поскольку я заполняю Thingos
, и выяснение некоторого способа препятствовать тому, чтобы пользователь выбрал обнуленный Thingo
.
Есть ли способ отобразить "Выбор thingo" в ComboBox
'границы с помощью чистого XAML, или чистого XAML и нескольких строк кода в классе с фоновым кодом представления?
Путь наименьшего сопротивления здесь, который я нашел, это использование Null Object Pattern Для примера использования этого паттерна в .NET Framework рассмотрим статическое значение Double.NaN Если вы создадите Null Object для вашего Thingo, в вашей модели представления вы можете добавить его к передней части списка, чтобы обозначить "ничего не выбрано". Создайте DataTemplate для класса Thingo, который имеет DataTrigger для экземпляра Null Object, который показывает "Select a Value".
Я мог бы привести пример кода, но мне уже пора спать.
Изменить: Похоже, идея триггера не подходит.Я добавил следующее в шаблон элемента управления тестового поля со списком, но безрезультатно:
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter Property="Text" Value="No Item Selected"/>
</Trigger>
Кроме того, при попытке отредактировать шаблон элемента управления в Blend (Edit Current) у меня остается невыразительное поле со списком, без цветов, просто уродливая кнопка (но есть раскрывающийся список без полей). Попробуйте другое предложение (возможно, Майк Браун).
Оригинал:
В шаблоне управления можно использовать триггер. Вот пример использования ListBox из приложения, над которым я работаю.
<ControlTemplate x:Key="SnazzyFormListBoxTemplate" TargetType="{x:Type ListBox}">
<Microsoft_Windows_Themes:ClassicBorderDecorator x:Name="Bd" SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderStyle="Sunken" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Padding="{TemplateBinding Padding}" Focusable="False" Template="{DynamicResource SnazzyScrollViewerControlTemplate}">
<Grid>
<TextBlock x:Name="textBlock" Text="No Items" FontFamily="Arial" FontWeight="Bold" FontSize="13.333" Foreground="#4D000000" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,10"/>
<ItemsPresenter x:Name="itemsPresenter" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Microsoft_Windows_Themes:ClassicBorderDecorator>
<ControlTemplate.Triggers>
<Trigger Property="Selector.IsSelected" Value="True"/>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" TargetName="textBlock" Value="Visible"/>
<Setter Property="Visibility" TargetName="itemsPresenter" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="True">
<Setter Property="Visibility" TargetName="textBlock" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="itemsPresenter" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Вышеупомянутый ControlTemplate имеет триггер, который проверяет свойства HasItems. Если False, текстовый блок с надписью «Нет элементов» отображается в середине ListBox. Если есть Предметы, они отображаются.
В вашем случае измените триггер, чтобы проверить, является ли ItemSelected равным x: Null, и установите для свойства Text значение «Nothing Selected».
Вы не можете использовать триггер шаблона элемента управления, но вы можете настроить простой шаблон элемента для поля со списком:
<ComboBox ItemsSource="{Binding}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="displayText" Text="{Binding}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="displayText" Property="Text" Value="Default Value" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Насколько строги ваши требования к MVVM? Можете ли вы иметь немного code-behind в представлении?
Возможно, вы могли бы разместить ComboBox в гриде, примерно так:
<Grid>
<ComboBox x:Name="ComboBoxControl"
SelectionChanged="ComboBoxControl_SelectionChanged"
HorizontalAlignment="Left" VerticalAlignment="Top"
MinWidth="{Binding ElementName=UnselectedText, Path=ActualWidth}">
<ComboBoxItem>One</ComboBoxItem>
<ComboBoxItem>Two</ComboBoxItem>
<ComboBoxItem>Three</ComboBoxItem>
</ComboBox>
<TextBlock IsHitTestVisible="False"
x:Name="UnselectedText"
HorizontalAlignment="Left"
Text="Select an option..."
VerticalAlignment="Top" Margin="4"
Padding="0,0,30,0" />
</Grid>
Затем в code-behind вставьте некоторую логику в обработчик событий:
Private Sub ComboBoxControl_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
If ComboBoxControl.SelectedIndex = -1 Then
UnselectedText.Visibility = Windows.Visibility.Visible
Else
UnselectedText.Visibility = Windows.Visibility.Hidden
End If
End Sub
Установка IsHitTestVisible="False"
DependencyProperty для TextBlock пропускает события мыши, чтобы вы могли щелкнуть на ComboBox, а установка видимости на Hidden
в code-behind удерживает макет внешнего вида ComboBox по умолчанию от прыжков, когда текст подсказки скрыт.