Fitting Page and Contents to a Window and Form c# - c#

I have a window that has a a frame in which I add pages two. So far I have Page1 and Page2. By using the NavigationService I can move between the two pages within the same window, something I have spent some time trying to implement.
The issue now however is that whilst the Window is maximised (which I want) the page's content is only displayed in the top-left hand corner of the Window.
I understand this is purely a matter of choosing the correct containers however I am a bit stuck in what I should go for, this is the layout of each section;
Window - Contains a form
Pages (added to the form) - Contain a dock panel - Contains a canvas - Contains elements (buttons etc)
As I say the issue is that the page is only displayed in the top-left hand corner, I would like the page to "grow" with the Window.
Code for the Window:
<Window x:Name="window" x:Class="WPFNavigation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFNavigation"
mc:Ignorable="d"
Title="Test1" Height="500" Width="500" ResizeMode="CanResizeWithGrip" WindowState="Maximized">
<Frame x:Name="frame" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" NavigationUIVisibility="Hidden"/>
</Window>
Code for a Page:
<Page x:Class="WPFNavigation.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFNavigation"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1" WindowTitle="Page 1">
<DockPanel HorizontalAlignment="Left" Height="280" LastChildFill="False" Margin="10,10,0,0" VerticalAlignment="Top" Width="280">
<Canvas HorizontalAlignment="Stretch" Width="280">
<Button x:Name="button_Page1" Content="Page 1" Canvas.Left="19" Canvas.Top="235" Width="75"/>
<Button x:Name="button_Page2" Content="Page 2" Canvas.Left="99" Canvas.Top="235" Width="75" Click="button_Copy_Click"/>
<Button x:Name="button_Page3" Content="Page 3" Canvas.Left="179" Canvas.Top="235" Width="75"/>
</Canvas>
</DockPanel>
</Page>

The main problem here is that you are using absolute positioning to position your elements on a Canvas and you are also explicitly setting the Width of elements. This doesn't play well with dynamic window sizes.
By setting the Canvas.Left and Canvas.Top you are making the assumption that every device that this application runs on can render the view in exactly the same way. Imagine what your application would look like on a phone, or a screen with a lower resolution.
Instead, consider using a Grid, or make better use of your DockPanel, here's an example:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Content="Hello"/>
<Button Content="World" Grid.Column="1"/>
<TextBox Text="Le toucan has arrived" Grid.Column="2"/>
</Grid>
This is obviously a very simple example however it showcases relative sizes and positioning of elements instead of explicitly setting the Canvas.Left, Canvas.Top and setting the Width.
The above example plays well with dynamic window sizing, if you were to resize the window, then the TextBox will resize as the column it sits inside is set to a * width, which basically means "fill all available space".
If you're in a scenario where you want the content of a page to scale up so that it fills all available space, then you can use a ViewBox, you can find a tutorial here. These are tricky to get right, however I would recommend that you get to grips with other layout panels before implementing a ViewBox, they should be used with care as it's very easy to screw up the user experience where content can become either too small or too large.
Explicitly setting the Width and/or Height of an element should be an absolute last resort. Allowing your application to transition nicely when the application is resized is what makes any user experience more delightful, and the way of which to achieve that is to use relative sizes and positions.
I would recommend reading up on this topic here and here.

Related

How can I make a ScrollViewer properly size dynamically with a StackPanel?

For reference, this is a chat application. This should give you some idea of a final goal.
Additionally, I am very new to WPF. This is one of my first applications and I am making this as a proof of concept. I've been using Windows Forms up until this point, so any comparison or reference to it would help me understand a bit better.
So, the issue at hand:
The chat box for my chat application is a StackPanel (should it be?) which is programmatically populated with TextBlock elements. I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise).
The question: How can I make a ScrollViewer properly size dynamically with a StackPanel?
Additionally, I also need this StackPanel to size dynamically as the window is sized. This, in turn, would affect the scroll bar.
My current "solution" is to use a ScrollViewer with the StackPanel nested. However, the ScrollViewer and StackPanel do not size properly with a change in window size, as shown in screenshot #2. The XAML code and a screenshot of the designer is shown below.
<Window x:Name="Main" x:Class="dprCxUiDemoWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:dprCxUiDemoWpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="#FF171717">
<TextBox x:Name="ChatBox" TextWrapping="Wrap" Background="#FF4F4F4F" Foreground="White" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" RenderTransformOrigin="-0.118,12.093" Margin="146,0,0,1" VerticalAlignment="Bottom" Height="46" BorderBrush="#FFFF00F3" KeyDown="ChatBox_KeyDown"/>
<Image x:Name="DprLogo" Source="/dprCxUiDemoWpf;component/Images/logo1.png" HorizontalAlignment="Left" Height="60" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="123"/>
<ScrollViewer Background="Red" Height="Auto" Width="Auto" ScrollViewer.CanContentScroll="True" Margin="146,0,0,0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" VerticalAlignment="Top" MinHeight="372">
<StackPanel x:Name="ChatPanel" Height="Auto" Width="Auto" Background="DimGray" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</Grid>
</Window>
(source: gcurtiss.dev)
Please note the following regarding the first screenshot:
A. The black column (containing the logo) is simply the background color of the window; there is nothing there.
B. The gray portion is ChatBox (the StackPanel)
C. The pink highlighted box below is the text box where messages are entered.
I appreciate and accept any and all help!
You have to use the Grid panel properly. You layout its children by defining rows and columns. Grid is a column/row based layout container. Then configure row and column definitions to control the resize behavior of the cells and their content.
Using absolute positioning and sizes will of course prevent controls from responding to their parent's size changes. Most control stretch to fit the available space. But this requires dimension properties being set to Auto.
You said you are "more of a hands-on learner", but you should still read some documentations. Otherwise you will significantly slow down your progress until stagnation.
There are tons of blogs waiting for you to read them. To poke around in the dark will get you nowhere. You need at least to know the basics. Instead of waiting 13+ hours for a copy & paste ready answer, you could have finished multiple tutorials already and solve this on your own. Success is a good feeling. This is a very trivial problem.
Find yourself a good tutorial that you find easy to understand and start to experiment with the Grid after reading it.
According to your posted code, you obviously have zero idea how this panel works and how you can use it to align your controls.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<StackPanel />
</ScrollViewer>
<Image Grid.Row="1" Grid.Column="0"
Height="60"
Width="123 />
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise)
You should read about data-binding and MVVM first. Usually you hold an ObservableCollection of items on your VM and bind them to eg a ListBox on your View.
Then you can scroll-down the ListBox, each time a new item got added to your collection:
private void ScrollRecordsListToBottom()
{
if (RecordsList.Items.Count == 0)
return;
var lastItem = RecordsList.Items[RecordsList.Items.Count - 1];
RecordsList.ScrollIntoView(lastItem);
}

How can I force a WPF TextBox to both fill the content space, but also NOT grow beyond the bounds of it's container?

I need to create a simple user control which is used for displaying a descriptive message to the user. The XAML definition that I have for this control is as follows:
<UserControl x:Class="Console.WPF.DisplayMessageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
...
mc:Ignorable="d"
d:DesignWidth="550"
HorizontalAlignment="Stretch">
<UserControl.Resources>
<!--Converters-->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox
Grid.Column="0"
VerticalAlignment="Top"
FontWeight="Normal"
FontSize="10"
Padding="12,4,2,2"
Height="74"
Background="White"
BorderThickness="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
TextWrapping="Wrap"
Text="{Binding DetailsText, Mode=OneWay}" />
<Image Source="..."
Stretch="None"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0,2,0,0"
Visibility="{Binding IsErrorMessage, Converter={StaticResource BooleanToVisibility}}"/>
</Grid>
</UserControl>
This UserControl is currently embedded within a sizable stack of containers (DockPanels, StackPanels, etc.) and user controls. However, it's first parent container is a DockPanel. It is the last control in the DockPanel and the LastChildFill property is set to true on the panel.
My problem is that when I use this user control, regardless of HorizontalAlignment settings, it alway sizes the user control to the size of the text field. This means that if the text field is short, the user control will be small and look odd since it's not filling it's respective content hole. If the text is a vary long block of descriptive text, it doesn't wrap and continues to expand and is hidden off the edge of the screen.
I can't figure out how to fix this. I also want to note that this application does not use scroll bars, which have been band from use, except on list boxes. I can't use scroll regions for this application, however, this message object absolutely must expand to fill the container but NOT expand any larger.
How can I accomplish this without constraining a fixed Width value on my user control instance? Instead, I need the user control to grow with the container, when the window grows as well.
NOTE
I'm adding the C# tag, just in case this has to involve a code-behind change. I really hope it's just a matter of setting the right set of magic-properties, which have eluded me, however, I'm not opposed to encoding a rock-solid code behind solution, so long as it specifically does what I need it to do.

WPF Force UserControl to expand to fill maximal render size

I need to create a UserControl, which is hosted within a "parent" ContentControl who is set with "Center" Horizontal and Vertical alignment.
This control hosts another "child" UserControl, which I would like to be as big as it can get without stretching over the render-able size.
I noticed that if I set the child's size to be bigger than the render-able size, i get the size I want as a "DesiredSize" property on that control.
However I don't see how can I get that information without oversizing the control.
I've created this sample to illustrate the situation, I want "ChildControl"'s pink background to stretch over the entire window.
Just to clarify, I only have control on "ControlableElementA" and "ControlableElementB"
I cannot bind to the main window, in the actual application I use this as an embedded window with varying levels of hierarchy in between..
The "ChildControl" and "ParentControl" are beyond my reach due to constraints above me.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<ContentControl Name="ParentControl" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Name="ControlableElementA" Background="Black" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentControl Name="ControlableElementB">
<Grid Name="ChildControl" Background="Pink" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Button Width="30" Height="30"></Button>
</Grid>
</ContentControl>
</Grid>
</ContentControl>
</Grid>
</Window>
Thanks ahead, I apologize if the description is a little cryptic
HorizontalAlignment and VerticalAlignment as Stretch means "Stretch to the maximum size within the bounds of the parent container."
However, your parent container is set to Center, which means your UserControl effectively has an Auto width and height.
To fix this, simply remove:
HorizontalAlignment="Center" VerticalAlignment="Center"
From the ContentControl.

WPF window resizes when ListBox scrolled

I want to have my window size to the items in the listbox. The listbox contains variable lenght strings (20 to 120 chars). When I scroll the listbox and longer strings in the listbox scroll off the display the listbox shrinks and my window shrinks with it. How can I keep my window size fixed as the user scrolls and yet still have the window initially size to content. You know, cake and eat it too.
<Window x:Class="MyApp.MyDialog
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="My Dialog" MaxHeight="600" SizeToContent="WidthAndHeight" ShowInTaskbar="False" Width="Auto" Height="Auto" Loaded="Window_Loaded">
<Grid Width="Auto" Height="Auto" Margin="5,5,5,5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="48" ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0" >
<TextBlock>Total: </TextBlock>
<TextBlock Text="{Binding myData.Count}"></TextBlock>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding myData}"/>
<Button Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center" Height="28" Click="buttonOK_Click" Margin="0,5,0,5" IsDefault="True" Name="buttonOK" Width="75">OK</Button>
</Grid>
I think you should remove your sizetocontent property and programatically resize your windows as you wish based on your listbox content.
I have, on occasion, had the requirement that a window size to fit its content initially, then keep the size fixed unless explicitly resized. Typically, I would just hook the window's ContentRendered event, and clear out the SizeToContent property in the event handler (and also unhook the event). This ensures that layout has fully completed, and the window has been shown and its bounds fully computed by the time you revert to a fixed size.
This isn't the best solution in the world, and it breaks down in cases where your content isn't fully available when the ContentRendered event fires. The most likely example I can think of would be if data in your view model is not yet available, and, consequently, your view is not fully populated yet. This may not be an issue for you--it depends on your design and whether you are doing any deferred/async data loading. But it works in simpler scenarios, and it has the virtue of being simple to implement.

WPF: Order of stretch sizing

I'm creating a modal dialog window which contains three essential parts: a TextBlock containing instructions, a ContentControl for the dialog panel, and a ContentControl for the dialog buttons. Each of these parts are contained in a separate Grid row.
I have some specific constraints when it comes to how the dialog should be sized. The issue I'm having is with the instructions TextBlock. I want the instructions to be as wide as the ContentControl for the dialog panel. The instructions should then wrap and grow vertically as needed. Should the instructions not be able to grow vertically, then it should begin to grow horizontally.
Getting the instructions to be the width of the ContentControl and grow vertically was simple. The part I can't seem to figure out is how to get it to grow horizontally when out of vertical space. My initial thought was to create a class that extends TextBlock and override MeasureOverride. However, that method is sealed. Currently, I'm playing with the idea of have the dialog Window override MeasureOverride to calculate the available size for the instructions block.
Am I missing a much simpler way of accomplishing this? Does anyone have any better ideas than this? Messing with MeasureOverride seems like it will be a lot of work.
Here is some sample code to give you a general idea of how the dialog is laid out:
<Window
x:Class="Dialogs.DialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="dialogWindow"
ShowInTaskbar="False"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen">
<Border Style="{StaticResource WindowBorderStyle}" Margin="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock
Margin="25,5"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Text="{Binding Instructions}"
TextWrapping="Wrap"
Width="{Binding ElementName=panelContentControl, Path=ActualWidth, Mode=OneWay}"/>
<ContentControl
x:Name="panelContentControl"
Grid.Row="1"
Margin="25,5"
Content="{Binding PanelContent}"/>
<ContentControl
x:Name="buttonsContentControl"
Grid.Row="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="25,5"
Content="{Binding ButtonsContent}"/>
</Grid>
</Border>
</Window>
It appears as if what you really want to create a new Panel or derive from an existing one which would take place of what is currently your Grid. Panel is responsible for laying out your content so you should go that way instead of messing with Window.MeasureOverride.
How exactly do you want your TextBlock to grow horizontally and why do you want this? By growing it horizontally do you want it to grow the Window too?

Categories