Hi all!
I use a XamTreeMap with an ObservableCollection as source. I apply a filter to the ObservableCollection. Here I have the following two problems:
Is this behavior by design or am I on the track of a bug?
Best regards,
Peter
Hi Peter,
Just checking in whether you have that source code I asked about in my previous reply..?
Thanks,
Peter,
I am creating a sample to reproduce the issue, and it would be helpful if I can get the implementation of your "PerfLogTreeMapViewModel" class. Please post this code when you have a chance.
I believe Nikolay is busy with the upcoming release, so I will take a look at this and I will post back here when I have more information (or to ask further questions if needed).
Thanks for your patience,
Hello Nikolay,
here I have the relevant code snippets (removed Style):
Control XAML:
<UserControl x:Class="CGIAnalyzer.View.PerfLogTreeMapView" 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:ig="http://schemas.infragistics.com/xaml" xmlns:vm="clr-namespace:CGIAnalyzer.ViewModel" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <ig:XamTreemap x:Name="treeMap" ItemsSource="{Binding Path=AllRecordings}" NodeMouseLeftButtonDown="OnTreeMapNodeMouseLeftButtonDown" NodeMouseRightButtonDown="OnTreeMapNodeMouseRightButtonDown" DataContextChanged="OnTreeMapDataContextChanged"> <ig:XamTreemap.NodeBinders> <ig:NodeBinder TextPath="RecorderQualifiedName" ValuePath="RecordingDuration" TargetTypeName="RecordingViewModel" ItemsSourcePath="ChildRecordings" NodeStyle="{StaticResource RecorderNodeStyle}"/> </ig:XamTreemap.NodeBinders> <ig:XamTreemap.ValueMappers> <ig:ColorMapper ValueTypeName="RecordingViewModel" ValuePath="RecordingDuration" TargetProperty="Fill" MappingMode="AllNodes" From="#009900" To="#990000" /> </ig:XamTreemap.ValueMappers> </ig:XamTreemap> </Grid></UserControl>Control Code Behind:
/// <summary> /// Interaction logic for PerfLogTreeMapView.xaml /// </summary> public partial class PerfLogTreeMapView { // Stack for drill-down private readonly Stack<object> _drilledNodes = new Stack<object>(); public PerfLogTreeMapView() { InitializeComponent(); } /// <summary> /// Drill down one level to the node under the mouse pointer. /// </summary> /// <param name="sender">Source of the event.</param> /// <param name="e">Further event information.</param> private void OnTreeMapNodeMouseLeftButtonDown(object sender, TreemapNodeClickEventArgs e) { //Check if the sender node is actually a different node from the ItemsSourceRoot if (treeMap.ItemsSourceRoot != e.Node.DataContext) { //Push the current ItemsSourceRoot in a stack _drilledNodes.Push(treeMap.ItemsSourceRoot); //Set the new ItemsSourceRoot treeMap.ItemsSourceRoot = e.Node.DataContext; } } /// <summary> /// Go up one level to the parent of the current highest level node. /// </summary> /// <param name="sender">Source of the event.</param> /// <param name="e">Further event information.</param> private void OnTreeMapNodeMouseRightButtonDown(object sender, TreemapNodeClickEventArgs e) { //Check if there are nodes in the stack if (_drilledNodes.Count > 0) { //Push the top node treeMap.ItemsSourceRoot = _drilledNodes.Pop(); } } private void OnTreeMapDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { if (e.OldValue != null) { var model = (PerfLogTreeMapViewModel)e.OldValue; model.AfterSamplingFilterChange -= OnBeforeSamplingFilterChange; } if (e.NewValue != null) { var model = (PerfLogTreeMapViewModel)e.NewValue; model.BeforeSamplingFilterChange += OnBeforeSamplingFilterChange; } } void OnBeforeSamplingFilterChange(object sender, SamplingFilterChangedEventArgs e) { var model = sender as PerfLogTreeMapViewModel; if (model == null) return; _drilledNodes.Clear(); if (treeMap.ItemsSourceRoot != null && treeMap.ItemsSourceRoot != model.AllRecordings) { treeMap.ItemsSourceRoot = model.AllRecordings; } } }
View model (base class):
/// <summary> /// Base class for all performance log views. /// </summary> internal abstract class PerfLogViewModel : WorkspaceViewModel { // The recordings to display. private ObservableCollection<RecordingViewModel> _recordings; /// <summary> /// The recording view models to be displayed. /// </summary> public ICollectionView AllRecordings { get { if (_recordings == null) { _recordings = CreateRecordings(); } return CollectionViewSource.GetDefaultView(_recordings); } } /// <summary> /// Sets the sampling filter for <see cref="AllRecordings"/> /// </summary> /// <param name="filter">Filter to use for sampling.</param> protected virtual void SetSampleFilter(Predicate<object> filter) { OnBeforeSamplingFilterChange(new SamplingFilterChangedEventArgs(filter)); AllRecordings.Filter = filter; OnAfterSamplingFilterChange(new SamplingFilterChangedEventArgs(filter)); } /// <summary> /// Removes the sampling filter for <see cref="AllRecordings"/> /// </summary> protected virtual void ResetSampleFilter() { OnBeforeSamplingFilterChange(new SamplingFilterChangedEventArgs(null)); AllRecordings.Filter = null; OnAfterSamplingFilterChange(new SamplingFilterChangedEventArgs(null)); } }
Hope, this is enough to get the general idea. In the derived view model, I have to do the filter assignments twice to get the desired effect.
This assignment in the controls code-behind has no effect, if a filter was set that lead to an empty result:
treeMap.ItemsSourceRoot = model.AllRecordings;
or
treeMap.ItemsSourceRoot = null;
Best Regards,
Hello Peter,
Can you post some sample code containing the xamTreemap and the filtering of the colleciton.
Nikolay