如何在WPF中的页面加载时,在组合框中显示默认文本“--选择团队--”?

127
在一个WPF应用程序中,在MVP应用程序中,我有一个下拉框,其中显示从数据库获取的数据。在添加项目到下拉框之前,我想显示默认文本,如:
 

"--选择团队--"

因此,在页面加载时它会显示,并在选择后清除文本并显示项目。
从数据库选择数据正在发生。我需要显示默认文本,直到用户从组合框中选择项目。请指导我。
24个回答

131

我发现最简单的方法是:

<ComboBox Name="MyComboBox"
 IsEditable="True"
 IsReadOnly="True"
 Text="-- Select Team --" />

显然,您需要添加其他选项,但这可能是最简单的方法。

但是,这种方法有一个缺点,即虽然组合框内的文本不可编辑,但仍然可以选择。然而,考虑到迄今为止我发现的每个替代方案的质量和复杂性都很差,这可能是目前最好的选择。


很棒的回答,Chris!我只想添加Focusable="True",但那只是外观上的改变。 - Slavisa
6
回答完美,克里斯。一个属性可以带来如此大的差异 :D - Aster Veigas
6
Focusable="False" IsEditable="True" IsReadOnly="True" - Kamil Lelonek
1
很遗憾,它不能处理混合项目(例如,如果组合框项目是图像)。 - greenoldman
22
简单有效的解决方案。WPF控件在整个框架中都存在这样的问题。时而,控件缺少大多数开发人员需要的功能。因此,开发人员浪费时间寻找解决方案,购买第三方替代控件或实施解决方法... WPF团队自己的开发甚至使用他们的控件吗? - Damn Vegetables
显示剩余2条评论

97

你可以使用一个 IValueConverter 来实现此功能,无需任何代码干预。

<Grid>
   <ComboBox
       x:Name="comboBox1"
       ItemsSource="{Binding MyItemSource}"  />
   <TextBlock
       Visibility="{Binding SelectedItem, ElementName=comboBox1, Converter={StaticResource NullToVisibilityConverter}}"
       IsHitTestVisible="False"
       Text="... Select Team ..." />
</Grid>

这里有一个转换器类,你可以重复使用。

public class NullToVisibilityConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == null ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

最后,你需要在资源部分声明你的转换器。

<Converters:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />

Converters是您放置转换器类的位置。一个示例是:

xmlns:Converters="clr-namespace:MyProject.Resources.Converters"
这种方法的好处是可以避免在代码后台重复编写代码。

我想使用这个,但似乎在组合框是数据网格的标题的情况下不允许使用... XAML会报错,指出标题已经定义(或者可能不能定义多次)。有什么想法吗?我考虑只使用一个空值选项,然后通过选择它来进行重置,但这似乎有点不太好。 - Paul Gibson
1
这是一个出色解决方案的重要原因是,几乎每个数据绑定的 WPF 项目都使用 NullToVisibilityConverter,因此大多数情况下已经存在 - 最好利用它! - tpartee
2
实际上,您可以使用“DataTrigger”来避免在此处使用转换器代码 :) - Billy ONeal

55

我喜欢Tri Q的回答,但那些值转换器很难使用。PaulB用事件处理程序完成了这个任务,但这也是不必要的。这里有一个纯XAML解决方案:

<ContentControl Content="{Binding YourChoices}">
    <ContentControl.ContentTemplate>
        <DataTemplate>
            <Grid>
                <ComboBox x:Name="cb" ItemsSource="{Binding}"/>
                <TextBlock x:Name="tb" Text="Select Something" IsHitTestVisible="False" Visibility="Hidden"/>
            </Grid>
            <DataTemplate.Triggers>
                <Trigger SourceName="cb" Property="SelectedItem" Value="{x:Null}">
                    <Setter TargetName="tb" Property="Visibility" Value="Visible"/>
                </Trigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ContentControl.ContentTemplate> 
</ContentControl>

41

没有人说一个纯XAML的解决方案必须很复杂。这是一个简单的解决方案,只有一个数据触发器在文本框上。边距和位置按需设置。

<Grid>
    <ComboBox x:Name="mybox" ItemsSource="{Binding}"/>
    <TextBlock Text="Select Something" IsHitTestVisible="False">
           <TextBlock.Style>
                <Style TargetType="TextBlock">
                      <Setter Property="Visibility" Value="Hidden"/>
                      <Style.Triggers>
                            <DataTrigger Binding="{Binding ElementName=mybox,Path=SelectedItem}" Value="{x:Null}">
                                  <Setter Property="Visibility" Value="Visible"/>
                             </DataTrigger>
                      </Style.Triggers>
                </Style>
           </TextBlock.Style>
     </TextBlock>
</Grid>

5
我需要将“Visibility =“ Hidden””移到数据触发器中。然后它按预期工作。这绝对是我见过的最直接的方法。为了可重用性,我将样式移入了资源中。 - Mitch
@Mitch IceForce的答案对我不起作用,你改了什么才让它工作? - Chris
1
@Chris 我认为他的意思是在触发器之外(在样式内部)添加<Setter Property="Visibility" Value="Hidden"/>,并从实际的文本块元素中删除Visibility="Hidden" - Reinstate Monica Please
@Mitch,如果您在DataTrigger中的ElementName指向特定对象(mybox),那么如何将Textblock样式移动到资源中以便重用?是否有一种通用的方式来指定该名称? - CrApHeR

29

ComboBox 元素的 IsEditable="True" 属性设置为True。这将显示 ComboBoxText 属性。


2
这是整个解决方案中最简单的解决方案。 - Sergey Koulikov
9
它确实会改变控件的外观。 - simonalexander2005

18
我不知道它是否直接支持,但是你可以用标签叠加在组合框上,如果选择为空,则将其设置为隐藏。
例如:
<Grid>
   <ComboBox Text="Test" Height="23" SelectionChanged="comboBox1_SelectionChanged" Name="comboBox1" VerticalAlignment="Top" ItemsSource="{Binding Source=ABCD}"  />
   <TextBlock IsHitTestVisible="False" Margin="10,5,0,0" Name="txtSelectTeam" Foreground="Gray" Text="Select Team ..."></TextBlock>
</Grid>

然后在选择更改处理程序中...

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    txtSelectTeam.Visibility = comboBox1.SelectedItem == null ? Visibility.Visible : Visibility.Hidden;
}

1
可以在XAML中设置TextBlock的可见性,而不是创建一个SelectionChanged处理程序。 - aliceraunsbaek

10

根据IceForge的回答,我准备了一个可重复使用的解决方案:

xaml样式:

<Style x:Key="ComboBoxSelectOverlay" TargetType="TextBlock">
    <Setter Property="Grid.ZIndex" Value="10"/>
    <Setter Property="Foreground" Value="{x:Static SystemColors.GrayTextBrush}"/>
    <Setter Property="Margin" Value="6,4,10,0"/>
    <Setter Property="IsHitTestVisible" Value="False"/>
    <Setter Property="Visibility" Value="Hidden"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding}" Value="{x:Null}">
            <Setter Property="Visibility" Value="Visible"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

使用示例:

<Grid>
     <ComboBox x:Name="cmb"
               ItemsSource="{Binding Teams}" 
               SelectedItem="{Binding SelectedTeam}"/>
     <TextBlock DataContext="{Binding ElementName=cmb,Path=SelectedItem}"
               Text=" -- Select Team --" 
               Style="{StaticResource ComboBoxSelectOverlay}"/>
</Grid>

不错。我通过使用相对源绑定TextBlock的DataContext来扩展它,以避免设置名称。请参见下一个注释中的Markup(SO评论中的代码看起来很丑)。 - Sascha
<TextBlock DataContext="{Binding Path=Children[0].SelectedItem, RelativeSource={RelativeSource AncestorType=Grid}}" Text=" -- Select Project --" Style="{StaticResource ComboBoxSelectOverlay}" /> - Sascha

4

我没有尝试过在组合框中使用它,但是我在其他控件中使用它时有效...

ageektrapped的博客文章

他在这里使用装饰层来显示水印。


刚刚下载并尝试了这段代码。它似乎按照广告所说的那样工作。它允许您使用一个简单的附加属性来装饰您的组合框,其中包含您的水印。它也适用于其他控件。这比这个问题的任何其他答案都要好得多。 - Ian Oakes
好东西,不仅解决了ComboBox的问题,现在我还可以摆脱WPF工具程序集,只需在我的TextBoxes上使用它,而不是WatermarkedTextBox控件,所以非常赞 :) - 哦,顺便说一下,这是A Geek Trapped而不是Agreed Trap! - dain

3

HappyNomad的解决方案非常好,最终帮助我得出了这个略微不同的解决方案。

<ComboBox x:Name="ComboBoxUploadProject" 
    Grid.Row="2"
    Width="200" 
    Height="23"                           
    Margin="64,0,0,0"
    ItemsSource="{Binding projectList}"
    SelectedValue ="{Binding projectSelect}" 
    DisplayMemberPath="projectName"
    SelectedValuePath="projectId"
    >
    <ComboBox.Template>
        <ControlTemplate TargetType="ComboBox">
            <Grid>
                <ComboBox x:Name="cb" 
                    DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                    ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}}"
                    SelectedValue ="{Binding SelectedValue, RelativeSource={RelativeSource TemplatedParent}}" 
                    DisplayMemberPath="projectName"
                    SelectedValuePath="projectId"
                    />
                <TextBlock x:Name="tb" Text="Select Item..." Margin="3,3,0,0" IsHitTestVisible="False" Visibility="Hidden"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger SourceName="cb" Property="SelectedItem" Value="{x:Null}">
                    <Setter TargetName="tb" Property="Visibility" Value="Visible"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </ComboBox.Template>
</ComboBox>

2
最简单的方法是使用CompositeCollection直接将默认文本和数据库数据合并到ComboBox中,例如:
    <ComboBox x:Name="SelectTeamComboBox" SelectedIndex="0">
        <ComboBox.ItemsSource>
            <CompositeCollection>
                <ComboBoxItem Visibility="Collapsed">-- Select Team --</ComboBoxItem>
                <CollectionContainer Collection="{Binding Source={StaticResource ResourceKey=MyComboOptions}}"/>
            </CompositeCollection>
        </ComboBox.ItemsSource>
    </ComboBox>

在资源中定义StaticResource,将ComboBox选项绑定到您的DataContext,因为在CollectionContainer中直接绑定不正确。
<Window.Resources>
    <CollectionViewSource Source="{Binding}" x:Key="MyComboOptions" />
</Window.Resources>

这样,您就可以仅在xaml中定义ComboBox选项,例如:
   <ComboBox x:Name="SelectTeamComboBox" SelectedIndex="0">
        <ComboBox.ItemsSource>
            <CompositeCollection>
                <ComboBoxItem Visibility="Collapsed">-- Select Team --</ComboBoxItem>
                <ComboBoxItem >Option 1</ComboBoxItem>
                <ComboBoxItem >Option 2</ComboBoxItem>
            </CompositeCollection>
        </ComboBox.ItemsSource>
    </ComboBox>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接