llenar from and wpf combobox

from - wpf combobox selectionchanged



Cómo mostrar el texto predeterminado "--Seleccione Team-" en el cuadro combinado en la carga de la página en WPF? (22)

En una aplicación WPF, en la aplicación MVP, tengo un cuadro combinado, para el cual visualizo los datos obtenidos de la base de datos. Antes de los elementos agregados al cuadro Combo, quiero mostrar el texto predeterminado como

" -- Selecciona un equipo --"

para que en la carga de página se muestre y al seleccionarlo, el texto se borre y los elementos se muestren.

Seleccionar datos de DB está sucediendo. Necesito mostrar el texto predeterminado hasta que el usuario seleccione un elemento del cuadro combinado.

Por favor guíame


// Código XAML

// Código de 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"); } }


Basado en la respuesta de IceForge, preparé una solución reutilizable:

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

ejemplo de uso:

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


Creo que una marca de agua como se menciona en esta publicación funcionaría bien en este caso

Se necesita un poco de código, pero puedes reutilizarlo para cualquier cuadro combinado o cuadro de texto (e incluso cajas de contraseña), así que prefiero esta opción.


EDITAR: según los comentarios a continuación, esta no es una solución. No estoy seguro de cómo lo estaba trabajando, y no puedo verificar ese proyecto.

Es hora de actualizar esta respuesta para el último XAML.

Al encontrar esta pregunta SO para encontrar una solución a esta pregunta, descubrí que la especificación XAML actualizada tiene una solución simple.

Un atributo llamado "Placeholder" ahora está disponible para realizar esta tarea. Es tan simple como esto (en 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>


Establecer IsEditable = True en el elemento Combobox. Esto mostrará la propiedad Text del Combobox


Estoy usando una clase IsNullConverter en mi proyecto y funcionó para mí. aquí está el código para ello en c #, cree una carpeta llamada Convertidor y agregue esta clase en esa carpeta, ya que el desencadenador utilizado no admite valor en lugar de nulo, e IsNullConverter solo hace eso

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."); } }

agregue el espacio de nombres en el archivo xaml como este.

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

medio

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

use esta línea debajo de los recursos para que esté disponible a través del código xaml

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

aquí está el código xaml, usé aquí el disparador así que cada vez que se selecciona un elemento en el cuadro combinado, la visibilidad del texto se vuelve falsa.

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


La forma más fácil que he encontrado para hacer esto es:

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

Obviamente tendrá que agregar sus otras opciones, pero esta es probablemente la forma más sencilla de hacerlo.

Sin embargo, hay un inconveniente en este método, que es cuando el texto dentro de su cuadro combinado no será editable, todavía es seleccionable. Sin embargo, dada la baja calidad y complejidad de cada alternativa que he encontrado hasta la fecha, esta es probablemente la mejor opción.


La forma más sencilla es utilizar CompositeCollection para fusionar texto y datos predeterminados de la base de datos directamente en ComboBox, por ejemplo:

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

Y en Resources define StaticResource para vincular las opciones de ComboBox a su DataContext, porque el enlace directo en CollectionContainer no funciona correctamente.

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

De esta forma, puede definir sus opciones de ComboBox solo en xaml, por ejemplo

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


La solución de HappyNomad fue muy buena y finalmente me ayudó a llegar a esta solución ligeramente diferente.

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


Lo hice antes de vincular el combobox con los datos de la base de datos en código detrás de este modo:

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


Me gusta la respuesta de Tri Q, pero esos convertidores de valor son difíciles de usar. PaulB lo hizo con un controlador de eventos, pero eso también es innecesario. Aquí hay una solución XAML pura:

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


Nadie dijo que una solución xaml pura tiene que ser complicada. Aquí hay uno simple, con 1 desencadenante de datos en el cuadro de texto. Margen y posición según lo deseado

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


No es una buena práctica ... pero funciona bien ...

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

Código detrás

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


No lo intenté con cuadros combinados pero esto me ha funcionado con otros controles ...

blogpost ageektrapped

Él usa la capa adorner aquí para mostrar una marca de agua.


No sé si es compatible directamente, pero podría superponer el combo con una etiqueta y establecerlo en oculto si la selección no es nula.

p.ej.

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

Luego, en la selección cambió el controlador ...

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


Sé que esto es semi viejo, pero ¿qué tal de esta manera?

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


Solo establece el atributo IsEditable en true

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


Un poco tarde pero ...

Una forma más simple sería agregar un elemento de datos ficticio a la lista con el parámetro IsDummy = true y asegurarse de que no sea HitTestVisable y su altura sea de 1 píxel (usando un convertidor) para que no se vea.

Luego de registrarse en SelectionChanged y en ella, establezca el índice en el índice de elementos ficticios.

Funciona como un amuleto y de esta manera no te metas con el estilo y los colores del ComboBox o el tema de tu aplicación.


Yo recomendaría lo siguiente:

Definir un comportamiento

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

Usuario el comportamiento

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


La respuesta de IceForge fue bastante cercana, y es AFAIK la solución más fácil para este problema. Pero falló algo, ya que no funcionaba (al menos para mí, nunca muestra el texto).

Al final, no puede simplemente establecer la propiedad "Visibilidad" de TextBlock en "Oculto" para que se oculte cuando el elemento seleccionado del cuadro combinado no sea nulo; tienes que CONFIGURARLO de esa manera por defecto (ya que no puedes verificar nulo en disparadores , usando un Setter en XAML en el mismo lugar que los Triggers).

Aquí está la solución real basada en la suya, el Setter faltante se coloca justo antes de los desencadenantes:

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


Puede hacer esto sin ningún código atrás mediante el uso de un IValueConverter .

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

Aquí tienes la clase de convertidor que puedes volver a usar.

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 }

Y, por último, debe declarar su convertidor en una sección de recursos.

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

Donde Convertidores es el lugar donde ha colocado la clase de convertidor. Un ejemplo es:

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

Lo bueno de este enfoque es que no hay repetición de código en tu código.


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

El código anterior muestra la forma más sencilla de lograrlo. Después de la carga de la ventana, declare el texto del cuadro combinado, utilizando la propiedad .Text del cuadro combinado. Esto también se puede extender al DatePicker, Textbox y otros controles.