get parent-note in treeview when the child-items are the same object

耗尽温柔 提交于 2021-02-11 14:21:43

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!