wpf layout with datagrid and expander - wpf

Im having trouble getting my xaml to work the way i want to.
What i want to is this.
My ItemsControl should have a height of 70. The rest of the space should be filled with a DockPanel.
Inside the DockPanel i have a GridControl (DataGrid from DevExpress) that are supposed to fill all empty space. The gridcontrol is docked to top. Below the gridcontrol but still insdie the dockpanel, i have a Expander that have a maxheight of 480, this should be docked to the bottom.
The problem im having is that i can't get the gridcontrol to dynamically change it's height so that it fills all remaining space between the top of the docking panel down to the Expander.
What am i doing wrong?
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="70" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<!--#region ItemControl-->
<ItemsControl
Grid.Row="0"
Grid.ColumnSpan="9"
HorizontalAlignment="Stretch"
IsTabStop="False"
ItemsSource="{Binding Path=ToolbarControlViewModel.CommandButtonList}"
Style="{DynamicResource ActionBarPanel}">
<ItemsControl.ItemsPanel>
...
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--#endregion-->
<DockPanel Grid.Row="1">
<!--#region Listview-->
<dxg:GridControl
x:Name="CMGrid"
DockPanel.Dock="Top"
Margin="0,0,0,10"
ItemsSource="{Binding Path=CustomerManagementListViewModel.Customers}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<dxg:GridControl.Columns>
...
</dxg:GridControl.Columns>
<dxg:GridControl.View>
<dxg:TableView
x:Name="CustomerListView"
AllowBestFit="True"
FocusedRow="{Binding Path=CustomerManagementListViewModel.SelectedCustomer, Mode=TwoWay}"
IsColumnMenuEnabled="False"
NavigationStyle="Row"
ShowGroupPanel="False"
AutoWidth="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
<!--#endregion-->
<!--#region Controls-->
<Expander
MaxHeight="480"
VerticalContentAlignment="Stretch"
DockPanel.Dock="Bottom"
Header="{T:Translate Equipment,
Format=' {0}:'}"
IsExpanded="True"
Style="{StaticResource K2M_Epander_Base}" VerticalAlignment="Bottom">
<Expander.Content>
<Grid>
...
</Grid>
</Expander.Content>
</Expander>
<!--#endregion-->
</DockPanel>

I feel bad getting points for such a quickie but if the question's going to remain I guess we may as well give some closure for future finders.
So, DockPanel has its uses but in this scenario it's your culprit. As a DockPanel nested in a layout as such is basically the equivalent of attaching the VerticalAlignment="Top" property to your GridControl. Which interprets as it only consuming the space necessary for it from the top down of the parent row.
By removing the DockPanel entirely (since in this scenario it's redundant) and applying the Row declaration directly to the GridControl it has the ability to act as a normal child to the parent Grid container and will consume the space provided by the Row's * declaration.
Glad you got it sorted, got to love the quick 'n easy ones. Cheers :)

Related

Sharing column width between grids defined in a template

I am trying to layout a couple of controls:
The light gray vertical lines are grid splitters. Each line in this table is a grid with three columns, the label on the left, the splitter in the middle and the control on the right, including the text boxes and the image.
I want all columns to be aligned. Here is the data template I use for each line:
<DataTemplate x:Key="PropertyLineTemplate">
<Grid Margin="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Name"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Value"/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}" />
<GridSplitter Grid.Column="1" Width="2"/>
<SomeUserControl
Grid.Column="2"
Content="{Binding}"
Margin="4, 0, 0, 0"/>
</Grid>
</DataTemplate>
and the template for the view itself:
<DataTemplate DataType="{x:Type vm:PropertiesViewModel}">
<DockPanel>
<!-- Properties -->
<ScrollViewer>
<StackPanel Margin="10">
<ItemsControl ItemsSource="{Binding Properties}"
ItemTemplate="{StaticResource PropertyLineTemplate}" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</DataTemplate>
and the usage of both:
<ContentControl Content="{Binding SelectedItem}" Grid.IsSharedSizeScope="True">
</ContentControl>
where SelectedItem is of type PropertiesViewModel.
I also tried to place Grid.IsSharedSizeScope="True" in each individual grid directly and on the panels containing the control, without effects. How can I achieve that all columns share the width?
Grid.IsSharedSizeScope should be set on the first parent ItemsControl of a Grid, in your case it would be the ItemsControl in the ViewModel template. Then the columns width will be adjusted.
However, if you move one GridSplitter, then it will only adjust that row, not the others (See WPF SharedSizeGroup GridSplitter Issue for a potential solution, if you need resizing.)
Other stuff: you don't need the Stackpanel wrapper around the ItemsControl, since the ItemsControl is the only child. If you need to adjust the panel the ItemsControl uses, then set it on the Property ItemsPanel, like this:
<ItemsControl ItemsSource="{Binding Properties}"
Grid.IsSharedSizeScope="True"
ItemTemplate="{StaticResource PropertyLineTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/> <!-- Or <WrapPanel/> or <UniformGrid/> etc. -->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
However, the Stackpanel is the default panel of the ItemsControl.
Based on Nautious' answer I've solved the problem by defining two ColumnDefinitions as resources and binding the Width property of all columns in question to the Width property of these 'master column definitions':
<ColumnDefinition x:Key="MasterColumnDefinition" />
<ColumnDefinition x:Key="MasterColumnDefinition2" />
<DataTemplate x:Key="PropertyLineTemplate">
<Grid Margin="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Source={StaticResource MasterColumnDefinition}, Path=Width, Mode=TwoWay}"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="{Binding Source={StaticResource MasterColumnDefinition2}, Path=Width, Mode=TwoWay}"/>
</Grid.ColumnDefinitions>
...
</Grid>
</DataTemplate>
Might be a little hacky, but it's simple and works. Not, however, that the shared size scope of this solution is the scope in which the resources are defined. I.e. placing these template in the app's resources will lead to all columns in the application being resized synchronously.

Stretching the items of an ItemsControl

I'm bulding a small WPF app over here. It's all built strictly with MVVM, using nothing but DataTemplates linked to view model types.
I've seen alot of questions about how to stretch and clip the contents of ListBoxes so that the items fill its parent. After alot of experimenting I managed to get my head around that but now I find myself in the same scenario with the ItemsControl but the same tricks doesn't seem to work.
Here's one of the DataTemplates in use (a simple TextBox). Note how I tried setting the HorizontalAlignment ...
<DataTemplate DataType="{x:Type vm:OneOfMyViewModelTypes}">
<TextBox
Text="{Binding Path=Value}"
HorizontalAlignment="Stretch"
/>
</DataTemplate>
Here's the ItemsControl inside a Grid ...
<Grid Background="Gray">
<Grid.Margin>
<Thickness
Left="{StaticResource ConfigurationDefaultMargin}"
Right="{StaticResource ConfigurationDefaultMargin}"
Bottom="{StaticResource ConfigurationDefaultMargin}"
Top="{StaticResource ConfigurationDefaultMargin}"
/>
</Grid.Margin>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="_key" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="_value" Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl
Background="DimGray"
Grid.IsSharedSizeScope="True"
ItemsSource="{Binding Path=Configuration, Mode=OneWay}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="_key"/>
<ColumnDefinition SharedSizeGroup="_value"/>
</Grid.ColumnDefinitions>
<TextBlock
Style="{StaticResource ExtensionConfigurationLabel}"
Grid.Column="0"
Margin="5,5,5,0"
Text="{Binding Path=Caption}"
/>
<ContentPresenter
Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="5,5,5,0"
Content="{Binding}"
/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I've used colors to see how the controls sizes. The Grid is gray and the ItemsControl is dark gray.
This is the result ...
As you can see from the colors the containing Grid stretches while the ItemsControl does not. I did set its HorizontalAlignment property to Stretch but it seems it has no effect. Is there anything else I need do?
Thanks
You have two columns in your main (outer) grid, yet you use only the first column.
The second column uses all the remaining space.

Embedding an ItemsControl within a ScrollViewer

There are several questions related to ScrollViewer that I've researched, but none of them have worked for me. This almost certainly comes down to my lack of understanding regarding the size of an element. First, my XAML (other than the UserControl definition, this is the entire control):
<DockPanel Width="Auto">
<ScrollViewer DockPanel.Dock="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible" CanContentScroll="True" BorderBrush="{DynamicResource PanelBorder}" BorderThickness="1,1,1,1" Background="{DynamicResource LightGradientBackgroundBrush}">
<ItemsControl Focusable="false" Width="Auto" MinHeight="30" ItemsSource="{Binding RequiredFields}" OverridesDefaultStyle="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="3,0,3,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Path=Header, Mode=OneTime}" Foreground="{DynamicResource SilverBorderColorBrush}" Grid.Column="0"/>
<TextBox Text="{Binding Path=Value}" HorizontalAlignment="Stretch" Foreground="{DynamicResource SilverBorderColorBrush}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
This control is loaded within a tab as the content. The window that hosts the tab is resizable, and the tab content minimum size is 60. What I'm attempting to do is have the scrollviewer fill the tab, but not push the tab past the bounds of the window. With the above XAML, excess content results in the tab height expanding, and a scrollbar appearing on the Window, not the Tab.
The scrollviewer is expanding to the size of its children, therefore pushing the tab height out. What I want, is the scrollviewer to be fixed to the size of the tab, and have the content within the scrollviewer grow within the bounds of the scrollviewer, so that a scrollbar is presented when there is too much content.
Where am I going wrong?
Edit:
The problem is the Height, not the Width. The width is behaving as it should. I've changed the wrapper to a grid as suggested, but unless I set the Height property, the grid expands to fit the contents of the expanded scrollviewer, causing the same issue as before.
Updated XAML:
<Grid MinHeight="60" VerticalAlignment="Top">
<Border BorderBrush="{DynamicResource PanelBorder}" BorderThickness="1,1,1,1">
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Visible"
CanContentScroll="True"
BorderBrush="{DynamicResource PanelBorder}"
BorderThickness="1,1,1,1"
Background="{DynamicResource LightGradientBackgroundBrush}"
>
<ItemsControl Focusable="false" Width="Auto" MinHeight="30" ItemsSource="{Binding RequiredFields}" OverridesDefaultStyle="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="3,0,3,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Path=Header, Mode=OneTime}" Foreground="{DynamicResource SilverBorderColorBrush}" Grid.Column="0"/>
<TextBox Text="{Binding Path=Value}" HorizontalAlignment="Stretch" Foreground="{DynamicResource SilverBorderColorBrush}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
</Border>
</Grid>
None of the answers below have helped to make this work, but I'm fairly confident that the answers would work in the majority of cases.
My control is hosted within a tab page. It is dynamically loaded in, and I have no control over how the tab page is designed. My gut feeling is that the tab control allows as much space as the content within it. Because the grid in my control is saying use up all the space available, and the tab page offers as much space as the child container needs, and the itemscontrol is trying to use as much space as the grid offers; it culminates in the tab page expanding to contain the entire ItemsControl content.
If the tab page was created to not expand to fit its content, the solutions below would work. Unfortunately, I don't believe this to be the case.
One whole day wasted on this. Time to move on.
Ah yes the intricacies of WPF layout is a pain sometimes. What's killing you is the combination of the DockPanel and the Width="Auto" on your ItemsControl. The DockPanel tells its children that they can have as much space as they want for layout. Your ItemsControl then tells its children the same thing. In the end, the Window creates a ScrollViewer to handle the oversized content.
Replace the outer DockPanel with a grid and all should be well.
To have scrollviewer not to strength, you need to use Grid container
<Grid>
<ScrollViewer DockPanel.Dock="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible" CanContentScroll="True" BorderBrush="{DynamicResource PanelBorder}" BorderThickness="1,1,1,1" Background="{DynamicResource LightGradientBackgroundBrush}">
...
</ScrollViewer>
</Grid>
DockPanel and StackPanel always stretch to fit it's children
Also look at this note (i've copied from here)
Panel X Dimension Y Dimension
Canvas No No
Dock Yes Yes
StackPanel
(Vertical) Yes No
StackPanel
(Horizontal) No Yes
Grid Yes* Yes*
WrapPanel No No
Except when using "Auto" rows and columns
Yes in the above table means "Children are stretched to available size"
No in the above table means "Children are their desired size"

Silverlight control not stopping at container bottom

I have a grid that hosts a stackpanel that hosts a listbox. The Listbox once filled from the itemssource is stretching out of the visible area of the stackpanel. I have tried limiting the grid and stackpanel in size and the listbox continues to stretch out of the visible range (it just goes to edge and continues as if nothing was there to stop it instead of limiting it's size and bringing up a vertical scrollbar). The only thing that seems to help is setting the MaxHeight on the listbox, the issue is I can't tell what that height should be for different clients. I have tried different VerticalAlignments and I have the bottom margin set to 5 to try and get it to stop at the edge but nothing has had an effect yet.
Snip: Edit to add full hierarchy:
<UserControl>
<Grid x:Name="LayoutRoot" Background="White" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="230"/>
<ColumnDefinition d:DesignWidth="500"/>
<ColumnDefinition MinWidth="300" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="ContentHolder" Grid.Column="0" DataContext="{Binding}" VerticalAlignment="Top">
<ListBox Name="lst" ItemsSource="{Binding}" Margin="5,0,15,5" VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Number}" Margin="15,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</grid>
</usercontrol>
I have removed most of the names and such from the code to just use this as an example.
Answering this for anyone confused and looking for an answer in the future. Basically stackpanels step to a vertical orientation do not limit the size of their child controls vertically so anything with a scroll bar (datagrid, listbox, etc) probably shouldn't be used with them.

wpf border control to span the width of listboxItem

Im trying to define a dataTemplate for a business object in my wpf application a collection of which is being bound to a ListBox.
<UserControl.Resources>
<DataTemplate x:Key="ResizedItemsDataTemplate" DataType="{x:Type resizer:ResizeMonitorItem}">
<Border x:Name="bdr" BorderBrush="Blue"
BorderThickness="1"
CornerRadius="2"
Width="auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="14"></RowDefinition>
<RowDefinition Height="14"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding SaveAsFileName}"></TextBlock>
<TextBlock Grid.Row="1" Text="{Binding ResizedImageFilePath}"></TextBlock>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0">
<Border BorderThickness="0,0,0,5" BorderBrush="DarkGray" >
<ListBox x:Name="ListBoxResizeItems" ItemsSource="{Binding Path=ResizeItems}" BorderThickness="0" ItemTemplate="{DynamicResource ResizedItemsDataTemplate}">
</ListBox>
</Border>
</Grid>
How can I get the border defined with x:Name=bdr to span the full width of each listbox item? At the moment it only spans the with of the textblocks inside it which dont neccessary fill the full width of the listboxitem and also vary for each listboxitem.
This is probably more to do with the ListBoxItems themselves not taking up the full width of the ListBox. Add the HorizontalContentAlignment="Stretch" attribute to your ListBox and see if it stretches the individual items to fill the width.
Worked it out. The trick is to set the HorizontalContentAlignment="Stretch" on your listbox to make its contents stretch the full width rather than fit the contents only.
<ListBox x:Name="ListBoxResizeItems"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Path=ResizeItems}"
BorderThickness="0"
ItemTemplate="{DynamicResource ResizedItemsDataTemplate}" >
</ListBox>
Sorry Matt, just got your answer thorugh as I was typing this post.
HorizontalContentAlignment is a nice, clean solution compared to what I was trying. Thanks!
Here's what ALMOST worked, but sometimes made a dialog box animated itself wider and wider forever:
Width="{Binding ActualWidth,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"

Resources