WPF Listbox is unbounded by the window - wpf

I'm working on a complex application, and I'm having an issue with a listbox not being bounded by the window height. Here's a simplified version of what it looks like. How would I get this listbox to correctly be bounded by the window? Right now, the bottom scroll button is off the screen and cannot be seen until the window is big enough to fit the entire listbox. I need to find a solution that makes the listbox always bounded because I have to implement zooming via a ScaleTransform.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Name="stack">
<Grid.LayoutTransform>
<ScaleTransform
ScaleX="{Binding ElementName=slider, Path=Value}"
ScaleY="{Binding ElementName=slider, Path=Value}" />
</Grid.LayoutTransform>
<WrapPanel HorizontalAlignment="Left">
<Expander IsExpanded="False" Width="300">hey</Expander>
<Expander IsExpanded="True" VerticalAlignment="Stretch" ClipToBounds="True">
<Grid>
<ListBox >
<Button>hey</Button>
<!-- just add a lot more of these to see the problem -->
<Button>hey</Button>
</ListBox>
</Grid>
</Expander>
</WrapPanel>
</Grid>
<Slider Grid.Column="1" Name="slider" Minimum="1" Maximum="4" Value="1" />
</Grid>

try the following code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="0">
<Grid Name="stack">
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=slider, Path=Value}"
ScaleY="{Binding ElementName=slider, Path=Value}" />
</Grid.LayoutTransform>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0">
<Expander IsExpanded="False"
Width="300">hey</Expander>
</Border>
<Border Grid.Row="1">
<Expander IsExpanded="True"
VerticalAlignment="Stretch"
ClipToBounds="True">
<Grid>
<ListBox>
<Button>hey</Button>
<Button>hey</Button>
<Button>hey</Button>
<Button>hey</Button>
<Button>hey</Button>
<Button>hey</Button>
<!-- just add a lot more of these to see the problem -->
<Button>hey</Button>
</ListBox>
</Grid>
</Expander>
</Border>
</Grid>
</Grid>
</Border>
<Border Grid.Row="1">
<Slider Name="slider"
Minimum="1"
Maximum="4"
Value="1" />
</Border>
</Grid>

Related

User control inside ScrollViewer doesn't scroll in WPF

I have the following code (abbreviated) in my main window:
Although I set both scroll bar visibilities and CanContentScroll properties it doesn't scroll. I assume it has to do with my user control.
<Window>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TabControl Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" Margin="0" VerticalAlignment="Stretch" Height="Auto" Width="Auto">
<TabItem Header="TEST">
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<my:MY_USER_CONTROL x:Name="myUserControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</TabItem>
</TabControl>
<Button Grid.Column="0" Grid.Row="2" >a button</Button>
<WrapPanel Grid.Column="0" Grid.Row="3" >
</WrapPanel>
</Grid>
</Window>
Abbreviated structure of my user control:
<UserControl>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="183*" />
<ColumnDefinition Width="117*" />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Children}" Grid.ColumnSpan="2">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.Children>
<TextBlock Background="LightGray" Padding="2" Margin="2" Grid.Row="0" Grid.Column="0" Text="{Binding Name}" />
<TextBlock Padding="2" Margin="2" Grid.Row="0" Grid.Column="1" Text="{Binding Content}" />
</Grid.Children>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</UserControl>
You need to set it like this. I changed the RowDefinition for Row0 to Height="*" So it will use as much space it can. Then changed place between the ScrollViewer and the TabControl. So the TabControl is a content of the ScrollViewer.
<Window>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Grid.Column="0" Grid.Row="0">
<TabControl HorizontalAlignment="Stretch" Margin="0" VerticalAlignment="Stretch"
Height="Auto" Width="Auto">
<TabItem Header="TEST">
<my:MY_USER_CONTROL x:Name="myUserControl"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.CanContentScroll="True" />
</TabItem>
</TabControl>
</ScrollViewer>
<Button Grid.Column="0" Grid.Row="2" >a button</Button>
<WrapPanel Grid.Column="0" Grid.Row="3" >
</WrapPanel>
</Grid>
When you set CanContentScroll to True, the ScrollViewer assumes that your content implements IScrollInfo (which i guess doesn´t).
Try setting CanContentScroll on the ScrollViewer to false, this allows the content to use as much space as it wants and the ScrollViewer takes care of scrolling.
However, depending on the size, number of visuals etc. of your control, this might become a performance issue (e.g. no UI Virtualization when CanContentScroll is set to False).
Looks like the content inside your scrollviewer is the same size as the viewer so there is nothing to scroll?
if you do something like
<ScrollViewer HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Grid Background="Red">
</Grid>
</ScrollViewer>
Then the grid is the same size as the scroll viewer and will never allow scrolling, if you set the height of the grid to more than the viewer can display you'll get a scroller.
<ScrollViewer HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Grid Background="Red" Height="500">
</Grid>
</ScrollViewer>

How to Use Template for Wpf DataGrid?

I have a template for datagrid like this:
<ControlTemplate TargetType="{x:Type DataGrid}" x:Key="zoomableControl">
<ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid
<ContentPresenter>
</ContentPresenter>
<Slider Name="zoomSlider" Minimum="1" Maximum="100" Visibility="Hidden"/>
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=Value,ElementName=zoomSlider}" ScaleY="{Binding Path=Value,ElementName=zoomSlider}"/>
</Grid.LayoutTransform>
</Grid>
</ScrollViewer>
</ControlTemplate>
and use it like this:
<DataGrid Template="{StaticResource zoomableControl}" ...>
I want to show datagrid in ScrollViewer inside the template, but it doesn't show anything. What's the problem?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ContentPresenter >
</ContentPresenter>
<Slider Name="zoomSlider" Minimum="1" Maximum="100" Visibility="Hidden" Grid.Row="1"/>
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=Value,ElementName=zoomSlider}" ScaleY="{Binding Path=Value,ElementName=zoomSlider}"/>
</Grid.LayoutTransform>
</Grid>
Your ContentPresenter and Slider are in same row of Grid so the ContentPresenter is not displayed as Slider overrides over it.I hope this will help.

LabeledPieChart: Style overrides DataTemplate

I have customized the template of LabeledPieChart (source: Bea) in a Style. Then I created a DataTemplate for the Slices of the LabeledPieChart. Both the style and the DataTemplate works great if they are alone. If they are together the Style overrides the DataTemplate that he has no effect. Is it possible to combine these two?
Here is my Code (The Style and the DataTemplate are in Window.Resources):
Style:
<Style TargetType="customControls:LabeledPieChart" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="customControls:LabeledPieChart">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*" />
<ColumnDefinition Width="9*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<dataVis:Title Content="{TemplateBinding Title}" Style="{TemplateBinding TitleStyle}" Margin="0,5,0,3"/>
<Grid Grid.Row="1" Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<chartPrmtvs:EdgePanel x:Name="ChartArea" Style="{TemplateBinding ChartAreaStyle}">
<Grid Canvas.ZIndex="-1" Background="Transparent" />
<Border Canvas.ZIndex="10" BorderBrush="#FF919191" BorderThickness="0" />
</chartPrmtvs:EdgePanel>
</Grid>
</Grid>
<dataVis:Legend x:Name="Legend" Style="{TemplateBinding LegendStyle}" Title="{TemplateBinding LegendTitle}"
BorderThickness="0" Background="Transparent" FontSize="15" Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
DataTemplate:
<DataTemplate DataType="{x:Type local:City}" >
<Border BorderThickness="1" BorderBrush="Gray">
<StackPanel Background="White" Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type customControls:PieChartLabel}}, Path=FormattedRatio}" VerticalAlignment="Center" Margin="5,0,5,0" />
<TextBlock Text="- " />
<TextBlock Text="{Binding Path=Population}" Margin="5,0,5,0" />
<TextBlock Text="- " />
<TextBlock Text="{Binding Name}" Margin="0,0,5,0"/>
</StackPanel>
</Border>
</DataTemplate>
LabeldPieChart:
<Grid>
<customControls:LabeledPieChart
x:Name="labeledPieChart"
Title="Population of Puget Sound Cities"
Height="500" Width="700"
Grid.Row="3"
BorderBrush="Gray"
>
<customControls:LabeledPieChart.Series>
<customControls:LabeledPieSeries
x:Name="labeledPieSeries"
ItemsSource="{Binding}"
IndependentValuePath="Name"
DependentValuePath="Population"
IsSelectionEnabled="True"
LabelDisplayMode="Auto"
/>
</customControls:LabeledPieChart.Series>
</customControls:LabeledPieChart>
</Grid>

Style each datagrid column header

I want to add another row to the HeaderRow and for each column insert specific control.
This is my code where add this row, but I can only set same control (in this case textbox) for every column, but I want to set specific control for specific column.
(like it's done in ASP.NET repeater)
<Style x:Key="DataGridColumnHeaderStyle1" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid x:Name="grid" VerticalAlignment="Center" HorizontalAlignment="Stretch" Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="ColumnHeader" Grid.Row="0" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="18" />
<Grid Grid.Row="1">
<TextBox Text="" HorizontalAlignment="Stretch" BorderThickness="0,1,1,1" />
</Grid>
</Grid>
</Style>
You can Make use of the HeaderTemplate to specify different datatemplates for each Column
Sample
<Window.Resources>
<DataTemplate x:Key="Template1">
<Grid x:Name="grid" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="ColumnHeader" Text="Student Id" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Height="18" />
<Grid Grid.Row="1">
<TextBox Text="" HorizontalAlignment="Stretch" BorderThickness="0,1,1,1" />
</Grid>
</Grid>
</DataTemplate>
<DataTemplate x:Key="Template2">
<Grid x:Name="grid" VerticalAlignment="Center" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="ColumnHeader" Text="Student Name" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center" Height="18" />
<Grid Grid.Row="1">
<TextBox Text="" HorizontalAlignment="Stretch" BorderThickness="0,1,1,1" />
</Grid>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid Name="dataGrid" >
<DataGrid.Columns>
<DataGridTextColumn HeaderTemplate="{StaticResource Template1}" Width="90*" MinWidth="120" Binding="{Binding StudentId}"/>
<DataGridTextColumn HeaderTemplate="{StaticResource Template2}" Width="90*" MinWidth="120" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Score" Width="100*" MinWidth="150" Binding="{Binding Score}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
I approached this a little bit different, I styled DataGridColumnHeadersPresenter, it allows me to syle the column headers row (instead of column header).
There I add my new row and all the controls I need, I alsoe set the width of every cell to match the width of it's header .
<Style x:Key="DataGridColumnHeadersPresenterStyle1" TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False"/>
<ItemsPresenter/>
<Grid Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ActualWidth, ElementName=Col1}"/>
<ColumnDefinition Width="{Binding ActualWidth, ElementName=Col2}"/>
<ColumnDefinition Width="{Binding ActualWidth, ElementName=Col3}"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Bottom" Width="Auto" Height="25" BorderThickness="1,2,0,2"/>
my other controls go here
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>

Decorating UserControl WPF

I want to decorate some controls in groups like:
<UserControl x:Class="Infrastructure.UI.ItemsGroup" ... >
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Background="Red">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" />
<TextBlock x:Name="ctrlGroupText" Grid.Row="1" HorizontalAlignment="Center" />
</Grid>
</Border>
</UserControl>
And use it in other XAML-files like:
<Grid Grid.Column="0">
<UI:ItemsGroup GroupText="Hello World">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button>1111</Button>
<Button>1111</Button>
</Grid>
</UI:ItemsGroup>
</Grid>
But it doesn't work. What did I wrong? :)
Thanks
You need to edit the Template for the UserControl instead of adding the Border as the Child
<UserControl x:Class="Infrastructure.UI.ItemsGroup" ... >
<UserControl.Template>
<ControlTemplate TargetType="{x:Type UserControl}">
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="5" Background="Red">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" />
<TextBlock x:Name="ctrlGroupText" Grid.Row="1" HorizontalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Update
To set the Text for the TextBlock to GroupText you can use a Binding
<TextBlock x:Name="ctrlGroupText"
Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ItemsGroup}},
Path=GroupText}"
Grid.Row="1"
HorizontalAlignment="Center" />

Resources