WPF: Using Grids - wpf

I created a grid and inside this grid it contains a TextBlock. When I maximize or adjust the size of the window the content of the TextBlock doesn't stay in the center of the Grid.
Tried to keep this as short as possible. :>
<Grid>
<Grid HorizontalAlignment="Left" Height="46" VerticalAlignment="Top" Width="515">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>

Grids, by default, auto expand. That is, they take up as much area as they are allowed. Your outer grid will take up the entire client area of the window while the inner grid will stick to the top left of the outer grid, just as you have coded it to. If you want the inner grid to be centered, then do something like
<Grid>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Height="46" Width="515">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>
You don't even have to include the horizontal and vertical alignment as grids will automatically centre.
If you just want to centre the TextBlock ...
<Grid>
<Grid>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Height="46" Width="515">Welcome! Use the functionalities below.</TextBlock>
</Grid>
</Grid>
The text can be centred using the TextAlignment property.
I hope this helps

Related

Horizontal scrolling a row of ViewBox images in Xaml

I'm working on a UWP Windows 10 app with a XAML UI. One of my pages requires that images fill the height of the window (or screen in tablet mode) and uniformly scale as one long row of images from left to right (going off-screen). I've got this set up perfectly using ViewBoxes for the images inside of a StackPanel set to a Horizontal Orientation like so:
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
The intention is for the images to flow off-screen with a horizontal scroll that allows the user to pan from left to right to see the gallery of images as one long row.
I've tried enabling HorizontalScrollMode on the parent StackPanel like so:
<StackPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollMode="Enabled">
But this did not enable any scrolling at all.
I also tried to wrap everything inside of a ScrollViewer like so:
<ScrollViewer HorizontalScrollMode="Enabled">
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
</ScrollViewer>
But this completly breaks my ViewBox layout by shrinking all the images so they fit within a small portion of the screen and no longer fill the window/tablet height.
I've tried a number of other variations with similar results. Does anyone have some suggestions for solving this? Let me know if you need more info.
You do need a ScrollViewer to enable scrolling, although you might need to set a few properties to make it only scroll horizontally as mentioned in Windows 8 ListView with horizontal item flow
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.ZoomMode="Disabled"
Now the Viewbox is not the most controllable... control. You could try using the
SquareGrid panel from my toolkit instead of the Viewboxes. Maybe simplify it a bit. If that isn't enough - you could add some bindable properties that would update when the size of your window changes and bind the Width and Height of these images to these properties. Note that you can't use ActualWidth or ActualHeight because these don't raise change notifications on size changes.
<GridView x:Name="ImageGridView"
SelectedItem="{x:Bind ViewModel.SelectedLocation, Mode=TwoWay}"
Margin="10,0"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.HorizontalScrollMode="Auto"
Grid.Row="4" Grid.ColumnSpan="5"
ItemsSource="{x:Bind ViewModel.CheckedLocations}"
ItemContainerStyle="{StaticResource PinsGridViewItemStyle}"
ItemTemplate="{StaticResource ImageOverlayGalleryFolderDataTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid MaximumRowsOrColumns="1"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
This is my code to show 1 row of photos. You can adjust by the MaxiumRowsOrColumns. Also note that both HorizontalScrollBarVisibility and HorizontalScrollMode are present to custom as you want to.

Having issues with the scroll bar in WPF

So im trying to get my scroll bar to A only show up as needed and B show up only around my description text
Right now the scroll view is going from the top of the window to the bottom
<Window x:Class="WpfApplication3.DataWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataWindow" Height="300" Width="300">
<Grid>
<Label x:Name="lblTitle" Content="Label" HorizontalAlignment="Left" Margin="96,25,0,0" VerticalAlignment="Top" Width="186"/>
<Label x:Name="lblPublishDate" Content="Label" HorizontalAlignment="Left" Margin="96,53,0,0" VerticalAlignment="Top" Width="186"/>
<Image x:Name="imgPic" HorizontalAlignment="Left" Height="81" Margin="10,10,0,0" VerticalAlignment="Top" Width="81"/>
<ScrollViewer>
<TextBlock x:Name="tbDesc" HorizontalAlignment="Left" Margin="10,96,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="167" Width="272" Text="TextBlock" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</ScrollViewer>
</Grid>
A grid tries to let it's children take up all availble space.
Your ScrollViewer is one of the children, so it will fill all available space by default.
There are a number of ways around this.
You could use a different panel type, one that doesn't try to stretch it's children to fill all available space. Based on what you're doing with excessively large margins, a Canvas might be suitable.
I would suggest reading this for a quick understanding of WPF's available Layout Panels : WPF Layouts - A Visual Quick Start
Another alternative is to give your Grid some Row Definitions, and specify that the row containing the ScrollViewer should be of a fixed size, or should be sized so it fits whatever size the child object wants (Height="Auto")
Or you could give your ScrollViewer a fixed height, and set it's VerticalAlignment property so it gets docked to either the top or bottom of the Grid.
Personally I would recommend the first option - reviewing WPF's layout system and determining a more approrpiate panel type for your layout. And if the most appropriate panel type is a Grid, then I would highly recommend using the RowDefinitions and ColumnDefinitions to give your Grid some structure, rather than trying to use excessively large Margins to position your controls.
You're pretty close, the problem appears to be an issue of layout. Because the controls are arranged in the grid without row and column definitions the scrollviewer is attempting to resize to the full size of the grid while the textblock is adhereing to its fixed size and margin. Try the following starting point and see if it helps:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Label x:Name="lblTitle" Content="Label" HorizontalAlignment="Left" Width="186"/>
<Label x:Name="lblPublishDate" Content="Label" HorizontalAlignment="Left" Width="186"/>
<Image x:Name="imgPic" HorizontalAlignment="Left" Height="81" Width="81"/>
</StackPanel>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="tbDesc" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock"/>
</ScrollViewer>
</Grid>

How to pad contents in XAML user control?

I have an XAML user control that has a Grid with various TextBoxes and Labels in it.
<UserControl>
<Grid Background="#FFE8EEF7" Height="Auto" Width="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- just various TextBoxes and Labels here -->
<TextBox x:Name="txtBottomTextBox" HorizontalAlignment="Left" Height="24" Margin="800,37,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="50" IsEnabled="False"/>
</Grid>
</UserControl>
I then have the user control on my main window like so:
<Window>
<Grid Width="Auto" Height="Auto">
<MyCtrlLib:GenParams Name="genParams1" Width="Auto" Height="Auto" Margin="0,0,0,0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
</Window>
Everything is fine except for the lack of space between the bottom-most TextBox and the bottom of the user control. There is perhaps three pixels of space at most between the two, and so I would like to have more padding (maybe up around 8 pixels). However, I've tried dumping in padding where I can, but nothing works like I want it to.
Any suggestions on how I can force some padding between the bottom-most TextBox and bottom of the user control?
Set the TextBox margin like this (the values go clockwise starting from left):
Margin="800,37,0,8"

Why ZIndex is not considered in arrange override of a stackpanel

Here is what I tried to to:
Added a stackpanel to my window (Orientation: Horizontal)
Added a set of buttons to it
Set the first button's ZIndex to be higher than the second one
Increased the width of the first button
What I expected:
I expected the first button to be on top of the second button (atlest overlay)
StackPanel's width should not change unless the width of the first button is no more sufficient
What is happening actually:
First button's width increases and the second button moves towards the right accordingly. They stay on the same plane
StackPanel's width increases with increase in the first button's width
Here is my question:
I know that stackpanel has not considered ZIndex while arranging the items within itself, BUT WHY?? Shouldn't it consider the ZIndex of its children while arranging them???
The Stackpanel 'stacks' its children based on their widths, i.e. if you increase the width of an item (or increase its margin), the stackpanel will simply expand to accomodate this. If you want to force items within a stackpanel to overlap, you will have to change their location after the layout has been computed. You can perform this using a RenderTransform. See the example below:
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Button Content="One" Canvas.ZIndex="10">
<Button.RenderTransform>
<TranslateTransform X="10"/>
</Button.RenderTransform>
</Button>
<Button Content="One"/>
<Button Content="One"/>
<Button Content="One"/>
</StackPanel>
And yes, the ZIndex is respected. This is an attached proepry of Canvas, however, it seems to be used by the rendering engine directly rather than by Canvas, hence it works in the above code.
I tried to find some relevant info about how to set the z index of wpf layout elements and panels. Using a Canvas comes with a different set of positioning issues which I simply hadn't the time to investigate. Here is a simple solution using the Panel.ZIndex property in xaml.
<Grid>
<Border Width="100" Height="100" Margin="0,0,50,50" Panel.ZIndex="1" Background="Navy" Opacity="0.3"
VerticalAlignment="Top" HorizontalAlignment="Left">
</Border>
<Border Width="100" Height="100" Margin="50,50,0,0" Background="Fuchsia" Opacity="0.3">
</Border>
The resulting two square border elements will overlap. One can use stackpanels instead of borders and use this logic to overlap anything easily.
Here is the same code adapted to the button problem:
<Grid>
<StackPanel Panel.ZIndex="10" Margin="20,20,0,0" HorizontalAlignment="Left">
<Button Content="One" Width="50" Height="40">
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="50,0,0,0" >
<Button Content="Two" Width="50" Height="40"/>
<Button Content="Three" Width="50" Height="40"/>
<Button Content="Four" Width="50" Height="40"/>
</StackPanel>
</Grid>

TextBox expanding with surrounding Grid but not with text

A window has a Grid with two columns. The left column contains a control with a constant width but with a height that adapts. The right column contains a TextBox that takes up all remaining space in the Grid (and thereby in the Window).
The Grid is given a minimal width and height and is wrapped within a ScrollViewer. If the user resizes the window to be smaller than the minimal width/height of the Grid, scrollbars are displayed.
This is exactly how I want it to be. However, a problem occurs when the user starts typing text. If the text is to long to fit in one line in the TextBox, I want the text to wrap. Therefore I set TextWrapping="Wrap" on the TextBox. But since the TextBox has an automatic width and is wrapped in a ScrollViewer (its actually the whole Grid that is wrapped), the TextBox just keeps expanding to the right.
I do want the TextBox to expand if the window is expanded, but I don't want the TextBox to expand by the text. Rather the text should wrap inside the available TextBox. If the text don't fit within the TextBox height, a scrollbar should be displayed within the TextBox.
Is there a way to accomplish this?
Below is some code that shows my problem:
<Window x:Class="AdaptingTextBoxes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="400" Background="DarkCyan">
<Grid Margin="10" Name="LayoutRoot">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid MinWidth="300" MinHeight="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="0,0,10,0" Content="Button" Width="100" />
<TextBox Grid.Column="1" AcceptsReturn="True" TextWrapping="Wrap" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</ScrollViewer>
</Grid>
</Window>
You could use an invisible border (its hacky but it works - its how I tend to sort out dynamic textbox sizes in Xaml):
<Border BorderThickness="0" x:Name="border" Grid.Column="1" Margin="0.5" />
<TextBox Grid.Column="1" AcceptsReturn="True" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}" />
Have you tried setting the MaxWidth property on just the TextBox?
Edit after OP's comment
I would try getting rid of the ScrollViewer. The sizing used in the Grid's layout should take care of re-sizing and the scroll bar settings on the TextBox should take care of the rest.
The answer is based on Leom's answer.
The solution works great when you enlarge the window, but the resizing is not smooth when you make the window smaller. As the textbox participates in the grid's layout, it has to perform layout process multiple times. You can fix that by putting the texbox in the canvas, so the change of the size of the textbox no longer triggers the grid's re-layout.
The updated code:
<Border BorderThickness="0" x:Name="border" Grid.Column="1" Margin="0.5" />
<Canvas Grid.Column="1">
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}" />
</Canvas>

Resources