Scrollable set of elements in WPF - wpf

I have a set of items in ItemsControl. I am displaying all of them but I want to make visible only a subset of items. So I kind of want to specify the visible area of ItemsControl (or any other element which supports this). Other elements could be seen after scrolling is applied.
I could do it on ViewModel-side and pass to ItemsControl only visible elements but I am interested in View-only solution. Is there any?

You can add a ScrollViewer in your ItemsControl's style, then if your items will overflow the width or height of the ItemsControl a ScrollBar will appear.
<Style x:Key="ItemsControlStyle1" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

How to bind to an element inside a ControlTemplate?

I've created two custom controls in wpf, Control_A and Control_B. Both of them define a ColorProperty. Control_A's ControlTemplate consists of a Control_B instance,
<ControlTemplate
TargetType="{x:Type Control_A}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Control_B />
</Border>
</ControlTemplate>
What I want is to bind A.Color (target) to B.Color (source). How can this be achieved in XAML?
It can be done with a TwoWay binding. This way has it's dissadvantages but it works,
<ControlTemplate
TargetType="{x:Type Control_A}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Control_B
Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Color,
Mode=TwoWay}"/>
</Border>
</ControlTemplate>
I would try to give Control_B a Name and bind to its color.
Color="{Binding ElementName=Foo, Path=Color}"

DisplayMemberPath not working for ListBox with custom ListBoxItem control

I've configured ListBox to use custom ControlTemplate for it self and custom ControlTemplate for ListBoxItem. Unfortunately such change disables the default behvaiour of DisplayMemberPath property.
// resource dictionary of ListBox control template
<ControlTemplate x:Key="CustomListBox" TargetType="ListBox" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:s="clr-namespace:System;assembly=mscorlib">
<Border CornerRadius="3" BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="1,1,1,1" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="Bd" SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
//trigger code omitted
<Style TargetType="ListBox">
<Setter Property="Control.Template" Value="{StaticResource CustomListBox}"></Setter>
</Style>
// resource dictionary of ListBoxItem control template
<ControlTemplate x:Key="CustomListBoxItem" TargetType="ListBoxItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Border BorderThickness="0" CornerRadius="3" Padding="6 6" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="White" Name="Bd" SnapsToDevicePixels="True">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Border>
<Label Height="3"></Label>
</StackPanel>
</ControlTemplate>
// trigger code omitted
<Style TargetType="ListBoxItem">
<Setter Property="Control.Template" Value="{StaticResource CustomListBoxItem}"></Setter>
<Setter Property="ItemsControl.DisplayMemberPath" Value="MatchText"></Setter>
</Style>
XAML
<ListBox x:Name="listBox" DisplayMemberPath="MyCustomText">
The DataContext of listBox is set using code. If I omit setting CustomListBoxItem as the control template, than it works as expected. How do you enable DisplayMemberPath when using custom control templates for ListBox and ListBoxItem?

scrollbar appears wrong on ItemsCotrol of a grid

I have Itemscontrol in the 3rd column of my grid which shows some set of buttons which gets loaded dynamically.
I wanted these contents (i.e. buttons) to occupy maximum width of Grid. and when contents exceeds Grid size, it will show vertical scrollbar.
I applied Scrollbar style to ItemsControl as follows :
<Style x:Key="ItemControlStyle" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border>
<ScrollViewer HorizontalContentAlignment="Stretch"
CanContentScroll="True"
HorizontalScrollBarVisibility="Disabled"
Uid="ScrollViewer_9"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter Margin="{TemplateBinding Padding}"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Uid="ItemsPresenter_5" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have also applied HorizontalAlignment and VerticalAllignMent as "Stretch" for both ItemsControl as well as its parent i.e. Grid.
Output view I want is( 3rd column of grid)
Output I am getting is :
Scrollbar should appear after size exceeds
How to adjust these contents horizontally to Grid's max width?
Is it just a case of adding HorizontalAlignment=Stretch to ScrollViewer?
ie:
<Style x:Key="ItemControlStyle" TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border>
<ScrollViewer HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
CanContentScroll="True"
HorizontalScrollBarVisibility="Disabled"
Uid="ScrollViewer_9"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter Margin="{TemplateBinding Padding}"
KeyboardNavigation.DirectionalNavigation="Cycle"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Uid="ItemsPresenter_5" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

wpf itemscontrol items outside of control

I have an own control which derives from itemscontrol with an own template. I am using a Canvas inside the itemscontrol as ItemsPanel. Why f.e. on resize of the window the items also can be outside of the itemscontrol?
Templates:
<Style TargetType="{x:Type local:Dashboard}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Dashboard}">
<Grid>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
The items use this:
<Style TargetType="{x:Type local:Widget}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Widget}">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderThickness="2" BorderBrush="Black" Background="WhiteSmoke"
x:Name="Part_Header">
<ContentPresenter ContentSource="Header"/>
</Border>
<Border Grid.Row="1" BorderThickness="2" BorderBrush="Black" Background="WhiteSmoke">
<Grid>
<ContentPresenter />
<ResizeGrip x:Name="Part_Resize"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Depending on your alignment and margin settings, resizing a parent can cause children to move outside of a parent's boundaries. The easiest way I've found to check this is to load Blend and resize the parent, watching how contained controls move. By tweaking the anchors in Blend (which changes alignments and margins), you should be able to troubleshoot why they move.

Window.Margin & Window.Padding don't work

I am setting peroperty Margin and Padding of a window and it doesn't take effect:
Here is an example:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
Padding="22"
Margin="22">
<Grid>
<Label
FontWeight="Bold"
FontSize="36"
BorderThickness="1"
BorderBrush="Red"
Content="Hello world!"/>
</Grid>
</Window>
Result:
The desired result is that the red frame of the lable should be away 44px from the window's frame (margin+padding).
Yes, I know I can set the margin of the label, but that's not what I want.
I have a whole project that all its windows are set to a style, I want to set this properties (or other) in the general window style.
I guess if I won't find any solution I will create a named style for greed where I will set the margin/padding, then I will go Window by window and set the Grid's style, but that's the last option I wanna do.
Thanks in advance.
It's not surprising that Margin doesn't work, because Margin is the amount of space to be placed around the control. For a Window, this would mean making the frame smaller (and offset), not the client area, and that would be a bit strange (and might not play nicely with the Win32 hosting environment, not sure). It is a bit surprising that Padding doesn't work, and I'm not sure why that would be.
However, there is a workaround which you can encapsulate in a style: replace the default Window ControlTemplate with your own template that does respect the Padding:
<ControlTemplate TargetType="Window">
<Border Background="White" Padding="{TemplateBinding Padding}">
<ContentPresenter />
</Border>
</ControlTemplate>
(You would probably want the Border Background to be the dynamic window background brush for production code, but you get the idea.)
Obviously you can put this template in a style Template setter so as to avoid having to repeat it on each Window.
Here is the full template (generated with Microsoft Expression):
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="{TemplateBinding Margin}"
Padding="{TemplateBinding Padding}">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="ResizeMode" Value="CanResizeWithGrip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
<ResizeGrip
x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
IsTabStop="false"
Visibility="Collapsed"
/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition
Property="ResizeMode"
Value="CanResizeWithGrip"
/>
<Condition
Property="WindowState"
Value="Normal"
/>
</MultiTrigger.Conditions>
<Setter
Property="Visibility"
TargetName="WindowResizeGrip"
Value="Visible"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Here's a simple alternative: just set a background colour on your Window and the Margin on the Grid within your Window:

Resources