当 WPF ComboBox 的选择为空时,它可以显示替代文本吗?

发布于 2024-09-02 23:59:31 字数 1734 浏览 3 评论 0原文

日安!

我希望我的 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 是在顶部获取额外项目的一种方式。它是纯粹的镀铬:视图模型保持纯粹和简单。只有一个问题:只要组合框的选择为空,用户就希望显示“选择一项”。

用户希望默认选择某个事物。他们希望看到一条消息,告诉他们选择一个事物。

我希望避免使用 ThingoWrapper 类污染视图模型,该类的 ToString 方法返回“Select a thingo”(如果其 .ActualThingo)属性为 null,在填充 Thingos 时包装每个 Thingo,并找出某种方法来防止用户选择空的 Thingo

有没有办法使用纯 XAML 或纯 XAML 和视图代码隐藏类中的几行代码在 ComboBox 边界内显示“选择事物”?

G'day!

I want my WPF ComboBox to display some alternative text when its data-bound selection is null.

The view model has the expected properties:

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
    }

    // ...

}

The view has XAML binding to the view model in the expected way:

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

The ComboBoxItem wedged into the top is a way to get an extra item at the top. It's pure chrome: the view model stays pure and simple. There's just one problem: the users want "Select a thingo" displayed whenever the ComboBox' selection is null.

The users do not want a thingo selected by default. They want to see a message telling them to select a thingo.

I'd like to avoid having to pollute the viewmodel with a ThingoWrapper class with a ToString method returning "Select a thingo" if its .ActualThingo property is null, wrapping each Thingo as I populate Thingos, and figuring out some way to prevent the user from selecting the nulled Thingo.

Is there a way to display "Select a thingo" within the ComboBox' boundaries using pure XAML, or pure XAML and a few lines of code in the view's code-behind class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

恰似旧人归 2024-09-09 23:59:31

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

在 TextBlock 上设置 IsHitTestVisible="False" DependencyProperty 可以让鼠标事件通过,以便您可以单击 ComboBox,并且在隐藏代码中将可见性设置为 Hidden 可以防止默认 ComboBox 外观的布局在提示文本隐藏时跳来跳去。

How strict is your MVVM requirement? Can you have a little code-behind in the view?

Perhaps you could contain the ComboBox in a grid, something like this:

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

Then, in the code-behind, insert some logic in an event handler:

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

Setting the IsHitTestVisible="False" DependencyProperty on the TextBlock lets mouse events through so that you can click on the ComboBox, and setting the visibility to Hidden in the code-behind keeps the layout of a default ComboBox's appearance from jumping around when the prompt text is hidden.

只涨不跌 2024-09-09 23:59:31

您不能使用控件模板触发器,但可以为组合框设置一个简单的项目模板:

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

You can't use a control template trigger, but you could set up a simple item template for the combobox:

<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>
萌酱 2024-09-09 23:59:31

编辑:
看来触发这个想法是行不通的。我将以下内容添加到测试组合框的控制模板中,但无济于事:

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

此外,当尝试在 Blend(编辑当前)中编辑控制模板时,我留下了一个毫无功能的组合框,没有颜色,只有一个丑陋的按钮(但有是一个无边框下拉菜单)。尝试别人的建议(也许是迈克·布朗)。

原文:

您可以在控件模板中使用触发器。这是一个使用我正在开发的应用程序中的列表框的示例。

<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,则在列表框的中间显示一个文本块“No Items”。如果有项目,则会显示它们。

在您的情况下,更改触发器以检查 ItemSelected 是否为 x:Null 并将 Text 属性设置为“Nothing Selected”。

Edit:
Looks like the trigger idea is a no go. I added the following to the control template of a test combo box to no avail:

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

Additionally, when trying to edit the control template in Blend (Edit Current) I am left with a featureless combobox, no colors, just an ugly button (but there is a borderless dropdown). Try someone elses suggestion (Mike Brown perhaps).

Original:

You can use a Trigger in the Control template. Here is an example using a ListBox from an app I am working on.

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

The above ControlTemplate has a Trigger which checks the Property HasItems. If False, a textblock saying "No Items" is displayed in the middle of the ListBox. If there are Items, they are displayed.

In your case change the trigger to check to see if ItemSelected is x:Null and set the Text property to "Nothing Selected".

沧桑㈠ 2024-09-09 23:59:31

我发现这里阻力最小的路径是使用 空对象模式在 .NET Framework 中使用此模式的示例,如果您为 Thingo 创建 Null 对象,请考虑静态值 Double.NaN,在视图模型中,您可以将其附加到列表的前面以表示“未选择任何内容”。为 Thingo 类创建一个 DataTemplate,该类具有用于显示“选择值”的 Null 对象实例的 DataTrigger。

我可以提供一个代码示例,但已经过了我的睡觉时间。

The path of least resistance here that I've found is to use the Null Object Pattern For an example of using this pattern in the .NET Framework, consider the static value Double.NaN if you create a Null Object for your Thingo, in your view model you can append it to the front of your list to signify "nothing is selected". Create a DataTemplate for the Thingo class that has a DataTrigger for the Null Object instance that shows "Select a Value".

I could give a code sample but it's past my bed time.

薔薇婲 2024-09-09 23:59:31

我知道这是一个旧线程,但这是我的做法。获取 Thingos 集合后,我只需插入一个具有虚假 ID 值和显示值“选择事物”的新 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 的 ID,并对其采取相应的操作。

I know this is an old thread, but here is how I do it. After I fetch the Thingos collection, I simply insert a new Thingo with a bogus ID value and a display value of "Select a 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);
        }

Now, when the ComboBox is databound, the first item is "Select a thingo." Then when a Thingo is selected, I test the ID of the SelectedThingo, and act on it accordingly.

來不及說愛妳 2024-09-09 23:59:31

我知道我正在恢复旧帖子,但这是我的谷歌搜索中出现的第一个帖子。
在 Visual Studio 中,您可以选择将“默认选择”设置为 0,而不是 -1,并且将第一个选择设置为默认文本。

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

I know I'm resurrecting an old post, but this was the first one that came up on my google search.
In Visual Studio, you can choose to set the Default Selection to 0, instead of -1, and just have your first selection be the default text.

<ComboBox x:name="ThingoSelector" SelectedIndex="0">
    <ComboBoxItem IsEnabled="False">Choose Thingo</ComboBoxItem>
    <ComboBoxItem>Thingo 1</ComboBoxItem>
</ComboBox>
溺ぐ爱和你が 2024-09-09 23:59:31

另一种选择:

<ComboBox>
  <ComboBoxItem Visibility="Collapsed" IsSelected="True">
    <TextBlock Text="Choose item" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 1" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 2" />
  </ComboBoxItem>
</ComboBox>

Another option:

<ComboBox>
  <ComboBoxItem Visibility="Collapsed" IsSelected="True">
    <TextBlock Text="Choose item" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 1" />
  </ComboBoxItem>
  <ComboBoxItem>
    <TextBlock Text="Item 2" />
  </ComboBoxItem>
</ComboBox>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文