Use scroll bars on child controls in wpf - c#

I have an explorer type window that I am trying to display in WPF.
<Grid ScrollViewer.VerticalScrollBarVisibility="Disabled">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<telerik:RadTreeView Grid.Column="0" Name="ExplorerTree"
ScrollViewer.VerticalScrollBarVisibility="Visible"/>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="1" Grid.RowSpan="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
<telerik:RadListBox Grid.Column="2" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible" />
</Grid>
However, when I expand an object within the tree view, the scroll bar appears on the grid causing the entire window to scroll rather than just the contents of the tree view. What do I need to change to make the contents of the tree view scroll instead? I don't want to set it to a specific height as I would like the height of the tree view to adjust with the height of the parent in which it is displayed.

If you just stop trying to set ScrollViewer properties altogether you should get the desired result as both RadTreeView and RadListBox will display a ScrollViewer automatically as needed (unless you have something else in the rest of your XAML that interferes with their normal behavior). I used both these controls extensively without setting any ScrollViewer properties in XAML and that's what they do automatically although you might have to set their VerticalAlignment to stretch... not sure about that.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<telerik:RadTreeView Grid.Column="0" Name="ExplorerTree"
VerticalAlignment="Stretch"/>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="1" Grid.RowSpan="1"
HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
<telerik:RadListBox Grid.Column="2" Grid.Row="1" VerticalAlignment="Stretch" />
</Grid>

Related

Align controls correctly when using ItemsControl in WPF

Can someone tell me how to align and resize controls correctly when using the ItemsControl.
I want to have a description on the left and a TextBox on the right for multiple fields which are defined in an ObservableCollection to end up with something like:
First Name: [FirstNameTextBox]
Last Name: [LastNameTextBox]
Date of Birth: [DobTextBox]
but instead I'm getting this:
First Name: [FirstNameTextBox]
Last Name: [LastNameTextBox]
Date of Birth: [DobTextBox]
I want all the textbox to be aligned based on the largest <TextBlock>. If this was done directly in a <Grid> control, it would be straight forward as all controls are directly in the grid and you would just have the following columns definition defined
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
I thought I could use the SharedSizeGroup property in the <Grid> but it still doesn't resize correctly. Instead it only displays the <TextBlock> stretch across the <Grid>.
Here's my code:
<Grid Grid.IsSharedSizeScope="True" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Labels" />
<ColumnDefinition Width="*" SharedSizeGroup="InputControls" />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Row="1" ItemsSource="{Binding SelectedTemplate.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels"/>
<ColumnDefinition SharedSizeGroup="InputControls"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Label}" Grid.Column="0" Margin="5"
VerticalAlignment="Center" Background="Red" />
<TextBox Text="{Binding Path=Value}" Grid.Column="1" Margin="5"
VerticalAlignment="Center" Background="Blue" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Any idea how I can resolve this?
Thanks.
UPDATE1: I cannot get this to work as I need it to. This is what I've got so far:
<Grid Grid.Row="1" Background="Purple">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="Overall" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels" Width="Auto" />
<ColumnDefinition SharedSizeGroup="InputControls" Width="*" />
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding SelectedTemplate.Fields}"
Background="Yellow"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="Green">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels"/>
<ColumnDefinition SharedSizeGroup="InputControls"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Label}"
Grid.Column="0"
Margin="5"
VerticalAlignment="Center"/>
<TextBox Text="{Binding Path=Name}"
Grid.Column="1"
Margin="5"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
This ends up displaying my layout this way:
As you can see, my TextBox are correctly aligned based on the largest TextBlock but my ItemsControls is not stretched all the way across. I guess that makes sense as it is within the same grid where the ColumnDefinitions are defined.
Now, if I move the ColumnDefinitions' out this grid to the outer grid and remove all instances ofGrid.IsSharedSizeScope`, I guess the following:
Which once again is closer to what I need as my ItemsControl is now stretching all the way as I've set its Grid.ColumnSpan="2" and my TextBox are still aligned to the TextBlock and are stretching all the way across but the problem now is that the TextBlock should be smaller as the Column is set to Auto but they appear to behave as if the column was set to * and I guess I'm losing the purpose of using IsSharedSizeScope since it has been removed.
Now if I add IsSharedSizeScope="True" to the outer grid, I get the following result:
Again, this is close to what I want as my ItemsControl is stretched, my textboxes are also stretch but they are no longer aligned to the largest TextBlock.
Finally, if I add Grid.IsSharedSizeScope="True" to ItemsControl as originally suggested by #mm8,
<Grid Grid.Row="1" Background="Purple" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels" Width="Auto" />
<ColumnDefinition SharedSizeGroup="InputControls" Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.ColumnSpan="2" >
<ItemsControl ItemsSource="{Binding SelectedTemplate.Fields}"
Background="Yellow"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="Green">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels"/>
<ColumnDefinition SharedSizeGroup="InputControls"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Label}"
Grid.Column="0"
Margin="5"
VerticalAlignment="Center"/>
<TextBox Text="{Binding Path=Name}"
Grid.Column="1"
Margin="5"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<!--<TextBlock Text="Invoice Number" Grid.Column="0" Margin="5" VerticalAlignment="Center"/>
<TextBox Text="InvoiceNumber" Grid.Column="1" Margin="5" VerticalAlignment="Center"/>-->
</Grid>
I get the following:
Which brings me back to square one, though the definitions are different?
I need to achieve the following:
What am I doing wrong??
Thanks.
Try to set the Grid.IsSharedSizeScope property of the ItemsControl itself:
<ItemsControl Grid.Row="1" ItemsSource="{Binding SelectedBarcodeTemplate.Fields}"
Grid.IsSharedSizeScope="True">
Synchronizing the width of elements in an ItemsControl: https://joshsmithonwpf.wordpress.com/2008/09/06/synchronizing-the-width-of-elements-in-an-itemscontrol/
I eventually found the answer in the following article: WPF Tutorial - Grid Panel under the section: "How to share the width of a column over multiple grids".
As per article:
Columns and rows that participate in size-sharing do not respect Star sizing. In the size-sharing scenario, Star sizing is treated as Auto. Since TextWrapping on TextBlocks within an SharedSize column does not work you can exclude your last column from the shared size. This often helps to resolve the problem.
So my final XAML looks like this:
<Grid Grid.Row="1" Background="Purple" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels" Width="Auto" />
<ColumnDefinition SharedSizeGroup="InputControls" Width="*" />
</Grid.ColumnDefinitions>
</Grid>
<ItemsControl ItemsSource="{Binding SelectedBarcodeTemplate.Fields}"
Background="Yellow"
Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Background="Green" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Labels"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Label}"
Grid.Column="0"
Margin="5"
VerticalAlignment="Center"/>
<TextBox Text="{Binding Path=Name}"
Grid.Column="1"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
And the outcome now finally looks correct:
Hope this helps others!

What kind of dynamic container and internal containers should I use for this intended view?

I want to make a popup which has a person's details on it. Each detail will be stacked vertically in the popup. I have two questions.
(1) How should I deal (graphically) with details which are not available?
(2) How to make the container around all the details dynamic so that its height is determined by the number of details available.
My first thought was the following;
<StackPanel Width="400"
Height="500">
<StackPanel x:Name="sp">
<Grid x:Name="spTelephone" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="1"
Margin="5,0,0,0">
<TextBlock Text="+Some Phone No."
VerticalAlignment="Center"
FontFamily="Verdana"/>
</Grid>
<Grid Grid.Column="2">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</Grid>
<Grid x:Name="spMobile" Height="50">
<!-- Repeat of above -->
</Grid>
<Grid x:Name="spEmail" Height="50">
<!-- Repeat of above -->
</Grid>
<!-- Further Grids -->
</StackPanel>
</StackPanel>
The idea being that if a detail is not available then I would set the Visibility property of the GRID to Visibility.Collapsed.
For example see my image with 3 details.
Then if a cell phone is not available it would look like this.
So how should I do this? I could for imagine using a ListView as well maybe as this would then take away the need to collapse the views. I could add each detail to an Item. But then how do I get the list view and its parent to resize its height?
Define datatemplate for item and use any items control to represent them.
Simple solution would be something like this:
Template
<DataTemplate x:Key="MyItemTemplate">
<Grid x:Name="spTelephone" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="1"
Margin="5,0,0,0">
<TextBlock Text="+Some Phone No."
VerticalAlignment="Center"
FontFamily="Verdana"/>
</Grid>
<Grid Grid.Column="2">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<Ellipse Fill="Blue"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</Grid>
</DataTemplate>
Control
<Border BorderBrush="Black" BorderThickness="2" VerticalAlignment="Center" HorizontalAlignment="Center">
<ItemsControl ItemsSource="{Binding ItemsCollection}"
ItemTemplate="{StaticResource MyItemTemplate}"/>
</Border>
Of course you must fill collection from dataContext and for Text="+Some Phone No." also use data binding from current collection item.(Use DataContext={Binding} in template)
Border here is just to show that ItemsControl size changes according to collection-items count. Also ItemsControl can be replaced with any of it's descendants.

How to dock an element to another while respecting parent container size?

I want to layout two labels in a fixed width column. One left-aligned and growing to the right, the other directly to the right of it.
When the combined width of the labels is smaller than the available space there should be empty space to the right. This is achieved with this markup:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock TextTrimming="CharacterEllipsis" Text="Sample Name" />
<Label Content="Text" Foreground="#AAAAAA" Grid.Column="1"/>
</Grid>
When the first label is too large, the text is trimmed and the label at the right should not be pushed out of the column. This is achieved with this markup:
<DockPanel LastChildFill="True">
<Label Content="Text" Foreground="#AAAAAA" DockPanel.Dock="Right" />
<TextBlock TextTrimming="CharacterEllipsis" Text="A Really Really Long Sample Name" />
</DockPanel>
How can I get the same results (the screenshots) without different pieces of Xaml?
Turns out it was something that I thought I had tried. This did it for me:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock TextTrimming="CharacterEllipsis" Text="Sample Name" />
<Label Content="Text" Foreground="#AAAAAA" Grid.Column="1" />
</Grid>

SizeToContent Fills Screen with RichTextBox and FlowDocumentScrollViewer

I'm trying to show a FlowDocument in a WPF form and have tried both RichTextBox and FlowDocumentScrollViewer. I also require that the window resizes to be able to show all text.
Unfortunately, when I set SizeToContent="WidthAndHeight" for the Window itself, no matter what content I put in the FlowDocument, the window expands to the full width of all my displays! The height seems to resize fine, however.
Anyone know how to get it to resize properly? Looked all over and cannot figure out how to get this going...
XAML below:
<Window x:Class="CustomControls.SecureConfirmationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecureConfirmationDialog"
MinHeight="120" MinWidth="200"
Height="120" Width="300"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStyle="ToolWindow"
Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FlowDocumentScrollViewer Name="flowMsg" Grid.Row="0" Grid.ColumnSpan="3" Margin="3" IsToolBarVisible="False" ScrollViewer.VerticalScrollBarVisibility="Hidden" />
<TextBox Name="txtConfirm" Grid.Row="1" Grid.Column="0" Text="Testing" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="3" />
<Button Name="btnOK" Grid.Row="1" Grid.Column="1" Content="OK" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3" Width="50" Click="btnOK_Click" />
<Button Name="btnCancel" Grid.Row="1" Grid.Column="2" Content="Cancel" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3" Width="50" Click="btnCancel_Click" />
</Grid>
</Window>
SizeToContent only "works" if the content is actually bounded, in this case however the Grid, which is the content of Window, has no size restrictions so it will try to get all the space it can get, the window responds by giving it as much space as fits the screen.
If you want to prevent this you would need to make the container for your document to size to their content which might be impossible if the document does not have any bounds itself and also behaves in a give-me-all-you-have manner.

How to make WPF Grid expandable?

Please, let's focus on the horizontal size (width).
I have horizontal StackPanel which auto-resizes to occupy entires space (it "expands"). Within it I have Grid (with 3 columns) and a scrollbar. The scrollbar width should be fixed, but Grid should expand. How can I force it to do it?
Current code:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<Grid Name="grid1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" HorizontalAlignment="Stretch">
<Label Content="Label" HorizontalAlignment="Center" Name="label1" VerticalAlignment="Bottom" />
</StackPanel>
<GridSplitter DragCompleted="OnDragCompleted" ShowsPreview="True" Name="gridsplitter1" Background="Red" Grid.Column="1" Grid.Row="0" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<StackPanel Orientation="Vertical" Grid.Column="2" HorizontalAlignment="Stretch">
<Label Content="Label" HorizontalAlignment="Center" Name="label2" VerticalAlignment="Bottom" />
</StackPanel>
</Grid>
<ScrollBar Name="scrollBarV" Orientation="Vertical" />
</StackPanel>
No matter what I do, width=auto, horizontalalignment=strech, every time each column of the grid occupies only the required space (sufficient for showing its content), not entire space available.
A horizontal StackPanel will always give its children their desired width, so it will never force the Grid to be larger than it wants to be. You will need to use a different kind of panel. One option is to use a DockPanel, and dock the ScrollBar to the right and let the Grid fill in the rest:
<DockPanel HorizontalAlignment="Stretch">
<ScrollBar DockPanel.Dock="Right" Name="scrollBarV" Orientation="Vertical" />
<Grid Name="grid1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
...
</Grid>
</DockPanel>
Another option is to use a Grid with the second column using Auto to size to the ScrollBar exactly and the first column left at the default of 1* to use the rest of the space:
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid Name="grid1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
...
</Grid>
<ScrollBar Grid.Column="1" Name="scrollBarV" Orientation="Vertical" />
</Grid>
Try
<ColumnDefinition Width="*"></ColumnDefinition>
Columns and rows that are defined within a Grid can take advantage of Star sizing to distribute remaining space proportionally. When Star is selected as the height or width of a row or column, that column or row receives a weighted proportion of the remaining available space. This is in contrast to Auto, which distributes space evenly based on the size of the content that is within a column or row. This value is expressed as * or 2* when you use Extensible Application Markup Language (XAML)
Source

Categories

Resources