Using A WrapPanel in a TreeView - c#

I want to display data with level of detail, so i use a TreeView, but each detail is quite short, so i would like to use a WrapPanel (horizontal) to have many details per line.
Something like :
This is an unexpanded item
This is The Header of an expanded item
Info 1 Info 2 Info 3 Info 4
Info 5 Info 6 Info 7
So i tried defining TreeViewItem's Template but i could not get the wrappanel to
wrap. I have only one info per line, when info's datatemplate width is 100 and TreeView
is 500. i tried setting Width of WrapPanel, ItemsWidth, are other things with no success.
Any idea ?
EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And i still let the not working solution here, for completeness sake.
(Why wouldn't it work ???)
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="2" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
!!!! this is the wrapanel not wrapping
<ListBox Name="AllItems" Grid.Row="1" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ItemsPresenter />
</ListBox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter
TargetName="AllItems"
Property="Visibility"
Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
(Maybe a binding of the width (but which ?) would solve this)
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

You must set
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
to your
<TreeView/>
not for
<WrapPanel/>
example:
<TreeView x:Name="fieldTreeView" Grid.Row="1" Margin="5,0,5,0" Background="Beige"
ItemsSource="{Binding Source={StaticResource bla}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<TreeView.Resources>
<DataTemplate DataType="{x:Type Model:bla}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Name}"/>
<TextBox Text=""/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>
This solution works for me.

Try
Disable the scroll bars
Widen the WrapPanel and see the visual impact, is it effected?
Make a color border/background to track the actual size of the WrapPanel
These will help you trace the problem

Related

UniformGrid Grid.Row Grid.Column not being adhered to

Following on from this question (DataTemplateSelector for Uniform Grid binding?), I still am not getting my Buttons/Textblocks to adhere to the row and the column that they are bound to in the view model. I will only post the XAML as I know the row and column bindings are correct (Live Visual Tree tells me Row is 1 and Column is 1 for example, but on the grid it shows otherwise...).
If you need anymore code let me know. This is the small bit of XAML.
<Grid DockPanel.Dock="Left" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="800" Height="400">
<ItemsControl ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="objGrid" Grid.Row="1"
Rows="{Binding RowCount}"
Columns="{Binding ColumnCount}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type engine:ObjA}">
<Button Content="{Binding Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type engine:GridLabeller}">
<TextBlock Text="{Binding HeaderName}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
A UniformGrid in WPF doesn't care about the Grid.Row and Grid.Column attached properties. You need to create a Grid with RowDefinitions and ColumnsDefinitions for setting these properties to have any effect.
You may create a Grid programmatically in the view based on the values of the RowCount and ColumnCount source properties.

Xaml Listview Windows-Universal Horizontal Scroll Mode

I coded a list that has as an Itemsource an Observable Collection, which is of type Grids. So the listview is containing 4 Items (Grids). I want the Grids to be like horizontal next to each other, so I tried this in Xaml.
<StackPanel Orientation="Horizontal">
<ListView ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollMode="Enabled" ItemsSource="{Binding DropGrids, Source={StaticResource view}}" Height="100" x:Name="DropList" RenderTransformOrigin="0.5,0.5" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<AppBarButton Icon="MapPin" Label="Go!" HorizontalAlignment="Right"></AppBarButton>
</StackPanel>
But when I try scrolling the list to the left it is instantly going back (If you want to simulate is use WGA resolution 4 Inch Emulator) and I can't see the fourth Grid on the screen. How do I fix this?
The ListView.ItemsPanel property I got from this post:
Stackoverflow Post - Horizontal Mode
Not sure on the Grid part in your ListView itemsource, but I blogged about how to create a Horizontal ListView here
http://depblog.weblogs.us/2015/03/25/show-items-scrolling-horizontally-with-listview-in-winrt/
I hope this helps...
The complete style is set as
<Style x:Name="BaseListViewStyle" TargetType="ListView">
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

Binding to data breaking on VirtualizingStackPanel

First of all, a disclaimer, I'm working with .net 3.5's virtualizingstackpanel. If you get different behavior in future versions let me know. It's relatively simple to set up a test case with listviews that you can test this against.
I have an itemcontainer style in a virtualizingstackpanel that binds the property IsSelected to the viewmodel.
When I Select an unselected item in the view model that is off screen, and then scroll to that item, both the datacontext (viewmode) and the actual listviewitem have the IsSelected Property as true (expected behavior). The trigger is properly applied to the listviewitem highlighting it.
However, when I deselect the datacontext for an item that is not in view and then scroll down until the item is in view, upon reaching the item and creating it, the item's datacontext now has IsSelected = true and the listviewitem's IsSelected property is also true, so the listviewitem ends up with a selection rectangle from the trigger(incorrect behavior).
It's almost as if the ListViewItem's Properties are all restored on creation of the item ( this makes sense to me, but then they should bind the datacontext's values to the item afterward).
But that doesn't seem to be happening. Moreover, after failing to deselect the item and scrolling back to find it selected. If i then select/desect it, the binding has no effect on the item.
I can see no logical reason why it would work when selecting an item in the viewmodel that is offscreen and not when I Deselect an item offscreen. In both cases, the newly created item should need to be rebound to the current value of the viewmodel. However, one works and the other doesn't.
Edit:
Ah ok, so I just can't use recycling mode and binding it seems. Thanks #devhedgehog. Would give you the bounty but you need an answer. I swear I tried that earlier, but maybe I was not handling click events in the bound listview before so that I was breaking the binding on physical selection or something. I do remember attempting both modes at one point, but probably had something else interfering so it wouldn't work. Anyway it works now.
Since you mentioned it, I would like to preferably avoid keeping unnecessary code and inherit from virtualizingstackpanel instead of virtualizing panel. But I want to be able to set the horizontal scroll extent, which requires me to reimplement Iscrollinfo. However, I was having trouble getting virtualizingstackpanel to interact nicely with iscrollinfo.
<ListView
x:Name="TestLV"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Green"
ItemsSource="{Binding Path=AddedItems, Mode=OneWay}"
SnapsToDevicePixels="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="true"
ScrollViewer.IsDeferredScrollingEnabled="False"
Grid.Column ="4"
MouseDown="TestLV_MouseDown"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid
x:Name="SignalGrid"
Background="Transparent"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border
Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="PART_Header"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
/>
</Border>
<ItemsPresenter
x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="0"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="false">
<Setter
TargetName="SignalGrid"
Property="Background"
Value="Transparent"
/>
</Trigger>
<Trigger Property="IsSelected"
Value="true">
<Setter
TargetName="SignalGrid"
Property="Background"
Value="Navy"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
<!--<Components:VirtualizingTilePanel
ChildSize="{Binding Path=GraphHeight}"
/>-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate>
<Grid >
<ScrollViewer
>
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</ListView.Template>
<!--Template Defining the layout of items in this treeview-->
<ListView.Resources>
<HierarchicalDataTemplate
ItemsSource ="{Binding Path = bits}"
DataType="{x:Type ViewModels:BusViewModel}"
>
<Grid>
<Components:CenteredTextBlock
x:Name="CommentTextBlock"
Foreground="Black"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
>
<Components:CenteredTextBlock.MainText>
<MultiBinding Converter="{StaticResource StringConcatConverter}">
<Binding Path="Alias" />
<Binding Path="SignalValueAtPrimaryMarker" />
</MultiBinding>
</Components:CenteredTextBlock.MainText>
</Components:CenteredTextBlock>
</Grid>
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:BitViewModel}"
>
<Grid>
<Components:CenteredTextBlock
Foreground="Black"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}">
<Components:CenteredTextBlock.MainText>
<MultiBinding Converter="{StaticResource StringConcatConverter}">
<Binding Path="Alias" />
<Binding Path="SignalValueAtPrimaryMarker" />
</MultiBinding>
</Components:CenteredTextBlock.MainText>
</Components:CenteredTextBlock>
</Grid>
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:SelectableItemViewModel}"
>
<Grid>
<Components:CenteredTextBlock
Foreground="Red"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
MainText="{Binding Path = FullName}"
/>
</Grid>
</DataTemplate>
</ListView.Resources>
</ListView>
It seems strange that this question actually appears to have been answered, yet is listed as not having an answer. So I will post dev hedgehog's comment/answer here.
Please use standard VirtualizingStackPanel without your custom measure
logic. There is nothing you added special in your logic that
VirtualizingStackPanel cant do. And RecyclingMode should not be
Recycle but instead leave it out or change to Standard.

Getting a separator to fill up the remaining space

I'm sure this is dead simple, but I can't seem to figure it out.
I have a ListBox to display items, and these are displayed with a DataTemplate. I now want to group these items, so have added a group based on the manufacturer property. This is done in code behind.
ICollectionView view = CollectionViewSource.GetDefaultView(Items);
PropertyGroupDescription groups = new PropertyGroupDescription("Manufacturer");
view.GroupDescriptions.Add(groups);
I wanted to have each group in an expander, so they can be hidden. I have got this working by looking at GroupTemplates at MSDN This involves, having an expander, textblock and then a seperator to rule off the extra space like in Windows Vista/7 Groups. As Below.
The problem I am having is I cannot get the separator to fill up the remaining space correctly. If I use a MinWidth value, all my expanders have the same width. If I use the {binding ActualWidth, ElementName=MyListBox}, then the separator is too wide, as its as wide as the control that contains it. So it sets the scroll bars to be visible, (see screenshot below). If i leave width blank, then the seperator is not drawn at all.
My gut feeling is the stackpanel should have expanded the seperator to use the remaining space but it didn't. So i tried a DockPanel as in the XamlCode below, yet this also fails. I have a few other problems with getting controls to fill up the remaining space, by using a suitable width so if you can help me resolve this, it would be great.
My current WPF Xaml Markup. You will need to add elements to get this to display something.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="myStackPanel">
<ListBox x:Name="MyListBox">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<DockPanel HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Height="Auto"
Width="{Binding ActualWidth, ElementName=MyListBox}"
Margin="10">
<TextBlock DockPanel.Dock="Left" Margin="0" FontSize="14" FontWeight="Bold" Foreground="Black" Text="{Binding Path=Name}"/>
<Separator DockPanel.Dock="Right" Margin="4,0,4,0"></Separator>
</DockPanel>
</Expander.Header>
<ItemsPresenter Margin="5,0,0,0" />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- Data Template Here -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
It is in fact not trivial, the control template of the Expander consists of a ToggleButton as the Header and a ContentPresenter for the content. The problem is that the ToggleButton itself has a special style & template which contains the arrow that has the alignment hard-coded into it, the default one looks something like this:
<Style x:Key="ExpanderDownHeaderStyle"
TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Padding="{TemplateBinding Padding}">
<Grid Background="Transparent"
SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- ... -->
<!-- The HorizontalAlignment needs to be set to stretch here -->
<ContentPresenter Grid.Column="1"
Margin="4,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
SnapsToDevicePixels="True"
RecognizesAccessKey="True"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- ... -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
To get your style to work you need to modify the default Expander template (get default templates on MSDN - Default WPF Themes link). Not nice but you don't really have much of a choice.

align wpf tabcontrol strip

I'm trying to align a tabcontrol strip on the right.
Just to be clear - I want the tabs on the top (tabstripplacement), but aligned on the right.
The Headers for the TabItem's are located in a panel of type TabPanel. We can add HorizontalAlignment="Right" for it in the Resources of TabControl
<TabControl ...>
<TabControl.Resources>
<Style TargetType="TabPanel">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</TabControl.Resources>
<!--...-->
</TabControl>
I don’t know why, but ItemsPanel replacement doesn’t work. You must replace template for whole TabControl:
<TabControl ItemsSource="{Binding Items}">
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" HorizontalAlignment="Right" IsItemsHost="true"/>
<ContentPresenter ContentSource="SelectedContent"/>
</DockPanel>
</ControlTemplate>
</TabControl.Template>
<!-- This XAML doesnt work!-->
<!--<TabControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Right" IsItemsHost="True"/>
</ItemsPanelTemplate>
</TabControl.ItemsPanel>-->
</TabControl>

Categories

Resources