Keeping controls in the visible area - c#

I've got a grid with several TextBoxes in it. I want to keep this grid fixed at the bottom of my main window. So if the user scrolls down the grid should basically stay in it's place.
One way I thought of doing this was to get some sort of value from the ScrollViewer and add it to the grids Canvas.TopProperty. However I am not sure which value changes when the user scrolls up or down.

Then don't put the scroll on the main window. Put ScrollViewer only on the content (rows) that you want to scroll. Careful not to use an auto for the height of the rows with the ScrollViewer or the container will grow to support all the content and the Scroll does not come into play.

One way:
<Window x:Class="Sample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<ListBox>
<!--Hardcoded listbox items just to force the scrollbar for demonstration purposes -->
<ListBoxItem>Item1</ListBoxItem>
<ListBoxItem>Item2</ListBoxItem>
<ListBoxItem>Item3</ListBoxItem>
<ListBoxItem>Item4</ListBoxItem>
<ListBoxItem>Item5</ListBoxItem>
<ListBoxItem>Item6</ListBoxItem>
<ListBoxItem>Item7</ListBoxItem>
<ListBoxItem>Item8</ListBoxItem>
<ListBoxItem>Item9</ListBoxItem>
<ListBoxItem>Item10</ListBoxItem>
<ListBoxItem>Item11</ListBoxItem>
<ListBoxItem>Item12</ListBoxItem>
<ListBoxItem>Item14</ListBoxItem>
<ListBoxItem>Item15</ListBoxItem>
<ListBoxItem>Item16</ListBoxItem>
<ListBoxItem>Item17</ListBoxItem>
<ListBoxItem>Item18</ListBoxItem>
<ListBoxItem>Item19</ListBoxItem>
<ListBoxItem>Item20</ListBoxItem>
<ListBoxItem>Item21</ListBoxItem>
<ListBoxItem>Item22</ListBoxItem>
</ListBox>
<Grid Panel.ZIndex="5" VerticalAlignment="Bottom" Background="DarkGray">
<StackPanel>
<TextBox HorizontalAlignment="Left" VerticalAlignment="Center">Text box 1</TextBox>
<TextBox HorizontalAlignment="Left" VerticalAlignment="Center">Text box 2</TextBox>
<TextBox HorizontalAlignment="Left" VerticalAlignment="Center">Text box 3</TextBox>
</StackPanel>
</Grid>
</Grid>

Related

WPF DataGrid Vertical Scroll Bar Issues with Dynamically Loaded Item Source

The issue: Cannot get the Vertical Scroll bar for the DataGrid to appear unless I set a static height on the grid. I know similar questions have been asked before, however unlike other questions my example is a lot more simple with no Grid Columns. The DataGrid is simply inside a StackPanel inside the control. That's it, and no combination of Auto, "*", etc works except setting a static Height.
Is this simply a lacking feature in WPF framework that it a Observable Collection on the ViewModel bound to a grid, will not notify the View when items are added to the VM Collection? Do I have to code a custom property and bind the DataGrid Height to that?
Here is my XMAL:
<Window x:Class="Monster.Configure"
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:viewModels="clr-namespace:Monster.ViewModels"
xmlns:local="clr-namespace:Monster"
mc:Ignorable="d"
Title="Configure" Width="1200">
<Window.DataContext>
<viewModels:ViewModelMain/>
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="b2v" />
</Window.Resources>
<Grid>
<StackPanel Margin="5">
<Button Name="button_Refresh" Content="Save / Refresh" HorizontalAlignment="Left" Margin="5" Width="100"
Click="button_Refresh_Click"></Button>
<StackPanel Orientation="Horizontal">
<!--Buttons and other junk here-->
</StackPanel>
<Label></Label>
<DataGrid Name="dataGrid_PendingCreation" CanUserAddRows="True" CanUserDeleteRows="True" AutoGenerateColumns="False"
ItemsSource="{Binding URLsForGrid}"
Loaded="dataGrid_PendingCreation_Loaded"
CellEditEnding="dataGrid_PendingCreation_CellEditEnding"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility ="Auto"
Width="Auto" Height="Auto">
<DataGrid.Columns>
<!--Columns and junk here-->
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
As you can see, the user is allowed to add new rows. When doing so, a Vertical Scroll Bar never shows up unless a Static Height is set in the DataGrid.
Your DataGrid is wrapped inside a StackPanel (with vertical orientation). A vertically oriented StackPanel always gives seemingly infinite available height to its children. Since your DataGrid's Height property is set to Auto, it will grow as large as it can; it will never show a vertical scrollbar, because this is done only if the DataGrid's available height (determined by the parent) is less than the actually needed height.
The solution is not to use a StackPanel here. Instead, use a Grid or a DockPanel.
I usually prefer the latter, if all I want to achieve is vertical or horizontal stacking of the children and have one child stretch as wide as possible; here is how it would be done:
<Window>
...
<Grid>
...
<DockPanel Margin="5">
<Button DockPanel.Dock="Top" Name="button_Refresh" ... />
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<!--Buttons and other junk here-->
</StackPanel>
<Label DockPanel.Dock="Top"></Label>
<DataGrid Name="dataGrid_PendingCreation" ... />
</DockPanel>
...
</Grid>
...
</Window>
Make sure that DataGrid is the last child in the DockPanel and that it has no DockPanel.Dock property. This ensures that the DataGrid will be given the available height and width that remains after placing all other controls.

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.

WPF xaml - datagrid and button can't coexist?

So I'm trying to add a datagrid and a button to my wpf form.
<Window x:Class="Database_Filler.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Database Filler" Height="350" Width="557">
<DataGrid x:Name="data" HorizontalAlignment="Left" VerticalAlignment="Top" Height="230" Width="520"/>
<!--<Button Content="Run Query" HorizontalAlignment="Left" Margin="468,294,0,0" VerticalAlignment="Top" Width="75" Click="Button1_Click"/>-->
</Window>
But whenever I uncomment the button I get the error: "The property 'Content' is set more than once."
Window is ContentControl as such can host only one element. If you want to host more then one element then you need some sort of Panel that can host many children. You can choose from few implementations like Grid, StackPanel, WrapPanel and so on depending on how you want to arrange your items
<Window x:Class="Database_Filler.MainWindow" ...>
<StackPanel>
<DataGrid x:Name="data" HorizontalAlignment="Left" VerticalAlignment="Top" Height="230" Width="520"/>
<Button Content="Run Query" HorizontalAlignment="Left" Margin="468,294,0,0" VerticalAlignment="Top" Width="75" Click="Button1_Click"/>
</StackPanel>
</Window>
EDIT
Most commonly used Panel types:
Grid: Defines a flexible grid area that consists of columns and rows
DockPanel: Defines an area where you can arrange child elements either horizontally or vertically, relative to each other.
Canvas: Defines an area within which you can explicitly position child elements by using coordinates that are relative to the Canvas area.
StackPanel Arranges child elements into a single line that can be oriented horizontally or vertically.
WrapPanel Positions child elements in sequential position from left to right, breaking content to the next line at the edge of the containing box. Subsequent ordering happens sequentially from top to bottom or from right to left
UniformGrid Provides a way to arrange content in a grid where all the cells in the grid have the same size.
The window can only have one child element. You need to put it e.g. inside a Grid or Stackpanel
<Window x:Class="Database_Filler.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Database Filler" Height="350" Width="557">
<Stackpanel>
<DataGrid x:Name="data" HorizontalAlignment="Left" VerticalAlignment="Top" Height="230" Width="520"/>
<Button Content="Run Query" HorizontalAlignment="Left" Margin="468,294,0,0" VerticalAlignment="Top" Width="75" Click="Button1_Click"/>
</Stackpanel>
</Window>

Layout two controls on a line in a stretchable WPF window

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>

C# WPF - ScrollViewer + TextBlock troubles

I have a TextBlock within a ScrollViewer that aligns with stretch to its window. I need the TextBlock to behave as the following:
Resizes with window, no scrollbars
When resized below a certain width the TextBlock needs to keep a MinWidth and scrollbars should appear
TextWrapping or TextTrimming should work appropriately
How can I get this functionality?
I have tried several ways, involving bindings to ActualWidth & ActualHeight, but can't get it to work.
This can't be that difficult, what am I missing?
Here is a code sample to put in XamlPad (no MinWidth is set yet):
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<TextBlock TextWrapping="Wrap" Text="Some really long text that should probably wordwrap when you resize the window." />
</ScrollViewer>
</Window>
This works:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Name="Scroller">
<TextBlock HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MinWidth="100"
Width="{Binding ElementName=Scroller, Path=ViewportWidth}"
TextWrapping="Wrap"
Text="Some really long text that should probably wordwrap when you resize the window." />
</ScrollViewer>
</Window>
Without more detail, the best I can do is provide the standard way of doing this. Basically, host your element (which has a minimum size) in a scroll viewer; when the scrollviewer is resized small enough such that the element cannot wholly fit inside it, it will automatically display scroll bars. Example:
<ScrollViewer>
<Button MinWidth="100" MinHeight="50"/>
</ScrollViewer>

Categories

Resources