I am attempting to write a simple WPF learning project which creates a set of buttons inside a resizeable main window. There is to be one Button
per entry in a collection, and the contents of that collection may vary during run-time. I want the buttons to fill the entire window (e.g. 1 button @ 100% width, 2 buttons @ 50% width, 3 buttons @ 33% width, etc. all at 100% height). A simplified version of what I've written so far is:
<ItemsControl x:Name="itemscontrolButtons" ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <Button Tag="{Binding}"> <TextBlock Text="{Binding}" /> </Button> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> ... List<string> listButtonTexts = new List<string>() { "Button1", "Button2" }; ... itemscontrolButtons.DataContext = listButtonTexts;
This results in this:
I have been unable to make the buttons stretch to fit the width and my attempts to use a Grid
instead of StackPanel
were fruitless.
Then, as an optional improvement, I would appreciate suggestions on how to adjust it such that if there are so many buttons that they cannot fit properly on a line or are narrower than a threshold, it will wrap onto a new line (thereby halving the button heights if going from 1 to 2 rows).
I'd like to emphasize that I'd like to know how to do this the WPF way. I realize I can consume window resize messages and resize the controls explicitly, and that's how I'd have done it with MFC or WinForms, but from what I've read that is not how such things should be done with WPF.
Replace the item control's panel with a UniformGrid, this will size all children so everything fits exactly:
<ItemsControl> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Rows="1"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl>
I was able to build on Nir's answer to fulfill my optional criterion allowing for WPF-managed stretching with multiple rows.
First you must be able to alter the properties of the template UniformGrid. To do that you need to store a reference to it. There are multiple methods for accomplishing this. I chose to handle the Loaded event for the UniformGrid and store a reference at that time.
<ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Rows="1" Loaded="UniformGrid_Loaded" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl>
And in the code behind:
UniformGrid uniformgridButtons; private void UniformGrid_Loaded(object sender, RoutedEventArgs e) { uniformgridButtons = sender as UniformGrid; }
Then, handle the SizeChanged event and adjust the rows parameter according to whatever criteria you wish.
private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { if ((uniformgridButtons != null) && (this.Width > 0)) { // Set row count according to your requirements uniformgridButtons.Rows = (int)(1 + ((this.MinWidth * iButtonCount) / this.Width)); } }
So this allows WPF to manage the stretching like in Nir's answer, but allows you to explicitly adjust the number of rows. The above code gives this result:
(Animated sample here)
Update 2009/08/28:
I was adjusting my learning application so that the ItemsControl
was in a DataTemplate
in a separate ResourceDictionary
. However, events such as Loaded
cannot be handled in an external ResourceDictionary
XAML file because it has no code-behind (usually). Therefore, I needed to cache the reference to the UniformGrid
using a different technique.
I instead used Alan Haigh's FindItemsPanel
method (10th reply). First, I replaced the ItemsControl
with :
<ContentPresenter x:Name="contentpresenterButtons" Content="{Binding obscolButtons}" />
Then in my ResourceDictionary
, I put the ItemsControl
in the DataTemplate
:
<DataTemplate DataType="{x:Type local:Buttons}"> <ItemsControl ... </DataTemplate>
Finally, I stored the reference to the templated UniformGrid
as so:
private void Window_Loaded(object sender, RoutedEventArgs e) { uniformgridButtons = FindItemsPanel<UniformGrid>(contentpresenterButtons); // Also call the code in Window_SizeChanged which I put in another method }
This works just the same as my previous solution but without requiring an event handler in the ResourceDictionary
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With