llenar from and c# wpf combobox

c# - from - ¿Cómo puedo hacer que un cuadro combinado de WPF tenga el ancho de su elemento más ancho en XAML?



wpf combobox selectionchanged (14)

Coloque un cuadro de lista que contenga el mismo contenido detrás de Dropbox. Luego aplique la altura correcta con un enlace como este:

<Grid> <ListBox x:Name="listBox" Height="{Binding ElementName=dropBox, Path=DesiredSize.Height}" /> <ComboBox x:Name="dropBox" /> </Grid>

Sé cómo hacerlo en código, pero ¿se puede hacer esto en XAML?

Window1.xaml:

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top"> <ComboBoxItem>ComboBoxItem1</ComboBoxItem> <ComboBoxItem>ComboBoxItem2</ComboBoxItem> </ComboBox> </Grid> </Window>

Window1.xaml.cs:

using System.Windows; using System.Windows.Controls; namespace WpfApplication1 { public partial class Window1 : Window { public Window1() { InitializeComponent(); double width = 0; foreach (ComboBoxItem item in ComboBox1.Items) { item.Measure(new Size( double.PositiveInfinity, double.PositiveInfinity)); if (item.DesiredSize.Width > width) width = item.DesiredSize.Width; } ComboBox1.Measure(new Size( double.PositiveInfinity, double.PositiveInfinity)); ComboBox1.Width = ComboBox1.DesiredSize.Width + width; } } }


El enfoque de Alun Harford, en la práctica:

<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- hidden listbox that has all the items in one grid --> <ListBox ItemsSource="{Binding Items, ElementName=uiComboBox, Mode=OneWay}" Height="10" VerticalAlignment="Top" Visibility="Hidden"> <ListBox.ItemsPanel><ItemsPanelTemplate><Grid/></ItemsPanelTemplate></ListBox.ItemsPanel> </ListBox> <ComboBox VerticalAlignment="Top" SelectedIndex="0" x:Name="uiComboBox"> <ComboBoxItem>foo</ComboBoxItem> <ComboBoxItem>bar</ComboBoxItem> <ComboBoxItem>fiuafiouhoiruhslkfhalsjfhalhflasdkf</ComboBoxItem> </ComboBox> </Grid>


En cuanto a mí, la solución para expandir ComboBox.Width a todo el ancho de la columna, estaba configurando el ancho ColumnDefinition en "*" en lugar de "Auto":

<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="140" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Label Content="List of items" Grid.Column="0" Margin="3" /> <ComboBox Grid.Column="1" ItemsSource="{Binding Path=DestinationSubDivisions}" SelectedValue="{Binding Path=TransferRequest.DestinationSubDivision}" DisplayMemberPath="Name" Margin="3" /> </Grid>


En función de las otras respuestas anteriores, esta es mi versión:

<Grid HorizontalAlignment="Left"> <ItemsControl ItemsSource="{Binding EnumValues}" Height="0" Margin="15,0"/> <ComboBox ItemsSource="{Binding EnumValues}" /> </Grid>

HorizontalAlignment = "Left" detiene los controles utilizando todo el ancho del control que lo contiene. Height = "0" oculta el control de elementos.
Margin = "15,0" permite un cromo adicional alrededor de los elementos del cuadro combinado (no me molesta el cromo agnóstico).


En mi caso, una forma mucho más simple parecía hacer el truco, acabo de utilizar un stackPanel adicional para envolver el cuadro combinado.

<StackPanel Grid.Row="1" Orientation="Horizontal"> <ComboBox ItemsSource="{Binding ExecutionTimesModeList}" Width="Auto" SelectedValuePath="Item" DisplayMemberPath="FriendlyName" SelectedValue="{Binding Model.SelectedExecutionTimesMode}" /> </StackPanel>

(trabajado en visual studio 2008)


Estaba buscando la respuesta yo mismo, cuando me encontré con el método UpdateLayout() que tiene cada UIElement .

¡Es muy simple ahora, afortunadamente!

Simplemente llame a ComboBox1.Updatelayout(); después de establecer o modificar el ItemSource .


Esto mantiene el ancho hasta el elemento más ancho, pero solo después de abrir el cuadro combinado una vez.

<ComboBox ItemsSource="{Binding ComboBoxItems}" Grid.IsSharedSizeScope="True" HorizontalAlignment="Left"> <ComboBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition SharedSizeGroup="sharedSizeGroup"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding}"/> </Grid> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox>


Esto no puede ser en XAML sin:

  • Creando un control oculto (respuesta de Alan Hunford)
  • Cambiar el ControlTemplate drásticamente. Incluso en este caso, es posible que deba crearse una versión oculta de ItemsPresenter.

La razón de esto es que las ComboBox ControlTemplates predeterminadas que he encontrado (Aero, Luna, etc.) anidan todo el ItemsPresenter en una ventana emergente. Esto significa que el diseño de estos elementos se difiere hasta que realmente se hagan visibles.

Una manera fácil de probar esto es modificar el ControlTemplate predeterminado para vincular el MinWidth del contenedor más externo (es una cuadrícula para Aero y Luna) con el Ancho real de PART_Popup. Podrá hacer que el ComboBox sincronice automáticamente su ancho al hacer clic en el botón desplegable, pero no antes.

Entonces, a menos que pueda forzar una operación de Medir en el sistema de disposición (lo cual puede hacer agregando un segundo control), no creo que se pueda hacer.

Como siempre, estoy abierto a una solución corta y elegante, pero en este caso un código subyacente o un control dual / cortes de ControlTemplate son las únicas soluciones que he visto.


No puedes hacerlo directamente en Xaml pero puedes usar este comportamiento adjunto. (El ancho será visible en el Diseñador)

<ComboBox behaviors:ComboBoxWidthFromItemsBehavior.ComboBoxWidthFromItems="True"> <ComboBoxItem Content="Short"/> <ComboBoxItem Content="Medium Long"/> <ComboBoxItem Content="Min"/> </ComboBox>

Comportamiento adjunto ComboBoxWidthFromItemsProperty

public static class ComboBoxWidthFromItemsBehavior { public static readonly DependencyProperty ComboBoxWidthFromItemsProperty = DependencyProperty.RegisterAttached ( "ComboBoxWidthFromItems", typeof(bool), typeof(ComboBoxWidthFromItemsBehavior), new UIPropertyMetadata(false, OnComboBoxWidthFromItemsPropertyChanged) ); public static bool GetComboBoxWidthFromItems(DependencyObject obj) { return (bool)obj.GetValue(ComboBoxWidthFromItemsProperty); } public static void SetComboBoxWidthFromItems(DependencyObject obj, bool value) { obj.SetValue(ComboBoxWidthFromItemsProperty, value); } private static void OnComboBoxWidthFromItemsPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e) { ComboBox comboBox = dpo as ComboBox; if (comboBox != null) { if ((bool)e.NewValue == true) { comboBox.Loaded += OnComboBoxLoaded; } else { comboBox.Loaded -= OnComboBoxLoaded; } } } private static void OnComboBoxLoaded(object sender, RoutedEventArgs e) { ComboBox comboBox = sender as ComboBox; Action action = () => { comboBox.SetWidthFromItems(); }; comboBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); } }

Lo que hace es que llama a un método de extensión para ComboBox llamado SetWidthFromItems que (invisiblemente) se expande y contrae a sí mismo y luego calcula el ancho basado en los ComboBoxItems generados. (IExpandCollapseProvider requiere una referencia a UIAutomationProvider.dll)

Luego método de extensión SetWidthFromItems

public static class ComboBoxExtensionMethods { public static void SetWidthFromItems(this ComboBox comboBox) { double comboBoxWidth = 19;// comboBox.DesiredSize.Width; // Create the peer and provider to expand the comboBox in code behind. ComboBoxAutomationPeer peer = new ComboBoxAutomationPeer(comboBox); IExpandCollapseProvider provider = (IExpandCollapseProvider)peer.GetPattern(PatternInterface.ExpandCollapse); EventHandler eventHandler = null; eventHandler = new EventHandler(delegate { if (comboBox.IsDropDownOpen && comboBox.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { double width = 0; foreach (var item in comboBox.Items) { ComboBoxItem comboBoxItem = comboBox.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem; comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); if (comboBoxItem.DesiredSize.Width > width) { width = comboBoxItem.DesiredSize.Width; } } comboBox.Width = comboBoxWidth + width; // Remove the event handler. comboBox.ItemContainerGenerator.StatusChanged -= eventHandler; comboBox.DropDownOpened -= eventHandler; provider.Collapse(); } }); comboBox.ItemContainerGenerator.StatusChanged += eventHandler; comboBox.DropDownOpened += eventHandler; // Expand the comboBox to generate all its ComboBoxItem''s. provider.Expand(); } }

Este método de extensión también proporciona la capacidad de llamar

comboBox.SetWidthFromItems();

en código detrás (por ejemplo, en el evento ComboBox.Loaded)


Puede vincular el ancho de cualquier contenedor que desee.

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" x:Name="Window1"> <Grid> <ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top"> <ComboBox.Width> <Binding ElementName="Window1" Path="ActualWidth"/> </ComboBox.Width> <ComboBoxItem>ComboBoxItem1</ComboBoxItem> <ComboBoxItem>ComboBoxItem2</ComboBoxItem> </ComboBox> </Grid>

Para obtener exactamente lo que estás tratando de hacer con el C # que escribiste, consideraría implícitamente un IValueConverter o IMultiValueConverter.


Sí, este es un poco desagradable.

Lo que he hecho en el pasado es agregar al ControlTemplate un listbox oculto (con sus elementoscontainerpanel establecidos en una grilla) mostrando cada elemento al mismo tiempo pero con su visibilidad configurada como oculta.

Me encantaría saber si hay alguna idea mejor que no se base en un código subyacente horrible o que su punto de vista tenga que entender que necesita usar un control diferente para proporcionar el ancho que soporte las imágenes (¡asqueroso!).


Solo agregue un ancho al cuadro combinado

<ComboBox Name="ComboBox1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100">


Terminé con una solución "lo suficientemente buena" para que el cuadro combinado nunca se redujera por debajo del tamaño más grande que tenía, similar al antiguo WinForms AutoSizeMode = GrowOnly.

La forma en que lo hice fue con un convertidor de valor personalizado:

public class GrowConverter : IValueConverter { public double Minimum { get; set; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { var dvalue = (double)value; if (dvalue > Minimum) Minimum = dvalue; else if (dvalue < Minimum) dvalue = Minimum; return dvalue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotSupportedException(); } }

Luego configuro el cuadro combinado en XAML así:

<Whatever> <Whatever.Resources> <my:GrowConverter x:Key="grow" /> </Whatever.Resources> ... <ComboBox MinWidth="{Binding ActualWidth,RelativeSource={RelativeSource Self},Converter={StaticResource grow}}" /> </Whatever>

Tenga en cuenta que con esto necesita una instancia separada de GrowConverter para cada cuadro combinado, a menos que, por supuesto, desee un conjunto de ellos para el tamaño, similar a la función SharedSizeScope de la cuadrícula.


Un seguimiento de la respuesta de Maleak: me gustó tanto la implementación que escribí un comportamiento real para ello. Obviamente necesitará el SDK de Blend para que pueda hacer referencia a System.Windows.Interactivity.

XAML:

<ComboBox ItemsSource="{Binding ListOfStuff}"> <i:Interaction.Behaviors> <local:ComboBoxWidthBehavior /> </i:Interaction.Behaviors> </ComboBox>

Código:

using System; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Interactivity; namespace MyLibrary { public class ComboBoxWidthBehavior : Behavior<ComboBox> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.Loaded += OnLoaded; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.Loaded -= OnLoaded; } private void OnLoaded(object sender, RoutedEventArgs e) { var desiredWidth = AssociatedObject.DesiredSize.Width; // Create the peer and provider to expand the comboBox in code behind. var peer = new ComboBoxAutomationPeer(AssociatedObject); var provider = peer.GetPattern(PatternInterface.ExpandCollapse) as IExpandCollapseProvider; if (provider == null) return; EventHandler[] handler = {null}; // array usage prevents access to modified closure handler[0] = new EventHandler(delegate { if (!AssociatedObject.IsDropDownOpen || AssociatedObject.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return; double largestWidth = 0; foreach (var item in AssociatedObject.Items) { var comboBoxItem = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem; if (comboBoxItem == null) continue; comboBoxItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); if (comboBoxItem.DesiredSize.Width > largestWidth) largestWidth = comboBoxItem.DesiredSize.Width; } AssociatedObject.Width = desiredWidth + largestWidth; // Remove the event handler. AssociatedObject.ItemContainerGenerator.StatusChanged -= handler[0]; AssociatedObject.DropDownOpened -= handler[0]; provider.Collapse(); }); AssociatedObject.ItemContainerGenerator.StatusChanged += handler[0]; AssociatedObject.DropDownOpened += handler[0]; // Expand the comboBox to generate all its ComboBoxItem''s. provider.Expand(); } } }