Layout two controls on a line in a stretchable WPF window - c#

Here's a fairly common UI pattern: Text box that contains a path to the left, and a Browse button to the right of it. If the window resizes, the button stays to the right, but the text box stretches to reveal more/less of the path. So in the good old days of anchors, the button would be anchored to the right and the text box would be anchored both left and right.
Trying to replicate this in WPF seems to be worryingly difficult.
If I create a new window, it comes with a Grid layout by default. I place my text box to the left and size it appropriately, then place the button to its right. HorizontalAlignment for the text box is Stretch and for the button it is Right.
In my mind, this works as described, but in real life the text box doesn't resize at all, but instead tries to center itself in the window, while the button acts as expected. What gives?
Here is my XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="241" d:DesignWidth="414" SizeToContent="WidthAndHeight">
<Grid>
<TextBox Height="23" HorizontalAlignment="Stretch" Name="textBox1" VerticalAlignment="Top" Margin="12,11,101,0" />
<Button Content="Button" Height="23" HorizontalAlignment="Right" Margin="0,11,12,0" Name="button1" VerticalAlignment="Top" Width="75" />
</Grid>
</Window>

You would create another GridControl with two columns, one with a fixed width for Browse button and another one for the TextBox.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" />
<Button Grid.Column="1" />
</Grid>

You can use a DockPanel and set LastChildFill property to true to ensure that the textbox uses all the available space:
<DockPanel LastChildFill="true">
<Button DockPanel.Dock="Right">Browse</Button>
<TextBox>Content</TextBox>
</DockPanel>

Related

Window size does not match what I have in the designer

I'm not sure why it seems that every time I work with WPF, things are always much more difficult that WinForms or C/SDK.
In this case, the window looks like this in the designer.
But here's how it looks at run time.
And my XAML:
<Window x:Class="InsiderArticlesManager.AuthorWindow"
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:InsiderArticlesManager"
mc:Ignorable="d"
Title="Set Author" Height="114" Width="341" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" SourceInitialized="Window_SourceInitialized" WindowStyle="SingleBorderWindow">
<StackPanel>
<Label Name="lblPrompt" Content="Select Author:" Margin="5,5,5,0" />
<ComboBox Name="UserList" DisplayMemberPath="Email" Margin="5,0,5,5"></ComboBox>
<WrapPanel HorizontalAlignment="Right">
<Button Name="btnOk" Content="OK" Width="78" Margin="5,5,0,5" IsDefault="True" Click="Ok_Click" />
<Button Name="btnCancel" Content="Cancel" Width="78" Margin="5,5,5,5" IsCancel="True" />
</WrapPanel>
</StackPanel>
</Window>
I thought the whole point of the designer was so that I could see how the window will look. Since it shows me something different, how do I know what size to set it?
You might want to set the Width and Height on the StackPanel and set the Window's SizeToContent to WidthAndHeight. That way the content is always visible, no matter the size of the window border (assuming you size your StackPanel correctly :)):
<Window x:Class="InsiderArticlesManager.AuthorWindow"
...
SizeToContent="WidthAndHeight">
<StackPanel Height="114" Width="341">
...
</StackPanel>
</Window>
In your case the difference between design time and runtime occurs because the XAML designer window has a smaller window border size.

How to click WPF ScrollViewer down button

Following picture shows my project.
Here is XAML code for your testing needs.
<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="200" Width="525">
<ScrollViewer Name="ScrollViewer1" Height="67" VerticalAlignment="Top">
<StackPanel>
<TextBox Name="TextBox1" Text="TextBox1"/>
<TextBox Name="TextBox2" Text="TextBox2"/>
<TextBox Name="TextBox3" Text="TextBox3"/>
<TextBox Name="TextBox4" Text="TextBox4"/>
<TextBox Name="TextBox5" Text="TextBox5"/>
<TextBox Name="TextBox6" Text="TextBox6"/>
</StackPanel>
</ScrollViewer>
</Window>
Following picture shows my question;
So, how to click WPF ScrollViewer down button in order to go end of ScrollViwer?
A solution could be to subscribe to the click of that button like here https://stackoverflow.com/a/4932118/6890102, then call the ScrollToEnd() method of the ScrollViewer. But there might be a better solution.
Right-Click on ScrollViewer->Edit Template->Edit a Copy, to see the Scrollviewer's Template. The ScrollViewer contains a ScrollBar. A ScrollBar contains, RepeatButton, and a Track. The RepeatButton is responsible for the up and down movement of the scrollbar. Try checking it. You may find some idea on how to implement your objective.

Automatic resizing of user controls when parent window is resized

I am working on a C# WPF project and I am making use of user controls but I am having a problem with getting the resize to work when the parent window is resized.
I have a Window called MainWindow which houses a canvas which will host the user controls. When Main Window is resized the user controls should also resize. This is more a less working except it doesn't seem to fill up the who screen. Below is the code and screenshots to explain what I mean.
Main Window
Below is the code for the Main Window, this window will host the user controls
<Window x:Class="ReportReader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="513" Width="925" WindowStartupLocation="CenterScreen">
<Grid>
<Menu Height="23" Name="menu1" VerticalAlignment="Top">
<MenuItem Name="mnuFile" Header="File">
<MenuItem Name="mnuOpen" Click="mnuOpen_Click" Header="Open" />
<MenuItem Name="mnuClose" Click="mnuClose_Click" Header="Close Report" />
<MenuItem Name="mnuRecentFile" Header="Recent Files" />
<Separator />
<MenuItem Name="mnuExit" Click="mnuExit_Click" Header="Exit" InputGestureText="Alt+F4" />
</MenuItem>
</Menu>
<StatusBar Name="statusBar1" Height="28" VerticalAlignment="Bottom" />
<Label Content="No Report Loaded" Name="lblStatus" Height="28" VerticalAlignment="Bottom" Margin="0,0,39,0" />
<Grid Margin="12,29,12,34" Name="MainWindowHostCanvas" Background="Blue" />
</Grid>
</Window>
The canvas is coloured blue so it shows my problem in the screenshots.
Below is the code for one of the user controls
<UserControl x:Class="ReportReader.UserControls.ReportViewer"
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="879">
<Grid Height="415">
<Label Content="Report for..." Margin="12,12,12,0" Name="lblReportDateTitle" FontSize="26" FontWeight="Bold" HorizontalContentAlignment="Center" Height="44" VerticalAlignment="Top" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="12,62,0,0" Name="cboRegisteredApps" VerticalAlignment="Top" Width="202" SelectionChanged="cboRegisteredApps_SelectionChanged">
<ComboBoxItem Content="Select App" IsSelected="True" />
</ComboBox>
<DataGrid AutoGenerateColumns="False" Margin="14,203,12,12" Name="dataExceptionGroups" />
</Grid>
</UserControl>
Below is a screenshot of the User Control hosted by the Main Window from when the program first loaded up.
As you can see the blue canvas fills the whole window up to the status bar, the data grid, is at the bottom of the window, just above the status bar, and the report title is at the top of the window just above the menu bar.
Below is the screen shot now that the window has been resized
As you can see from this screenshot, the window has been resized and the blue canvas has resized to fill up the extra space, however, the datagrid is no longer at the bottom of the window, just above the status bar and instead floating around the middle. Also the title at the top is no longer just under the menu bar, and is also in the middle.
I'm not sure what I can do to resolve this.
Thanks for any help you can provide.
Your Grid within UserControl has fixed Height set to 415 which is why it does not stretch:
<UserControl>
<Grid Height="415">
If you're using designer to create your interface then it has tendency to set these properties to fixed values.
If Height="*" is set then all the rows would share the available space equally. If you want to set in % then you can set some thing like this
<Grid.RowDefenitions>
<RowDefenition Height="10*"/>
<RowDefenition Height="20*"/>
<RowDefenition Height="60*"/>
<RowDefenition Height="10*"/>
</Grid.RowDefenitions>
now when ever the window is re-sized always same percentage will be shared for all the rows
It is not necessary the height value in all the rows should sum up to 100. For example if you have three rows then you can set as below as well
<Grid.RowDefenitions>
<RowDefenition Height="20*"/>
<RowDefenition Height="30*"/>
<RowDefenition Height="10*"/>
</Grid.RowDefenitions>
then the total window size will be divided among the all the three rows as follows
for 1st row height will be WindowSize*20/(20+30+10)
for 2nd row height will be WindowSize*30/(20+30+10)
for 3rd row height will be WindowSize*10/(20+30+10)
All the Row sizes will re-size accordingly when ever window size is re-sized
You are using Static Sizing with margin's and height's with hard coded values.
1) Remove all the hard coded values for margin's height and width.
2) Split your grid using Row/Column Definitions with relative height using * , FYI you can set MinHeight
on RowDefenition .
<Grid>
<Grid.RowDefenitions>
<RowDefenition Height="*"/>
<RowDefenition Height="*"/>
<RowDefenition Height="*"/>
<RowDefenition Height="*"/>
</Grid.RowDefenitions>
<Menu>
.....
</Menu>
<StatusBar Grid.Row="1"/>
<Label Content="No Report Loaded" Grid.Row="2" />
<Grid Grid.Row="3" />
</Grid>
Do the same for the UserControl.

WPF, Window resizing as TextBox resizes

So I posted this question but the suggested answers do not seem to work. So once again, re-posting it, much simpler version of the XAML. We usually expect the TextBox to resize as the Window resize. I want the "other-way-around" behavior. TextBox that takes the whole Window area. The TextBox grows/shrinks in size, the Window follows. Please suggest on ways doing it.
<Window>
<Grid HorizontalAlignment="Stretch">
<TextBox Width="Auto">
</TextBox>
</Grid>
</Window>
<Window x:Class="MiscSamples.SizeToContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SizeToContent" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Grid HorizontalAlignment="Stretch">
<TextBox Width="Auto" AcceptsReturn="True">
</TextBox>
</Grid>
</Window>
Notice that you have to include ResizeMode="NoResize", because if the user resizes the window manually, the behavior is lost.
You could give the textbox a name and then bind the window width to the size of the textbox as such:
<Window Width="{Binding ElementName=txtbox, Path=ActualWidth}">
<Grid HorizontalAlignment="Stretch">
<TextBox x:Name="txtbox" Width="Auto">
</TextBox>
</Grid>
</Window>
I've not included any code for sizing the textbox (I assume you're going to do that programmatically or so) but with this XAML the window should size to the textbox width.

how do i get my datagrid to grow with the window and maintain margins?

Everything about my layout will flow with the resizing of the main window. The problem I'm facing is that as you can see, the datagrid goes off the screen. If you maximize the window, the datagrid will resize with the window, but continue to go off the screen. How do I get it to maintain its margin of 20 with it's parent grid?
<Grid>
<StackPanel Orientation="Vertical">
<TextBox Height="170" Name="txtSQL" VerticalAlignment="Top" AcceptsReturn="True" TextWrapping="Wrap" Margin="20"/>
<Button Content="Run!" Height="23" HorizontalAlignment="Left" Name="btnRun" VerticalAlignment="Top" Margin="20,0,0,0" Width="75" Click="btnRun_Click" />
<Grid>
<my:DataGrid Name="dgResults" VerticalAlignment="Top" Margin="20" />
</Grid>
</StackPanel>
</Grid>
UPDATE:
Just to be more specific. The effect I'm trying to accomplish here is this:
When the window first loads, you are presented with a blank datagrid, so it's only about 15 pixels high. When you run the query, it will populate the datagrid by reassigning the itemssource. As of now, when you do that, if the data exceeds the window size, it will go off the bottom of the screen. I need it to only expand to the bottom of the window then enable the scrollbar. I can do this just by wrapping it in a scrollviewer im sure. However, when the window is resized, the datagrid needs to resize with it.
I'm wondering if the setup might have something to do with it. The form is actually a wpf page being displayed in a frame.
UPDATE:
<Page x:Class="Hold_Database___Prototype_1.Views.SQL"
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:DesignHeight="304" d:DesignWidth="732"
Title="SQL" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" AllowDrop="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="23" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Height="170" Name="txtSQL" VerticalAlignment="Top" AcceptsReturn="True" TextWrapping="Wrap" Margin="20" Grid.Row="0"/>
<Button Content="Run!" Height="23" HorizontalAlignment="Left" Name="btnRun" VerticalAlignment="Top"
Margin="20,0,0,0" Width="75" Grid.Row="1" Click="btnRun_Click" />
<DockPanel Grid.Row="2">
<my:DataGrid Name="dgResults" Margin="20" />
</DockPanel>
</Grid>
</Page>
What is the dock panel for in this example?
Try putting the DataGrid directly in the cell with no stackpanel. If you are setting the button height then set set grid to auto.
Also, why give so much space to the text?
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Height="170" Name="txtSQL" VerticalAlignment="Top" AcceptsReturn="True" TextWrapping="Wrap" Margin="20" Grid.Row="0"/>
<Button Content="Run!" Height="23" HorizontalAlignment="Left" Name="btnRun" VerticalAlignment="Top"
Margin="20,0,0,0" Width="75" Grid.Row="1" Click="btnRun_Click" />
<my:DataGrid Grid.Row="2" <my:DataGrid Name="dgResults" Margin="20" />
Then also set HorizontalAlignment, VerticleAlignment, HorizontalContentAlignment, and VerticalContentAlignment = stretch
Your Grid is drawn off screen without a scroll bar because you are using a StackPanel. Care must be taken when using StackPanel - it is the most simplistic of all WPF Panel derived classes since its MeasureOverride calls Measure for all of its children with a size of double.PositiveInifity regardless of how much space the panel actually has available. Even a ScrollViewer will not help you with a StackPanel (the ScrollBar will be shown, but you won't be able to move it).
For example, consider a Window 350 in height and width, and a single Button as its content which is 500 in both height and width:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="350">
<StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Button Content="Hello" Height="500" Width="500" />
</ScrollViewer>
</StackPanel>
</Window>
Similar to your example, the Button here is drawn off screen in both the vertical and horizontal directions and a non-functional scroll bar will be present. If we change the panel to one which respects the size of its given area (e.g. DockPanel):
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ScrollViewer>
<Button Content="Hello" Height="500" />
</ScrollViewer>
</DockPanel>
</Window>
then scroll bars appear which are functional and hence allow the content off screen to be shown by scrolling.
Hope this helps!
What I have done was do define the horizontal and vertical alignments to "Stretch" respectively at the control you want (your DataGrid) to take up the size based on the window being resized.

Categories

Resources