Як відобразити текст за замовчуванням "- Вибрати команду -" у вікні комбінації на завантаженні сторінки у WPF?


109

У додатку WPF у додатку MVP у мене є комбіноване поле, для якого я відображаю дані, отримані з бази даних. Перш ніж елементи, додані до поля Combo, я хочу відобразити текст за замовчуванням, наприклад

"- Вибрати команду -"

щоб під час завантаження сторінки він відображався та підбирав текст, слід очистити текст та відображати елементи.

Вибір даних з БД відбувається. Мені потрібно відображати текст за замовчуванням, поки користувач не вибере елемент із комбінованого поля.

Будь ласка, направляйте мене

Відповіді:


107

Найпростіший спосіб, який я знайшов це зробити:

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

Вам, очевидно, потрібно буде додати інші варіанти, але це, мабуть, найпростіший спосіб зробити це.

Однак у цього методу є один недолік, який полягає в тому, що текст у вашому комбінованому вікні не буде редагуватися, але він все ще може бути обраний. Однак, враховуючи низьку якість та складність кожної альтернативи, яку я знайшов на сьогоднішній день, це, мабуть, найкращий варіант там.


Чудова відповідь, Кріс! Я б просто додати Focusable = "True", але це лише косметичні зміни.
Славіша

6
ідеальна відповідь Кріс. Одна власність може призвести до такої великої різниці: D
Aster Veigas

4
Focusable="False" IsEditable="True" IsReadOnly="True"
Каміль Лелонек

1
+1. На жаль, він не працює зі змішаними елементами (наприклад, якщо об'єкти в коробці є зображеннями).
greenoldman

11
Просте і робоче рішення. Такі органи контролю мають всі проблеми. Тут і там в елементах управління відсутні функції, які знадобляться більшості розробників. Як результат, розробники витрачають свій час на пошук рішень, придбання сторонніх альтернативних засобів контролю або впровадження обхідних завдань ... Чи команда WPF навіть використовує свій контроль для власних розробок?
Чортові овочі

90

Ви можете зробити це без будь-якого коду позаду, скориставшись a 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" />

Де перетворювачі - це місце, де ви розмістили клас перетворювача. Приклад:

xmlns:Converters="clr-namespace:MyProject.Resources.Converters"

Дуже приємна річ у цьому підході - це не повторення коду в коді позаду.


Я хотів би скористатися цим, але, здається, це не дозволено у випадках, коли комбобокс є заголовком таблиці даних. . . XAML видає помилку, що заголовок вже визначений (або, можливо, його неможливо визначити більше одного разу). Якісь ідеї? Я думаю просто використовувати параметр з нульовим значенням, який потім дозволить скинути, вибравши його, але здається трохи неохайним.
Пол Гібсон

1
Велика причина цього відмінне рішення полягає в тому, що майже кожен проект, пов'язаний з даними WPF, використовує NullToVisibilityConverter, так що він вже є там більшу частину часу - можливо, він також скористається ним!
tpartee

2
Насправді ви можете використовувати A, DataTriggerщоб уникнути навіть коду перетворювача тут :)
Біллі 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

Ніхто не сказав, що чистий ксамовий розчин повинен бути складним. Ось простий, з текстовим полем 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 IceForce відповідь не працює для мене, що ви змінили, щоб він працював?
Кріс

1
@ Кріс Я думаю , що він мав в виду , додавши <Setter Property="Visibility" Value="Hidden"/>поза тригера (в стилі) і видалення Visibility="Hidden"від фактичного елемента TextBlock
відновить Моніку ласка

@Mitch, як ви переміщуєте стиль Textblock у ресурс для повторного використання, якщо у вас є ElementName у DataTrigger, що вказує на конкретний об'єкт (mybox)? Чи є якийсь спосіб вказати це ім'я в загальному вигляді?
CrApHeR


16

Я не знаю, чи він безпосередньо підтримується, але ви можете накласти комбо з міткою і встановити його приховано, якщо вибір не є нульовим.

напр.

<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

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>

Приємно. Я вийшов з нього, зв'язавши DataContext TextBlock за допомогою відносного джерела, щоб уникнути необхідності встановлення імені. Дивіться наступний коментар до розмітки (код у коментарях SO виглядає некрасиво)
Sascha

<TextBlock DataContext = "{Binding Path = Children [0] .SelectedItem, RelativeSource = {RelativeSource AncestorType = Grid}}" Text = "- Виберіть проект -" Style = "{StaticResource ComboBoxSelectOverlay}" />
Sascha

4

Не пробував це з комбінованими коробками, але це працювало для мене з іншими елементами управління ...

ageektrapped blogpost

Тут він використовує шар прикраси для відображення водяного знака.


Щойно завантажили та спробували цей код. Здається, працює як рекламується. Дозволяє прикрасити комбо простим доданим властивістю, що містить ваш водяний знак. Він також працює для інших елементів управління. Це набагато кращий підхід, ніж будь-який з інших відповідей на це питання.
Ян Оукс

Хороший матеріал, він не тільки вирішує проблему ComboBox, але тепер я можу позбутися складання інструментів WPF і просто використовувати це на своїх TextBoxes замість управління WatermarkedTextBox теж, так що виграш :) - О, btw, це Geek Trapped не Погоджена пастка!
Дейн

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>

А в ресурсах визначте StaticResource для прив’язки параметрів ComboBox до вашого DataContext, оскільки пряме прив'язування у CollectionContainer працює не правильно.

<Window.Resources>
    <CollectionViewSource Source="{Binding}" x:Key="MyComboOptions" />
</Window.Resources>

Таким чином ви можете визначити свої параметри ComboBox лише у xaml, наприклад

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

Це працює як шарм для однієї комбінованої коробки. Але коли я використовую його з більш ніж 1 комбо, він дає мені помилку (але компілює та працює добре) "" Властивість DefaultText ", вже зареєстрована" ComboBox "". Я згадував виправлення у своєму блозі.
Ромеш Д. Ніріелла

Дякуємо, що вказали на це. Я не міг створити цю помилку на своєму комп’ютері. Однак я погоджуюсь, що typeof (ComboBoxBehaviors) повинен бути переданий у 3-му параметрі RegisterAttached замість typeof (ComboBox).
Usman Zafar

Хоча ця публікація трохи стара, я не бачу, як вона може працювати. Bg комбінації встановлюється через тригери w / декілька умов. Спробуйте розмістити комбінацію поодинці на сітці та вручну встановіть bg на "червоний". Це не впливає на область, де ви хочете з’явити водяний знак. Це може вплинути лише на bg за панеллю, що випадає. Краще рішення - скопіювати шаблон керування комбінації ящиків і додати кілька тригерів і стилів, щоб намалювати візуальну пензлик, що складається з текстового блоку, на задній план межі.
Ньюклік

1

Відповідь IceForge була досить близькою, і AFAIK є найпростішим рішенням цієї проблеми. Але щось пропустило, оскільки воно не працювало (принаймні для мене, він ніколи насправді не відображає текст).

Зрештою, ви не можете просто встановити властивість TextBlock на "Прихований" для того, щоб він був прихований, коли вибраний елемент у вікні комбінації недійсний; ви повинні встановити його таким чином за замовчуванням (оскільки ви не можете перевірити не нульові тригери , використовуючи Setter в XAML там же, як і тригери.

Ось власне рішення, засноване на його, відсутній сетер розміщується перед тригерами:

<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

EDIT: За коментарями нижче, це не є рішенням. Не впевнений, як я працював, і не можу перевірити цей проект.

Час оновити цю відповідь для останньої XAML.

Знайшовши це питання, шукаючи рішення цього питання, я виявив, що оновлений специфікат 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, але він працював.
Робд Садлер

1
Хоча я і не є нижчим виборцем, я уявляю, що ви проголосували за те, що PlaceholderTextв System.Windows.ComboBoxкласі немає власності . Це питання про WPF, а не про WinForms.
Шерідан

Людина це дивно - я знаю, що я працював над додатком XAML, і я знаю, що я щойно це виявив і побачив, як це працює. Можливо, в проект було включено розширення? IDK - з тих пір я переглянув, і напевно у ComboBox немає Заповнювача. Я не можу повернутися до проекту, над яким працював - старого клієнта. тьфу.
Робд Садлер

2
Ви не помиляєтеся, але це не для WPF. UWP ComboBox має це, будь ласка, дивіться цю сторінку: 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 у своєму проекті, і він працював на мене. ось код для нього в c #, створіть папку з назвою Converter і додайте цей клас у цю папку, оскільки тригер, який використовується, не підтримує значення, а не null, а 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, я використовував тут тригер, тому щоразу, коли елемент обраний у поле комбінації, видимість вашого тексту стає хибною.

<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
Це просто додає текст як варіант у спадному меню. Ось цього не просив ОП.
Дін Фрідленд

йдеться про додавання тексту за замовчуванням, і я зробив це таким чином
Atiq Baqi

0
  1. Поставте етикетку поверх комбобокса.

  2. Прив’яжіть вміст мітки до властивості Text Combobox.

  3. Встановіть непрозорість комбобокса на нуль, непрозорість = 0.

  4. Напишіть текст за замовчуванням у властивість текстового списку

          <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
ComboBoxне має EmptySelectionBoxTemplateвласності.
Novitchi S
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.