问题
i have a TreeView with same in xaml createt TreeViewItems. And one note has a ObservableCollection as ItemSource. This works like a Charm. But now i want same Notes to every item of the list (for better organization). So i do this:
This is my HierarchicalDataTemplate for the liste
<HierarchicalDataTemplate DataType="{x:Type classes:Connection}" ItemsSource="{Binding Source={StaticResource ConnectionChilds}}" >
<TextBlock Text="{Binding DisplayName}" />
</HierarchicalDataTemplate>
And the ItemsSource:
<collections:ArrayList x:Key="ConnectionChilds">
<classes:TreeItemObject ItemsSourcePath="Child1" />
<classes:TreeItemObject ItemsSourcePath="Child2" />
<classes:TreeItemObject ItemsSourcePath="Child3" />
</collections:ArrayList>
TreeItemObject is a simple Class:
public class TreeItemObject
{
public string ItemsSourcePath { get; set; }
}
And last but not least HierarchicalDataTemplate for TreeItemObject:
<DataTemplate DataType="{x:Type classes:TreeItemObject}">
<TextBlock Margin="5,0" Text="{Binding Path=ItemsSourcePath}"/>
</DataTemplate>
Looked like this
Connection 1
Child1
Child2
Child3
Connection 2
Child1
Child2
Child3
Connection 3
Child1
Child2
Child3
Works perfekt. But now if i select "Connection 2\Child3" i got the same object like "Connection 1\Child3" or "Connection 3\Child3". Ok make sense because based on same object. With that situation i have no chance to find out the parent-note on OnSelectedItemChanged.
Because if i search with this extension-Class. I only get the first expanded Connection-Note.
http://social.msdn.microsoft.com/Forums/silverlight/en-US/84cd3a27-6b17-48e6-8f8a-e5737601fdac/treeviewitemcontainergeneratorcontainerfromitem-returns-null?forum=silverlightnet
Is there a way to find the real parent in the TreeView?
回答1:
I personally do not like the idea of creating clones within a converter but I do not know the full scope of your problem. So working with what you have presented here, we can achieve the assignment of a parent to each TreeItemObject via a MultiValueConverter.
WPF has an awesome feature called MultiBinding. It processes 1 or more source values into a single target. To do this, it needs a multivalue converter.
So, change the TreeItemObject to
public class TreeItemObject
{
public string ItemsSourcePath { get; set; }
public WeakReference Parent { get; set; }
}
The hierarchicalDataTemplate for the Connection type would become
<HierarchicalDataTemplate DataType="{x:Type classes:Connection}">
<HierarchicalDataTemplate.ItemsSource>
<MultiBinding Converter="{StaticResource items2Clones}">
<Binding Source="{StaticResource ConnectionChilds}" />
<Binding />
</MultiBinding>
</HierarchicalDataTemplate.ItemsSource>
<TextBlock Text="{Binding DisplayName}" />
</HierarchicalDataTemplate>
Based on the above binding, to set the parent in the converter, the Convert method in your convert will be along the lines
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var destinationItems = new Collection<TreeItemObject>();
var items = values[0] as Collection<TreeItemObject>;
var parent = values[1] as Connection;
// null checks are required here for items and parent
foreach (var item in items)
{
var destinationItem = item.Clone(); // Assumed extension method
destinationItem.Parent = new WeakReference(parent);
destinationItems.Add(destinationItem);
}
return destinationItems;
}
Finally, the SelectedItemChanged event handler would be something like
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var item = (TreeItemObject)e.NewValue;
if ((item != null) && (item.Parent != null) && (item.Parent.IsAlive))
{
// do stuff - Console.WriteLine(((Connection)item.Parent.Target).DisplayName);
}
}
I have removed exception management and some null checking for brevity.
I hope this helps
回答2:
I think your only choice is to clone your children before you add them to the TreeView, allowing at least a binary difference between the child nodes.
If you do this, instead of handling the OnSelectedItemChanged event and traversing the object graph, add a WeakReference of the parent to each of its children. This will allow you to immediately reference the parent from the child and also allow .Net to clean up the object graph correctly.
An example of using a WeakReference property from a SelectedItemChanged event handler is as follows
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var treeView = sender as TreeView;
var item = treeView.SelectedItem as TreeItemObject;
if (item.Parent.IsAlive)
{
var parent = item.Parent.Target;
}
}
I have removed exception management and null checking for brevity.
I hope this helps.
回答3:
It's difficult to get the parent from the treeView item so what i did is that, i had a member property of the parent of the parent type which holds the reference to the parent as below
public class FileSystem :NotifyChange, IEnumerable
{
#region Private members
private ObservableCollection<FileSystem> subDirectoriesField;
#endregion
#region Public properties
/// <summary>
/// Gets and sets all the Files in the current folder
/// </summary>
public ObservableCollection<FileSystem> SubDirectories
{
get
{
return subDirectoriesField;
}
set
{
if (subDirectoriesField != value)
{
subDirectoriesField = value;
RaisePropertyChanged("SubDirectories");
}
}
}
/// <summary>
/// Gets or sets name of the file system
/// </summary>
public string Name
{
get;
set;
}
/// <summary>
/// Gets or sets full path of the file system
/// </summary>
public string FullPath
{
get;
set;
}
/// <summary>
/// object of parent, null if the current node is root
/// </summary>
public FileSystem Parent
{
get;
set;
}
public FileSystem(string fullPath, FileSystem parent)
{
Name = fullPath != null ? fullPath.Split(new char[] { System.IO.Path.DirectorySeparatorChar },
StringSplitOptions.RemoveEmptyEntries).Last()
FullPath = fullPath;
Parent = parent;
AddSubDirectories(fullPath);
}
public IEnumerator GetEnumerator()
{
return SubDirectories.GetEnumerator();
}
private void AddSubDirectories(string fullPath)
{
string[] subDirectories = Directory.GetDirectories(fullPath);
SubDirectories = new ObservableCollection<FileSystem>();
foreach (string directory in subDirectories)
{
SubDirectories.Add(new FileSystem(directory, this));
}
}
}
And my viewModel is as below
public class ViewModel:NotifyChange
{
private ObservableCollection<FileSystem> directories;
public ObservableCollection<FileSystem> Directories
{
get
{
return directoriesField;
}
set
{
directoriesField = value;
RaisePropertyChanged("Directories");
}
}
public ViewModel()
{
//The below code has to be moved to thread for better user expericen since when UI is loaded it might not respond for some time since it is looping through all the drives and it;s directories
Directories=new ObservableCollection<FileSystem>();
Directories.Add(new FileSystem("C:\\", null);
Directories.Add(new FileSystem("D:\\", null);
Directories.Add(new FileSystem("E:\\", null);
}
}
Since each child knows it;s parent now you can traverse back, root node parent will be null
Xmal will have the following
<TreeView Grid.Row="1" Background="Transparent" ItemsSource="{Binding Directories}" Margin="0,10,0,0" Name="FolderListTreeView"
Height="Auto" HorizontalAlignment="Stretch" Width="300" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:FileSystem}" ItemsSource="{Binding SubDirectories}">
<Label Content="{Binding Path= Name}" Name="NodeLabel" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Hope this helps you
来源:https://stackoverflow.com/questions/20735152/get-parent-note-in-treeview-when-the-child-items-are-the-same-object