Your Privacy Matters: We use our own and third-party cookies to improve your experience on our website. By continuing to use the website we understand that you accept their use. Cookie Policy
2815
Creating datatemplate programmatically for xamDataTree causing {"A NodeLayout with this key is already in this collection."} exception
posted

I have created a DataTemplate as part of a resource of a user control with the following XAML:

<UserControl x:Class="XamDataTreeApp.SystemSettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="AppSettingsProviderDataTemplate">
<StackPanel Orientation="Horizontal">
<ContentPresenter Grid.Column="1" Content="{Binding Icon}" Margin="4,1,0,1" />
<TextBlock Grid.Column="2" Margin="4,1,0,1" FontWeight="Normal" Text="{Binding Alias, Mode=OneWay}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources><Border><ContentPresenter TreeViewContainer/></Border></UserControl>

and code behind.

public partial class SystemSettingsView
{
public SystemSettingsView(ITreeViewFactory treeViewFactory)
{
InitializeComponent();

Loaded += (sender, args) =>
{
if (TreeView == null)
{
var treeCreationCtx = new TreeViewCreationContext
{
Name = "SystemSettingsView",
DataContext = DataContext,
ItemSourceBinding = new Binding("SettingProviders"),
ItemDataTemplate = (DataTemplate)Resources["AppSettingsProviderDataTemplate"],
OnSelectionChanged = (selectedItem) =>
{
var systemSettingsViewModel = DataContext as SystemSettingsViewModel;
if (systemSettingsViewModel != null)
systemSettingsViewModel.SelectedSettingsProvider = (IAppSettingsProvider) selectedItem;
}
};

TreeView = treeViewFactory.GetTreeView(treeCreationCtx);
}
};
}

public FrameworkElement TreeView
{
get { return TreeViewContainer.Child as FrameworkElement; }
set
{
TreeViewContainer.Child = value;
}
}
}
}

When I attempt to programmatically create an instance of xamDataTree that users this template with the following constructs:

public FrameworkElement GetTreeView(TreeViewCreationContext creationContext)
{
if (creationContext == null)
throw new ArgumentNullException("creationContext");
if (creationContext.IsInvalid())
throw new InvalidDataException("creation context is not valid");

var tree = new XamDataTree()
{
Name = creationContext.Name,
NodeLineVisibility = Visibility.Visible,
DataContext = creationContext.DataContext
};

tree.SetBinding(XamDataTree.ItemsSourceProperty, creationContext.ItemSourceBinding);

if (creationContext.SelectedItemBinding != null)
tree.SetBinding(XamDataTree.SelectedDataItemsProperty, creationContext.SelectedItemBinding);

if (creationContext.ItemDataTemplate != null)
{
var nodeLayout = new NodeLayout();
nodeLayout.ItemTemplate = creationContext.ItemDataTemplate;
tree.GlobalNodeLayouts.Add(nodeLayout);
}

if (creationContext.OnSelectionChanged != null)
{
tree.SelectedNodesCollectionChanged += (sender, args) => creationContext.OnSelectionChanged.Invoke(tree.SelectedDataItems.First());
}

return tree;
}

public class TreeViewCreationContext
{
public string Name { get; set; }
public object DataContext { get; set; }
public Binding ItemSourceBinding { get; set; }
public DataTemplate ItemDataTemplate { get; set; }
public Action<object> OnSelectionChanged { get; set; }
public Binding SelectedItemBinding { get; set; }

public bool IsValid()
{
return !IsInvalid();
}

public bool IsInvalid()
{
if (DataContext == null)
return true;
if (ItemSourceBinding == null)
return true;
if (ItemDataTemplate == null)
return true;

return false;
}
}

I get the following exception: "A NodeLayout with this key is already in this collection."

If I do the same thing using WPF native treeview as follows:

public FrameworkElement GetTreeView(TreeViewCreationContext creationContext)
{
if (creationContext == null)
throw new ArgumentNullException("creationContext");
if (creationContext.IsInvalid())
throw new InvalidDataException("creation context is not valid");

var tv = new TreeView { Name = creationContext.Name, DataContext = creationContext.DataContext };
tv.SetBinding(ItemsControl.ItemsSourceProperty, creationContext.ItemSourceBinding);

if (creationContext.ItemDataTemplate != null)
tv.ItemTemplate = creationContext.ItemDataTemplate;

if (creationContext.SelectedItemBinding != null)
tv.SetBinding(BindableSelectedItemBehavior.SelectedItemProperty, creationContext.SelectedItemBinding);

if (creationContext.OnSelectionChanged != null)
{
tv.SelectedItemChanged += (sender, args) => creationContext.OnSelectionChanged.Invoke(tv.SelectedItem);
}

var behaviors = Interaction.GetBehaviors(tv);
behaviors.Add(new BindableSelectedItemBehavior());
return tv;
}

I do not have this problem.

Any tips?