Exclude control from scrolling - wpf

I have a situation in which ScrollViewer is set in the base class (Window) and my user control is dynamicaly added as a content of the window. Now, I want one of the controls (ToolBar) in my user control to be excluded from scrolling (to stay visible on top). I know there is a property HandlesScrolling on the Control class but it is internal. I have no way to put the toolbar outside the ScrollViewer because I don't have access to it.
Is there any way to do this?

The only way I can think to do that is to make your control size itself to the outer ScrollViewer and then have its own inner ScrollViewer to handle scrolling. There's some example code below but the key is binding the Height of your control to the ActualHeight of the outer ScrollerViewer like this: Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}"
Outer Scrollviewer:
<ScrollViewer>
<ListBox:HandlesItsOwnScrolling />
</ScrollViewer>
Your Control:
<UserControl x:Class="ListBox.HandlesItsOwnScrolling"
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="300" d:DesignWidth="300" Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type ScrollViewer}}}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0">Toolbar</Button>
<ScrollViewer Grid.Row="1">
<Border Background="AliceBlue" Height="1000" />
</ScrollViewer>
</Grid>
</UserControl>

Related

How to have Grid row height take only the space it needs, but also be constrained by available space in its container?

I want to have a header, then below that a ScrollViewer with an ItemsControl, then below that a footer. Something like:
<Window x:Class="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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ShowGridLines="True">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="36"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0">Header</TextBlock>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl>
<ItemsControl.Items>
<TextBlock>Item 1</TextBlock>
<TextBlock>Item 2</TextBlock>
<TextBlock>Item 3</TextBlock>
<TextBlock>Item 4</TextBlock>
<TextBlock>Item 5</TextBlock>
</ItemsControl.Items>
</ItemsControl>
</ScrollViewer>
<TextBlock Grid.Row="2">Footer</TextBlock>
</Grid>
</Window>
The above is almost what I want, except the middle row is greedy; even if the window is very tall, it takes up as much space as it can, pushing the footer to the bottom of the window.
If I change the middle row's definition to Height="Auto", it takes up exactly the amount of space that it needs, even if that space isn't available, so the ScrollViewer never shows the scrollbar, and the footer will get lost off the bottom of the window if the window isn't tall enough.
How do I make it so that if the window is tall enough for everything to fit, the footer is immediately below the ItemsControl, but if the window isn't tall enough, the ScrollViewer shows a scrollbar and the footer is at the bottom of the window?
I don't necessarily need to do this with a Grid, but I didn't find any other Panel that would do what I want either. E.g., a DockPanel with the header set to DockPanel.Dock="Top", the footer set to DockPanel.Dock="Bottom", and the ItemsControl filling the rest behaves exactly the same way.
Some other stuff I've tried:
Setting VerticalAlignment="Stretch" on the footer TextBlock: no change.
Making the footer row Height="*": still not what I want; the footer and the ItemsControl get the same height, so the footer takes up too much space most of the time, or if you make the window extremely short, it goes off the bottom of the window.
Probably there is a much more elegant way, but at least the below works for me in a similar situation. It is about calculating the real heigth of all items in the ItemsControl and adjusting the Grid's row height if needed.
<Grid ShowGridLines="True" Loaded="Grid_Loaded">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="36"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" Name="middlerow"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0">Header</TextBlock>
<ScrollViewer Name="scv" Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="items" Height="Auto" VerticalAlignment="Top">
<ItemsControl.Items>
<TextBlock>Item 1</TextBlock>
<TextBlock>Item 2</TextBlock>
<TextBlock>Item 3</TextBlock>
<TextBlock>Item 4</TextBlock>
</ItemsControl.Items>
</ItemsControl>
</ScrollViewer>
<TextBlock Grid.Row="2">Footer</TextBlock>
</Grid>
And in code behind:
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
double height = 0;
foreach (var item in items.Items)
{
height += (item as TextBlock).ActualHeight;
}
if (height < scv.ActualHeight)
{
middlerow.MaxHeight = height;
}
}
Thanks to mami's answer for the concept of how to do this, and Markus's answer for the idea to bind the Row's MaxHeight.
XAML:
<Window x:Class="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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ShowGridLines="True">
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="36"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" MaxHeight="{Binding ItemsMaxHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0">Header</TextBlock>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl Name="ic" SizeChanged="Ic_SizeChanged">
<ItemsControl.Items>
<TextBlock>Item 1</TextBlock>
<TextBlock>Item 2</TextBlock>
<TextBlock>Item 3</TextBlock>
<TextBlock>Item 4</TextBlock>
<TextBlock>Item 5</TextBlock>
</ItemsControl.Items>
</ItemsControl>
</ScrollViewer>
<TextBlock Grid.Row="2">Footer</TextBlock>
</Grid>
</Window>
Code behind:
Imports System.ComponentModel
Class MainWindow
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public ReadOnly Property ItemsMaxHeight As Double
Get
Dim Height = 0.0
Dim icg = ic.ItemContainerGenerator
For i = 0 To ic.Items.Count - 1
Height += DirectCast(icg.ContainerFromIndex(i), FrameworkElement).ActualHeight
Next
Return Height + 6.0 ' 6.0 to account for the size of borders? Not sure :(
End Get
End Property
Private Sub Ic_SizeChanged(sender As Object, e As SizeChangedEventArgs)
If e.HeightChanged Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ItemsMaxHeight"))
End If
End Sub
End Class
In this sample XAML, I don't need to increase the MaxHeight by 6, but in my actual app, if I don't add in a little extra, the ScrollViewer always shows a scrollbar that can be scrolled a tiny bit. Not sure what causes the discrepancy.

WPF - scaling uniformtofill not working as expected

Been struggling with this for a while now. I still don't really get what's going on. Viewbox Scale="UniformToFill" is supposed to make the content stretch out until it fills the screen. In the following pictures. I'm using the same control. Yet I get different results. What I really want is both to look like img1. When I say look I mean I want both to scale to fit the screen and not go out of bounds like on img1.
img1 - correct one
img2 - wrong scaling
<UserControl x:Class="lou"
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:lou.Controls"
xmlns:src="clr-namespace:lou"
mc:Ignorable="d"
x:Name="control" DataContext="{Binding RelativeSource={RelativeSource Self}}" MaxHeight="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=ActualHeight}">
<Viewbox Stretch="UniformToFill">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="SportLabel" Content="{Binding Path=SportName}" Foreground="Orange"/>
<ScrollViewer x:Name="SV1" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Hidden" >
<DataGrid Name="dataGrid" ItemsSource="{Binding DataTable}" Style="{StaticResource dataGrid}" ColumnHeaderStyle="{StaticResource DataGridColumnStyle}"/>
</ScrollViewer>
</Grid>
</Viewbox>
<Window x:Class="lou.test"
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:lou"
mc:Ignorable="d"
Title="asdfasdf" Height="300" Width="300"
WindowStyle="None"
WindowState="Normal"
x:Name="this" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="grid1" Background="Black">
<ScrollViewer x:Name="mainWindowScroller" VerticalScrollBarVisibility="Visible" CanContentScroll="True">
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Collection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate x:Name="itemsPanelTemplate">
<StackPanel x:Name="stackPanel" Orientation="Vertical" IsItemsHost="True"
MouseLeftButtonDown="stackPanel_MouseLeftButtonDown" ScrollViewer.VerticalScrollBarVisibility="Hidden"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
UniformToFill will stretch input content into the output area by using the same horizontal and vertical scale ratios, and considering the top-left corner as origin point for the transform. Therefore, either the entire width or the entire height are matching the output area width or height (depending on size ratios of input and output).
You may want to use Fill if you want to stretch all input content to all output area, but it will use different size ratios for horizontal and vertical scales.
Another option is to use Uniform if you want to stretch using the same scale ratio and ensure all input is displayed in output area, but some margin may remain empty in the left-right or top-bottom sides of the output.
I tested with this XAML example (based on your question):
<Viewbox Stretch="UniformToFill">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="SportLabel" Content="Test" Foreground="Orange"/>
<ScrollViewer x:Name="SV1" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<DataGrid Name="dataGrid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}"/>
</DataGrid.Columns>
<DataGrid.Items>
<sys:String>Test1</sys:String>
<sys:String>Test2</sys:String>
<sys:String>Test3</sys:String>
<sys:String>Test4</sys:String>
</DataGrid.Items>
</DataGrid>
</ScrollViewer>
</Grid>
</Viewbox>
And checked the result in four ways:
If width of the container is large enough, with UniformToFill
If width of the container is smaller, with UniformToFill
If we use Fill instead of UniformToFill
If we use Uniform instead of UniformToFill
Test screenshots
I hope that by applying the same logic to your context will you will be able to understand the exact situation in your case, and you can choose the best solution for your needs.

How to prevent Labels on bottom of window from disappearing when resizing?

Basically I have a window is that SizeToContent="WidthAndHeight" and I have a Label positioned on the bottom. When resizing the window the label disappears. Unless I make the window a lot bigger.
I have the following XAML code:
<Window x:Class="DateKeeper.View.Test"
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:DateKeeper.View"
mc:Ignorable="d"
Title="Test" SizeToContent="WidthAndHeight"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="label" Content="Label" HorizontalAlignment="Left" VerticalAlignment="Bottom" Grid.Column="0" Grid.Row="1" FontSize="36"/>
<TextBlock x:Name="textBlock" Grid.Column="1" Text="TextBlock" VerticalAlignment="Bottom" Grid.Row="1" FontSize="36"/>
</Grid>
</Window>
And here are the pictures:
Before Resizing
After resizing
SizeToWidthAndHeight sets the window's initial size. It doesn't prevent the user from resizing the window.
ResizeMode="NoResize" will prevent all resizing. If you want to set a minimum size and allow the window to be sized larger than that, you can't do that in pure XAML.
However, you can use that initial size as a minimum width and height with some simple code in a loaded handler, like so:
<Window
x:Class="DateKeeper.View.Test"
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:DateKeeper.View"
mc:Ignorable="d"
Title="Test"
SizeToContent="WidthAndHeight"
Loaded="Window_Loaded"
>
Test.xaml.cs (or whatever you call the codebehind for the above Window):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MinWidth = ActualWidth;
MinHeight = ActualHeight;
}
You could also write an attached property that would do the same thing.
Update
This pure-XAML approach with Mode=OneTime seems promising:
<Window
...
SizeToContent="WidthAndHeight"
MinHeight="{Binding ActualHeight, RelativeSource={RelativeSource Self}, Mode=OneTime, PresentationTraceSources.TraceLevel=High}"
MinWidth="{Binding ActualWidth, RelativeSource={RelativeSource Self}, Mode=OneTime}"
...
...but in trace output, we see that it gets the values of ActualHeight and ActualWidth very early on, before the window is sized to its content, perhaps before then actual UI window object is even created -- at a point when the actual width and height are both reported as zero.
You have 50% now
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="label" Content="Label" FontSize="36"
Grid.Row="1" Grid.Column="0" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
<TextBlock x:Name="textBlock" Text="TextBlock" FontSize="36"
Grid.Row="1" Grid.Column="1" VerticalAlignment="Bottom"/>
</Grid>
Not sure what you are trying to create, but three things:
The elements are losing visibility because they are going off of the screen because they are running out of room
Your layout puts both text elements in the second row of the Grid
a. The elements will only have 50% of the available height to render
b. FontSize and Margins/Padding will further offset this
You have two different text element types (Label and TextBlock)
a. Both have different Margin/Padding values
b. The different layout values will result in different levels of visibility
If you don't want the elements to lose visibility, you need to set a minimum size of the Window (e.g. 325x150).

Using MVVM in ListView on checking a checkbox show a label and on unchecking checkbox hide a label in the selected row - wpf

In my wpf application I am using MVVM. In ListView in selected row I want to show/ hide a label on checking/unchecking of a checkbox in the same selected row.
You should use the built-in BooleanToVisibilityConverter, and bind it to the Checkbox's IsChecked Property. See below for a simple example.
<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">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<CheckBox x:Name="myCheckBox" Content="CheckBox" VerticalAlignment="Center" HorizontalAlignment="Center">< /CheckBox>
<TextBlock Text="Label" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"
Visibility="{Binding IsChecked, ElementName=myCheckBox, Converter={StaticResource BoolToVis}}"></TextBlock>
</Grid>
</Window>

Why is my wpf grid not being contained in its container?

I have a user control and within it I have two controls: 1) a search criteria control (custom user control) and 2) a Telerik RadGridView.
Apart from that I have a window that is the shell for my application and in it exists three regions. When this window opens I click a button and the user control mentioned above gets inserted into one of the regions. Now that the user control is loaded I edit my search criteria and click the button to populate my grid with around 1,000 records. The problem is that the grid's scroll bar doesn't become enabled because the grid control maximizes to fit the size of all the data which runs past the screen.
The only way this works properly is to set the Height property of the grid to an absolute value but then the grid doesn't size with the window.
I've tried everything that I can think of: When in the grid I set the row def size to 200*, I put the grid in a wrap panel, I put it in a dockpanel, set the VerticalAlignment to stretch, changed the dock panel to stack panel.
Shell:
<Window x:Class="WTS.CGCApplicationInterface.CGCApplicationShellView"
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:cal="http://www.codeplex.com/CompositeWPF"
xmlns:inf="clr-namespace:WTS.CGCApplicationInterface.Infrastructure;assembly=WTSCGCApplicationInterface.Infrastructure"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Width="750" Height="480"
Title="CGC Interface Window">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="200*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl cal:RegionManager.RegionName="MenuRegion" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" />
<ItemsControl cal:RegionManager.RegionName="SwitchboardRegion" Grid.Column="0" Grid.Row="1" />
<ItemsControl cal:RegionManager.RegionName="MainRegion" Grid.Column="1" Grid.Row="1" />
</Grid>
User Control:
<UserControl x:Class="WTS.CGCApplicationInterfaceMVVM.Views.Oncor.OncorTimesheetCGC"
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:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:wtsControls="clr-namespace:WTS.CGCApplicationInterface.UserControls;assembly=WTSCGCApplicationInterface.Infrastructure"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600" Background="Red">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="200*" />
</Grid.RowDefinitions>
<wtsControls:CGCQueryUserControl Grid.Row="0"/>
<telerik:RadGridView ItemsSource="{Binding Path=Timesheets}" AutoGenerateColumns="False" Grid.Row="1"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<telerik:RadGridView.Columns>
<telerik:GridViewToggleRowDetailsColumn></telerik:GridViewToggleRowDetailsColumn>
<telerik:GridViewDataColumn Header="MyField1" UniqueName="AssociatedObject.MyField1" IsVisible="False" />
<telerik:GridViewDataColumn Header="MyField2" UniqueName="AssociatedObject.MyField2" IsVisible="False" />
I wish I could get points for answering my own question. I do realize that this could be taken advantage of. That said, in my shell, I changed the ItemsControl region to a ContentControl and it works perfectly.

Resources