Может ли WPF ComboBox отображать альтернативный текст, если его выбор нулевой? - PullRequest
9 голосов
/ 25 мая 2010

G'day!

Я хочу, чтобы мой 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 вклинивается в верхнюю часть - это способ получить дополнительный предмет в верхней части. Это чистый хром: модель представления остается чистой и простой. Есть только одна проблема: пользователи хотят, чтобы «Выберите вещь» отображалось всякий раз, когда выбор ComboBox равен нулю.

Пользователи не хотят, чтобы вещь была выбрана по умолчанию. Они хотят видеть сообщение, говорящее им, чтобы выбрать вещь.

Я бы хотел избежать загрязнения модели представления классом ThingoWrapper с помощью метода ToString, возвращающего «Выберите вещь», если его свойство .ActualThingo имеет значение null, оборачивая каждый Thingo при заполнении Thingos и выяснить, каким образом пользователь не может выбрать nulled Thingo.

Есть ли способ отобразить «Выберите вещь» в границах ComboBox ', используя чистый XAML или чистый XAML и несколько строк кода в классе code-behind представления?

Ответы [ 7 ]

8 голосов
/ 29 июня 2010

Насколько строгое ваше требование к MVVM? Можете ли вы иметь немного кода в представлении?

Возможно, вы можете поместить 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>

Затем в коде вставьте некоторую логику в обработчик событий:

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 в выделенном фрагменте кода предотвращает перепрыгивание макета внешнего вида ComboBox по умолчанию при текст подсказки скрыт.

4 голосов
/ 25 мая 2010

Вы не можете использовать триггер шаблона элемента управления, но вы можете настроить простой шаблон элемента для комбинированного списка:

<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>
3 голосов
/ 25 мая 2010

Путь наименьшего сопротивления, который я здесь обнаружил, заключается в использовании шаблона нулевого объекта В качестве примера использования этого шаблона в .NET Framework рассмотрите статическое значение Double.NaN, если вы создаете Нулевой объект для вашего Thingo, в вашей модели представления вы можете добавить его в начало списка, чтобы обозначить «ничего не выбрано». Создайте DataTemplate для класса Thingo, в котором есть DataTrigger для экземпляра Null Object, в котором отображается «Выберите значение».

Я мог бы дать пример кода, но он уже не спит.

3 голосов
/ 25 мая 2010

Изменить: Похоже, идея триггера не нужна. Я добавил следующее к контрольному шаблону тестового поля со списком безрезультатно:

    <Trigger Property="SelectedItem" Value="{x:Null}">
        <Setter Property="Text" Value="No Item Selected"/>
    </Trigger>

Кроме того, при попытке отредактировать шаблон элемента управления в Blend (Edit Current) у меня остался безобъектный комбинированный список, без цветов, просто уродливая кнопка (но есть выпадающий список без полей). Попробуйте предложить кому-нибудь еще (возможно, Майк Браун).

Оригинал:

Вы можете использовать Trigger в шаблоне Control. Вот пример использования 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, текстовое поле с надписью «Нет элементов» отображается в середине списка. Если есть Предметы, они отображаются.

В вашем случае измените триггер, чтобы проверить, является ли ItemSelected x: Null, и установите для свойства Text значение «Ничего не выбрано».

1 голос
/ 30 июня 2011

Я знаю, что это старая ветка, но вот как я это делаю. После получения коллекции Thingos я просто вставляю новый Thingo со значением фиктивного идентификатора и отображаемым значением «Выберите вещь».

    public ThingoSelectionViewModel(IProvideThingos) {
            this.Thingos = IProvideThingos.GetThingos();
            Thingo newThingo = new Thingo();
            newThingo.ThingoID = -1;
            newThingo.ThingoDisplayName = "Select a thingo";
            this.Thingos.Insert(0, newThingo);
        }

Теперь, когда ComboBox привязан к данным, первый пункт - «Выберите вещь». Затем, когда выбран Thingo, я проверяю идентификатор SelectedThingo и действую соответствующим образом.

0 голосов
/ 10 января 2018

Другой вариант:

<ComboBox>
  <ComboBoxItem Visibility="Collapsed" IsSelected="True">
    <TextBlock Text="Choose item" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 1" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 2" />
  </ComboBoxItem>
</ComboBox>
0 голосов
/ 24 января 2016

Я знаю, что я воскрешаю старый пост, но это было первое, что появилось в моем поиске в Google. В Visual Studio вы можете выбрать для параметра «Выбор по умолчанию» значение 0, а не «-1», и просто сделать первый выбор текстовым по умолчанию.

<ComboBox x:name="ThingoSelector" SelectedIndex="0">
    <ComboBoxItem IsEnabled="False">Choose Thingo</ComboBoxItem>
    <ComboBoxItem>Thingo 1</ComboBoxItem>
</ComboBox>
...