WPFのページロード時にコンボボックスにデフォルトのテキスト「--Select Team-」を表示する方法


109

WPFアプリのMVPアプリには、データベースから取得したデータを表示するコンボボックスがあります。コンボボックスにアイテムを追加する前に、次のようなデフォルトのテキストを表示したい

" - チームを選ぶ - "

ページロード時に表示され、選択時にテキストがクリアされ、アイテムが表示されるようにします。

DBからデータを選択しています。ユーザーがコンボボックスからアイテムを選択するまで、デフォルトのテキストを表示する必要があります。

案内してください

回答:


107

これを行うために私が見つけた最も簡単な方法は次のとおりです。

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

他のオプションを追加する必要があることは明らかですが、これがおそらく最も簡単な方法です。

ただし、この方法には1つの欠点があります。コンボボックス内のテキストは編集できませんが、選択は可能です。しかし、私がこれまでに見つけたすべての代替案の質と複雑さが低いことを考えると、これはおそらくそこにある最良の選択肢です。


すばらしい答えクリス!Focusable = "True"を追加するだけですが、それは表面的な変更です。
スラビサ2013年

6
完璧な答えクリス。1つの特性は、このような大きな違いを行うことができます:D
アスターVeigas

4
Focusable="False" IsEditable="True" IsReadOnly="True"
Kamil Lelonek 2014年

1
+1。残念ながら、混合アイテムでは機能しません(たとえば、コンボボックスアイテムが画像の場合)。
greenoldman 2014年

11
シンプルで実用的なソリューション。WPFコントロールには、フレームワーク全体でこの種の問題があります。あちこちに、コントロールにはほとんどの開発者が必要とする機能が欠けています。その結果、開発者は、ソリューションの検索、サードパーティの代替コントロールの購入、または回避策の実装に時間を費やしています... WPFチームは、独自の開発にコントロールを使用していますか?
くそー野菜

90

コードビハインドなしでこれを行うには、を使用し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は、ヘッダーがすでに定義されている(またはおそらく複数回定義できない)というエラーを出します。何か案は?null値のオプションを使用することを考えています。これを選択するとリセットできますが、少しずさんなようです。
Paul Gibson

1
これが優れたソリューションである大きな理由は、ほぼすべてのデータバインドWPFプロジェクトがNullToVisibilityConverterを利用するため、ほとんどの場合既に存在しているため、それを利用する可能性もあります。
tpartee

2
実際には、DataTriggerここでコンバータコードさえも回避するためにa を使用できます:)
Billy ONeal

49

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

33

純粋なxamlソリューションは複雑でなければならないという人はいません。これは、テキストボックスに1つのデータトリガーがある単純なものです。マージンと必要に応じた位置

<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の答えは私にとってうまくいきません、それを動かすために何を変更しましたか?
クリス

1
@Chris彼は<Setter Property="Visibility" Value="Hidden"/>トリガーの外(スタイル内)に追加Visibility="Hidden"し、実際のtextblock要素から削除することを意味していたと思います
モニカを

@ Mitch、DataTriggerのElementNameが特定のオブジェクト(mybox)を指している場合、Textblockスタイルをリソースに移動して再利用するにはどうすればよいですか?その名前を一般的な方法で指定する方法はありますか?
CrApHeR 2015年

23

要素に設定IsEditable="True"ComboBoxます。これTextにより、のプロパティが表示されますComboBox


2
これは、ロット全体の中で最も単純なソリューションです。
Sergey Koulikov

6
ただし、コントロールの外観は変わります
simonalexander2005

16

直接サポートされているかどうかはわかりませんが、コンボをラベルでオーバーレイし、選択範囲がnullでない場合は非表示に設定できます。

例えば。

<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
SelectionChangedハンドラーを作成する代わりに、TextBlockの可視性をXAMLで設定できます。
aliceraunsbaek 2013

6

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をバインドすることで、それを拡張しました。マークアップの次のコメントを参照してください(SOのコメントのコードは醜く見えます)
Sascha

<TextBlock DataContext = "{Binding Path = Children [0] .SelectedItem、RelativeSource = {RelativeSource AncestorType = Grid}}" Text = "-プロジェクトの選択-" Style = "{StaticResource ComboBoxSelectOverlay}" />
Sascha

4

コンボボックスで試してみませんでしたが、これは他のコントロールで私にとってはうまくいきました...

ageektrappedブログ投稿

ここでは、アドナーレイヤーを使用して透かしを表示しています。


このコードをダウンロードして試してみました。宣伝どおりに動作するようです。透かしを含む単純な添付プロパティでコンボを装飾できます。他のコントロールでも同様に機能します。これは、この質問に対する他のどの回答よりもはるかに優れたアプローチです。
Ian Oakes 2011

ComboBoxの問題を解決するだけでなく、WPFツールアセンブリを削除して、WatermarkedTextBoxコントロールの代わりにこれをTextBoxesでも使用できるようになりました。同意したわな!
dain

2

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>

また、Resourcesで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>

1

私は以下をお勧めします:

動作を定義する

public static class ComboBoxBehaviors
{
    public static readonly DependencyProperty DefaultTextProperty =
        DependencyProperty.RegisterAttached("DefaultText", typeof(String), typeof(ComboBox), new PropertyMetadata(null));

    public static String GetDefaultText(DependencyObject obj)
    {
        return (String)obj.GetValue(DefaultTextProperty);
    }

    public static void SetDefaultText(DependencyObject obj, String value)
    {
        var combo = (ComboBox)obj;

        RefreshDefaultText(combo, value);

        combo.SelectionChanged += (sender, _) => RefreshDefaultText((ComboBox)sender, GetDefaultText((ComboBox)sender));

        obj.SetValue(DefaultTextProperty, value);
    }

    static void RefreshDefaultText(ComboBox combo, string text)
    {
        // if item is selected and DefaultText is set
        if (combo.SelectedIndex == -1 && !String.IsNullOrEmpty(text))
        {
            // Show DefaultText
            var visual = new TextBlock()
            {
                FontStyle = FontStyles.Italic,
                Text = text,
                Foreground = Brushes.Gray
            };

            combo.Background = new VisualBrush(visual)
            {
                Stretch = Stretch.None,
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Center,
                Transform = new TranslateTransform(3, 0)
            };
        }
        else
        {
            // Hide DefaultText
            combo.Background = null;
        }
    }
}

ユーザーの行動

<ComboBox Name="cmb" Margin="72,121,0,0" VerticalAlignment="Top"
          local:ComboBoxBehaviors.DefaultText="-- Select Team --"/>

これは、単一のコンボボックスの魅力のように機能します。しかし、複数のコンボで使用すると、エラーが発生します(ただし、コンパイルと実行は正常に行われます)。私は自分のブログで修正について述べました。
Romesh D. Niriella 2014

これを指摘してくれてありがとう。コンピューターでこのエラーを生成できませんでした。ただし、typeof(ComboBoxBehaviors)は、typeof(ComboBox)の代わりにRegisterAttachedの3番目のパラメーターで渡す必要があることに同意します。
ウスマンザファール2014年

この投稿は少し古いですが、どのように機能するかわかりません。コンボのbgは、複数の条件付きのトリガーを介して設定されます。グリッドに単独でコンボを配置し、手動でbgを「赤」に設定してみてください。透かしを表示する領域には影響しません。ドロップダウンパネルの後ろのbgにのみ影響する場合があります。より良い解決策は、コンボボックスのコントロールテンプレートをコピーし、いくつかのトリガーとスタイルを追加して、テキストブロックで構成される視覚的なブラシを境界線の背景にペイントすることです。
Newclique 2016年

1

IceForgeの答えはかなり近いものであり、この問題に対する最も簡単な解決策はAFAIKです。しかし、機能していなかったため、何かを見逃しました(少なくとも私にとっては、実際にテキストが表示されることはありません)。

最後に、TextBlockの "Visibility"プロパティを "Hidden"に設定するだけでは、コンボボックスの選択された項目がnullでないときに非表示にすることができません。トリガーと同じ場所でXAMLのセッターを使用して、トリガーnullでないことを確認できないため、デフォルトでそのように設定する必要があります。

彼に基づく実際の解決策は次のとおりです。欠落しているセッターはトリガーの直前に配置されます。

<ComboBox x:Name="combo"/>
<TextBlock Text="--Select Team--" IsHitTestVisible="False">
    <TextBlock.Style>
        <Style TargetType="TextBlock">

            <Style.Setters>
                <Setter Property="Visibility" Value="Hidden"/>
            </Style.Setters>

            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=combo,Path=SelectedItem}" Value="{x:Null}">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

1

編集:以下のコメントによると、これは解決策ではありません。どのように機能していたかわからず、そのプロジェクトを確認できません。

この回答を最新のXAMLに更新する時がきました。

このSOの質問を見つけて、この質問の解決策を探したところ、更新されたXAML仕様には単純な解決策があることがわかりました。

このタスクを実行するために、「プレースホルダー」と呼ばれる属性が利用可能になりました。これは(Visual Studio 2015では)これと同じくらい簡単です。

<ComboBox x:Name="Selection" PlaceholderText="Select...">
    <x:String>Item 1</x:String>
    <x:String>Item 2</x:String>
    <x:String>Item 3</x:String>
</ComboBox>

私はこのソリューションを使用しました-否定的な有権者は詳しく説明しますか?確かに私はXAMLのエキスパートではありませんが、うまくいきました。
Robb Sadler

1
私は反対投票者ではありませんPlaceholderTextが、System.Windows.ComboBoxクラスにプロパティがないため、反対投票されたと思います。これは、WinFormsではなく、WPFに関する質問です。
シェリダン

これは奇妙なことです。私はXAMLアプリで作業していたことを知っています。また、これを発見して、それが機能することを確認したところです。プロジェクトに拡張機能が含まれていたのでしょうか?IDK-私はそれ以来調べており、確かにComboBoxにプレースホルダーはありません。私が取り組んでいたプロジェクト-古いクライアントに戻ることができません。うん。
Robb Sadler

2
あなたは間違っていませんが、それはWPFには当てはまりません。UWPコンボボックスがこれを持って、このページを参照してください。msdn.microsoft.com/en-us/library/windows/apps/...は
laishiekai

0

ベストプラクティスではありませんが、正常に動作します...

<ComboBox GotFocus="Focused"  x:Name="combobox1" HorizontalAlignment="Left" Margin="8,29,0,0" VerticalAlignment="Top" Width="128" Height="117"/>

コードビハインド

public partial class MainWindow : Window
{
    bool clearonce = true;
    bool fillonce = true;
    public MainWindow()
    {
        this.InitializeComponent();          
        combobox1.Items.Insert(0, " -- Select Team --");
        combobox1.SelectedIndex = 0;
    }

    private void Focused(object sender, RoutedEventArgs e)
    {
            if(clearonce)
            {
                combobox1.Items.Clear();
                clearonce = false;
            }
            if (fillonce)
            {
              //fill the combobox items here 
                for (int i = 0; i < 10; i++)
                {
                    combobox1.Items.Insert(i, i);
                }
                fillonce = false;
            }           
    }
}

0

この投稿で言及さている透かしは、この場合にうまく機能すると思います

必要なコードは少しありますが、任意のコンボボックスまたはテキストボックス(さらにはパスワードボックス)に再利用できるので、この方法をお勧めします


0

私のプロジェクトではIsNullConverterクラスを使用していますが、それがうまくいきました。使用されているトリガーはnullではなくの値をサポートしていないため、C#でのコード、Converterという名前のフォルダーを作成し、そのフォルダーにこのクラスを追加します。IsNullConverterはそれを行うだけです

 public class IsNullConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value == null);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
    }
}

このようにxamlファイルに名前空間を追加します。

xmlns:Converters="clr-namespace:TymeSheet.Converter"

手段

xmlns:Converters="clr-namespace:YourProjectName.Converter"

リソースの下のこの行を使用して、xamlコードで使用できるようにします

<Converters:IsNullConverter x:Key="isNullConverter" />

これがxamlコードです。ここではトリガーを使用しているため、コンボボックスでアイテムが選択されるたびに、テキストの可視性がfalseになります。

<TextBlock Text="Select Project" IsHitTestVisible="False" FontFamily="/TimeSheet;component/Resources/#Open Sans" FontSize="14" Canvas.Right="191" Canvas.Top="22">
                        <TextBlock.Resources>
                            <Converters:IsNullConverter x:Key="isNullConverter"/>
                        </TextBlock.Resources>
                        <TextBlock.Style>
                            <Style TargetType="TextBlock">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding ElementName=ProjectComboBox,Path=SelectedItem,Converter={StaticResource isNullConverter}}" Value="False">
                                        <Setter Property="Visibility" Value="Hidden"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </TextBlock.Style>
                    </TextBlock>

0

// XAMLコード

// ViewModelコード

    private CategoryModel _SelectedCategory;
    public CategoryModel SelectedCategory
    {
        get { return _SelectedCategory; }
        set
        {
            _SelectedCategory = value;
            OnPropertyChanged("SelectedCategory");
        }
    }

    private ObservableCollection<CategoryModel> _Categories;
    public ObservableCollection<CategoryModel> Categories
    {
        get { return _Categories; }
        set
        {
            _Categories = value;
            _Categories.Insert(0, new CategoryModel()
            {
                CategoryId = 0,
                CategoryName = " -- Select Category -- "
            });
            SelectedCategory = _Categories[0];
            OnPropertyChanged("Categories");

        }
    }

0

少し遅れますが…

もっと簡単な方法は、パラメーターIsDummy = trueを使用してダミーデータアイテムをリストに追加し、HitTestVisableでなく、その高さが1ピクセルであること(コンバーターを使用)を確認して、表示されないようにすることです。

SelectionChangedに登録して、その中でインデックスをダミーアイテムインデックスに設定するだけではありません。

それは魅力のように機能し、このようにして、ComboBoxやアプリケーションテーマのスタイルや色を変更する必要がありません。


0
InitializeComponent()
yourcombobox.text=" -- Select Team --";

上記のコードは、それを実現する最も簡単な方法を示しています。ウィンドウのロード後、コンボボックスの.Textプロパティを使用して、コンボボックスのテキストを宣言します。これは、DatePicker、Textbox、その他のコントロールにも拡張できます。


0

このように分離コードでデータベースからのデータをコンボボックスにバインドする前にそれを行いました-

Combobox.Items.Add("-- Select Team --");
Combobox.SelectedIndex = 0;

1
これは、ドロップダウンのオプションとしてテキストを追加するだけです。それはOPが求めていたものではありませんでした。
ディーンフリードランド

それはデフォルトのテキストを追加することであり、私はこの方法でそれを行いました
Atiq Baqi 2018

0
  1. コンボボックスの上にラベルを貼ります。

  2. ラベルのコンテンツをコンボボックスのTextプロパティにバインドします。

  3. コンボボックスの不透明度をゼロ、Opacity = 0に設定します。

  4. コンボボックスのTextプロパティにデフォルトのテキストを書き込む

          <ComboBox Name="cb"
            Text="--Select Team--" Opacity="0" 
            Height="40" Width="140" >
             <ComboBoxItem Content="Manchester United" />
             <ComboBoxItem Content="Lester" />
         </ComboBox>
     </Grid>

-2

IsEditable属性のみをtrueに設定します

<ComboBox Name="comboBox1"            
          Text="--Select Team--"
          IsEditable="true"  <---- that's all!
          IsReadOnly="true"/>

-3

私はこれが半ば古いことを知っていますが、この方法はどうですか:

<DataTemplate x:Key="italComboWM">
    <TextBlock FontSize="11" FontFamily="Segoe UI" FontStyle="Italic" Text="--Select an item--" />
</DataTemplate>

<ComboBox EmptySelectionBoxTemplate="{StaticResource italComboWM}" />

2
ComboBoxEmptySelectionBoxTemplateプロパティはありません。
Novitchi S 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.