I can't find any documentation on how to size the dropdown portion of the combobox. I have two columns and 4 or 5 rows. I want all columns and rows to be visible when the use expands their selections. Right now it's only as wide as the combo and it's showing less than one row.
Thank you.
Mike
Hi Mike,
I've been looking into your requirements and I believe I have the right sample for you. This sample demonstrates how to change the dropdown width. The sample also automatically sizes the dropdown width to show all the columns.
In the sample I handle the dropdown opening event and inside this event I traverse the visual tree of the combo editor to get the Popup that is used for the dropdown. From the Popup it's possible to change the width and height of the dropdown.
Let me know if you have any questions on this.
Rob, this is really great. I actually saw the project in another answer and started working with it this morning.
I can see in your example that the two string columns are sized correctly, however in my situation I have an image column and a string column. For some reason in the popup_Opened event around line 53, I only have one control in the itemsPanel. In your example, both of your columns show up, but in mine, only the first one will show up.
My xaml code is as follows:
<ig:XamMultiColumnComboEditor x:Name="cbInternalSecretion" Width="93" VerticalAlignment="Bottom" AllowMultipleSelection="False" DisplayMemberPath="PickListItem" AutoGenerateColumns="False" SelectedItemsResetButtonVisibility="Collapsed" ItemsSource="{Binding DataContext.InternalSecretion, ElementName=LayoutRoot, Mode=OneWay}" SelectedItem="{Binding DataContext.InternalSecretionSelectedItem, ElementName=LayoutRoot, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" SelectionChanged="cbInternalSecretion_SelectionChanged" Loaded="cbInternalSecretion_Loaded" Panel.ZIndex="0"> <ig:XamMultiColumnComboEditor.Resources> <Style TargetType="{x:Type ig:ComboHeaderCellControl}"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content}" Value="{x:Null}"> <Setter Property="Visibility" Value="Collapsed" /> </DataTrigger> </Style.Triggers> </Style> <Style TargetType="{x:Type ig:ComboCellControl}"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=HasContent}" Value="False"> <Setter Property="Visibility" Value="Collapsed" /> </DataTrigger> </Style.Triggers> </Style> </ig:XamMultiColumnComboEditor.Resources> <ig:XamMultiColumnComboEditor.Columns> <ig:TextComboColumn Key="PickListItem" HeaderText="Infection Classification" /> <ig:ImageComboColumn Key="Thumbnail" HeaderText="Thumbnail Photo" /> </ig:XamMultiColumnComboEditor.Columns> </ig:XamMultiColumnComboEditor>
My code behind is almost exactly what is in your sample except for a check on the child count which I added to prevent a zero value exception.
private void cbInternalSecretion_Loaded( object sender, RoutedEventArgs e ) { try { if( VisualTreeHelper.GetChildrenCount( sender as XamMultiColumnComboEditor ) > 0 ) { Grid grid = VisualTreeHelper.GetChild( sender as XamMultiColumnComboEditor, 0 ) as Grid; Popup popup = ( ( grid.Children[ 0 ] as Border ).Child as Grid ).Children[ 0 ] as Popup; // This will change the white space to gray, you really can't get rid of the space though unless the dropdown size // is restricted. ( ( popup.Child as Grid ).Children[ 0 ] as Border ).Background = new SolidColorBrush( Colors.Gray ); // Handle the popup opened event. The grid rows will exist by the time this event is fired. popup.Opened += new EventHandler( popup_Opened ); } } catch( Exception ) { throw; } } void popup_Opened( object sender, EventArgs e ) { Popup popup = sender as Popup; DependencyObject itemsPanel = FindFirstRow( popup.Child ); if( itemsPanel != null && VisualTreeHelper.GetChildrenCount( itemsPanel ) > 0 ) { double width = 0; for( int i = 0; i < VisualTreeHelper.GetChildrenCount( itemsPanel ); i++ ) { ComboCellControl control = VisualTreeHelper.GetChild( itemsPanel, i ) as ComboCellControl; width += control.ActualWidth; } ( ( popup.Child as System.Windows.Controls.Grid ).Children[ 0 ] as Border ).Width = width + 3; popup.MaxWidth = width + 3; } } /// <summary> /// Helper method used to find the first grid row in the visual tree. /// </summary> /// <param name="root"></param> /// <returns></returns> private DependencyObject FindFirstRow( DependencyObject root ) { for( int i = 0; i < VisualTreeHelper.GetChildrenCount( root ); i++ ) { DependencyObject dObj = VisualTreeHelper.GetChild( root, i ); if( dObj.GetType() == typeof( ComboCellsPanel ) ) { return dObj; } dObj = FindFirstRow( dObj ); if( dObj != null ) { return dObj; } } return null; }
Any thoughts?
Thanks,
I see what your seeing now. I've added an image column and I'm seeing the same behavior. This is due to cell virtualization that I had not anticipated for in my previous sample. It looks like the reason my sample worked initially was because the two columns were still visible even at the minimum width which means they are loaded into memory before the Popup_Opened event is fired. Once I add a third column the children count for itemsPanel is still showing 2 because the 3rd column is not initially visible and a visual element has not been created for it.
To get around this you can handle the XamMultiColumnComboEditor.DropDownOpened event and inside here, set the dropdown width to a very large value. This will force the remaining columns to be created which will then lead into Popup_Opened where you can set the width to the calculated width. DropDownOpened will fire first, then Popup_Opened will fire afterwards.
Rob, what you did was really very good, you got deep into handling low level objects and gave me visibility into the control I did not have. I'll try this and let you know how it goes.
Thanks for jumping on this.
Let me know if you have any further questions on this matter.
Sounds good. Let me know how it goes.
Rob,
Thanks for your help on these final details. I'll look at this later today and get back with you.
You have been very helpful to me.
The row that has a height of 21.96 is the header row. I was under the impression from my own tests though that the header panel was always first in the MultiColumnComboItemsPanel container but in your case you're saying that isn't always the case.
To remedy this you will need to modify the code a bit to perform a search for the header row and then search for the first non-header row. You can then multiply the height of the non-header row by the number of rows and add the header row height to the result. Something like this:
ComboCellsPanel headerPanel = null; ComboCellsPanel actualRowPanel = null; foreach (ComboCellsPanel panel in itemsPanel.Children) { if (panel.Row is ComboHeaderRow) headerPanel = panel; else if (actualRowPanel == null) actualRowPanel = panel; } double totalHeight = actualRowPanel.ActualHeight * numRows + headerPanel.ActualHeight;
I saw how you changed your code, and what you did was very interesting. thank you for your help.
I have an additional question here that is vexing me. In the line where you are getting the actual row panel's actual height, sometimes the value is 66.0 pixels which is correct. However sometimes the value is 21.96 which is not correct and I can't understand why that would be since every row has a picture in it and every row should be 66.0. Do you have any thoughts about it? I have 8 combo boxes for different phases of diagnosis and they all come from the same object list. For now I am hard coding this value, but I don't understand why sometimes the first row is 66.0 and then at other times it is 21.96.