WPF : TreeView virtualization not working - wpf

What can stop a TreeView from virtualizing if the TreeView is set up as follows?
<TreeView
ItemsSource="{Binding}"
VirtualizingStackPanel.IsVirtualizing="True">
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.ItemContainerStyle>
<Style
TargetType="{x:Type TreeViewItem}">
<Setter
Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
I have one that is not virtualizing, when i expand the nodes (and use snoop to check) i have all of the TreeViewItems being created. I am wondering if there is some combination of containers that would prevent the TreeView from virtualizing its content. (like hosting it in a StackPanel for example)

The problem was with the styling. After some research we found that there was an unnamed style targeting the TreeView (i.e. one with DataType={x:Type TreeView} without an x:Key) and one targetting the TreeViewItem in our App.xaml (or equivalent) It was overriding the ControlTemplate for each respectively.
These styles did not have the triggers to set the ItemsPanel to a VirtualizingStackPanel and had no mention of any virtualization. When the styles are removed the TreeView works fine. Even though the local properties set the ItemsPanel and the VirtualizingStackPanel.Isvirtualizing="True" on the TreeView these properties were not being propogated to the TreeViewItems so the top level of the TreeView would virtualize whilst the sub categories would not (as their virtualization behaviour was dependant on the TreeViewItem)

Had the same problem. In my case, the initial size of the TreeView was not limited (TreeView is within a popup). Therfore, the virtualization panel initialized all controls for the first time.
Setting the MaxHeigt of the TreeView to 1000 solvend the problem.

Related

WPF TabControl: Children unloaded when other tab is selected

Is it possible to prevent WPF TabControl from unloading the children of a TabItem when selecting an other tab?
The problem I'm facing is similar to the one described here:
WPF TabControl - Preventing Unload on Tab Change?
The solution provided there seems to work only if the TabControl is data bound.
It doesn't work if you add TabItems:
<local:TabControlEx>
<TabItem Header="First Tab">
<TreeView ItemsSource="{Binding TreeNodes}" Unloaded="treeView_Unloaded">
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:NodeViewModel}">
<TextBlock Text="{Binding NodeName}" />
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</TabItem>
<TabItem Header="Second Tab">
<TextBlock Name="txText2">Second Text 2</TextBlock>
</TabItem>
</local:TabControlEx>
When you select "Second Tab", treeView_Unloaded is triggered.
Background:
In my real application, one of the TabItems contains a UserControl containing a data bound TreeView: TreeViewItem.IsSelected is bound to a property. Setting the Property bound to IsSelected selects the corresponding TreeViewItem. If the user switches to another tab, the TreeView is unloaded (removed from VisualTree). In that situation, setting IsSelected to true on any node not having a corresponding TreeViewItem causes the TreeView to misbehave - probably because the TreeView does not create a TreeViewItem for the node which should be selected, because the TreeView is not currently part of the visual tree. So what I want to achieve is that I can select any other node by setting IsSelected to true - even if the TreeView is currently on a non-visible tab. Moving the TreeView to some place outside the TabControl seems to resolve the problem - even if it is inside a panel which has visibility collapsed. So, visibility doesn't seem to be the problem, but the fact that the TreeView is not currently part of the visual tree.
The solution presented in the original question works if you override the control template based on the default control template of TabControl:
- Remove ContentPresenter
- Add a Grid named PART_ItemsHolder.
No more Unloaded events - no more trouble with the TreeView when it is on a non-selected TabItem.

How to bind PreviewMouseDown to FormattedText within an ItemsControl

In WPF using MVVM I would like to set a property in the view model to the displayed text when the mouse is clicked. That is I want the PreviewMouseDown event from the ItemsControl to set a property in the viewmodel.
In the following XAML, I am using an ItemsControl to display Strings from a FormattedText ObservableCollection. All goes well with the XAML below to display the FormattedText.
But, how can I bind a PreviewMouseDown to each of the generated items for the view model?
All my attempts to use DataTemplate within the ItemsControl ultimately lead to:
System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type;
XAML
<ItemsControl
ItemsSource="{Binding Strings}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas
Background="Transparent"
Width="{x:Static h:Constants.widthCanvas}"
Height="{x:Static h:Constants.heightCanvas}"
/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Adding
h:MouseBehaviour.PreviewMouseDownCommand="{Binding PreviewMouseDown}"
to the Canvas definition never results in the command being called and I can't add it in a DataTemplate.
Any help or better idea is appreciated.
as items in an ItemsControl are hosted in ContentPresenter so if you bind your command to the same it will be applied to the Item's in the ItemsControl
so for that purpose we can use a generic Style for ContentPresenter in the resources of ItemsControl or any parent container
eg
<Style TargetType="ContentPresenter">
<Setter Property="h:MouseBehaviour.PreviewMouseDownCommand"
Value="{Binding PreviewMouseDown}" />
</Style>
above example is based on assumption that PreviewMouseDown command is in the view model for each item, if the command is in the parent view model then you may perhaps use
<Style TargetType="ContentPresenter">
<Setter Property="h:MouseBehaviour.PreviewMouseDownCommand"
Value="{Binding PreviewMouseDown, RelativeSource={RelativeSource FindAncestor,AncestorType=ItemsControl}}" />
</Style>

Unable to drag drop items between bound itemscontrols

iv'e got several bound itemscontrols which all play the role of of drop target
i need to be able to drag drop items between these items controls .
the problem is that the items control's are not recognized as drop targets by the drag drop framework
The ItemsControl Panel :
<ItemsPanelTemplate x:Key="TopPipePanelTemplate">
<StackPanel></StackPanel>
</ItemsPanelTemplate>
The DataTemplate :
<DataTemplate x:Key="PipeDataItem" >
<Ellipse Width="45" Height="45" Fill="{Binding IsMine,Converter={StaticResource MyCheckerOwnerToColorConverter}}"></Ellipse>
</DataTemplate>
The ItemsControl Style :
<Style TargetType="{x:Type ItemsControl}" x:Key="ItemsControlStyle">
<Setter Property="ItemTemplate" Value="{StaticResource PipeDataItem}"></Setter>
<Setter Property="AllowDrop" Value="True"></Setter>
<EventSetter Event="Drop" Handler="ItemsControlDropTarget"></EventSetter>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ItemsControl_MouseLeftButtonDown"></EventSetter>
</Style>
** for this example lets take 2 of The ItemsControls :**
<ItemsControl ItemsSource="{Binding Path=Pipes[23].Checkers}" Style="{StaticResource ItemsControlStyle}"/>
<ItemsControl Grid.Column="1" ItemsSource="{Binding Path=Pipes[22].Checkers}" Style="{StaticResource ItemsControlStyle}"/>
when regularly displayed :
the empty ( Left ) one is not recognized as a drop target although it as AllowDrop set to True and
handles the Drop event ( as do all the itemscontrols in this screen , look at ItemsControl Style above )
now when i color the itemscontrol panel it s suddenly is recognized :
<ItemsPanelTemplate x:Key="TopPipePanelTemplate">
<StackPanel Background="AliceBlue"></StackPanel>
</ItemsPanelTemplate>
as if now it takes up the entire cell which it occupies ..
iv'e tried setting the panel to VerticalAlignment="Stretch" but this also had no affect
i'm trying to understand why my itemcontrols are not recognized as drop enabled ,
even though i expect that they take up that space , in addition the itemscontrol with the ellipses
is only recognized until the height of the ellipses that occupie it's content .
any ideas ?
just to clarify what i mean by Recognized as drop target is when you drag the ellipse you are able
to drop it on top of the itemscontrol .
for now iv'e just set the background as Transparent
The default background color of a panel doesn't exist, which means hit tests pass through it. To get it to register hit tests, such as mouse over events, you need to give it a background color. Usually I just use White, although you said Transparent also works and would be a better choice.
In addition, StackPanels will usually only take up the space they need. You might be better off using a Panel that stretches to fill all available space, such as a DockPanel with LastChildFill="False", and set DockPanel.Dock="Top" on your items

Silverlight - Add Ellipses to a canvas dynamically with MVVM

I want to add a dynamic number of ellipses to a canvas and set the ellipse position (canvas.top, canvas.left). I tried binding to an ItemsControl but it each item (ellipse) has a container, so I cant set the ellipses position directly. I don't want an items container, I just want a canvas that contains ellipses.
Can this be done?
Try this - worked for me -- I use it to freely place textblocks on a canvas.
Re: Re: Positioning Items when Canvas is the ItemsPanel of an ItemsControl
02-26-2010 7:17 AM |
There is an alternative simpler solution that does work in silverlight 3.
<Canvas>
<ItemsControl ItemsSource={Binding MyItems}>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<TextBlock Canvas.Left={Binding Left} Canvas.Top={Binding Top} Text={Binding Text} />
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
If the MyItems is a list of items that are of a class that has Left, Top and Text public properties, this works fine. I have also tested with Line and Border to draw simple bar graph graphics in silverlight 3.
From the bottom of this post:
http://forums.silverlight.net/forums/p/29753/450510.aspx#450510
Combine it with a Silverlight DataTemplateSelector and you can change the objects you draw based on view model properties:
http://www.codeproject.com/KB/silverlight/SLTemplateSelector.aspx
Ordinarily I would say use an ItemsControl in conjunction with a Canvas:
<ItemsControl ItemsSource="{Binding Ellipses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemsContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
</Style>
</ItemsControl.ItemsContainerStyle>
</ItemsControl>
But in a display of Silverlight suckiness, the ItemContainerStyle property does not work on ItemsControl. It has support in ItemsControl, but it's not exposed by ItemsControl itself. Instead, it's up to subclasses of ItemsControl - such as ListBox - to expose it. Oh, and those subclasses have to be provided by Microsoft because the functionality is protected internal, so you can't just subclass ItemsControl and expose this stuff yourself. :S
So you could use ListBox instead, possibly by subclassing it and changing its item container to something simpler than a ListBoxItem. Or you could just use ListBox directly and fiddle around until the ListBoxItems look the way you want them to (i.e. not selected).

WPF TreeView with horizontal orientation?

What would be the bast way to change the orientation of the WPF treeview. I would like to work the expand-collapse-functionality to work left to right instead of top to down. I.e. when I click on on the expand button of a treenode I would its subnode to appear right of the parent and the indent should work top-down instead. Also the vertical lines that connect the node must now be horizontal.
Here is a great article by Josh Smith on CodeProject detaling exactly how to do this kind of thing.
To expand on John Smith's CodeProject article, if you want to have horizontal layout on only a particular level in the tree (instead of on all levels like his article shows), then just set the ItemsPanel property on the TreeViewItem at the level you want to have a StackPanel.
It wasn't intuitive to me at first, but you can get to this property through the ItemContainerStyle property of the HierarchicalDataTemplate for the layer above the layer you want to be horizontal.
Like this:
<ItemsPanelTemplate
x:Key="ItemsPanelForHorizontalItems">
<StackPanel
Orientation="Horizontal"/>
</ItemsPanelTemplate>
<HierarchicalDataTemplate
x:Key="DataTemplateForLayerAboveHorizontalItems"
DataType="{x:Type viewModel:ThingHavingHorizontalItems}"
ItemsSource="{Binding HorizontalItems}"
ItemTemplate="{StaticResource DataTemplateForLayerWithHorizontalItems}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style
TargetType="TreeViewItem">
<Setter
Property="ItemsPanel"
Value="{StaticResource ItemsPanelForHorizontalItems}"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<ContentControl
Content="{Binding}"
ContentTemplate="{StaticResource DataTemplateForThingHavingHorizontalItems}"/>
</HierarchicalDataTemplate>
Following this pattern will let you set horizontal layout for any individual layer within your tree, except the root layer. And if you want the root layer to be horizontal, then just set the ItemsPanel property on the TreeView to use a horizontal StackPanel.

Resources