【问题标题】:Infinite recursion loop when binding TreeView control to filesystem将 TreeView 控件绑定到文件系统时的无限递归循环
【发布时间】:2014-10-09 16:21:09
【问题描述】:

我正在我的 WPF 应用程序中构建一个文件夹选择器对话框。我已经定义了以下类用作树中的节点:

public class FileSystemNode : ViewModelBase {

    protected static readonly FileSystemNode DummyNode = new FileSystemNode( null, false );

    public ObservableCollection<FileSystemNode> Children { get; private set; }

    public bool IsExpanded {
        get { return iIsExpanded; }
        set {
            SetAndNotify( "IsExpanded", ref iIsExpanded, value );

            // Expand all the way up to the root.
            if ( iIsExpanded && Parent != null )
                Parent.IsExpanded = true;

            // Lazy load the child items, if necessary.
            if ( HasDummyNode ) {
                Children.Remove( DummyNode );
                LoadChildren();
            }

        }
    }
    private bool iIsExpanded = false;

    public bool IsSelected {
        get { return iIsSelected; }
        set { SetAndNotify( "IsSelected", ref iIsSelected, value ); }
    }
    private bool iIsSelected = false;

    public bool HasDummyNode {
        get { return Children.Count == 1 && Children[ 0 ] == DummyNode; }
    }

    public virtual Uri Icon {
        get { return null; }
    }

    public string Name { get; protected set; }

    public FileSystemNode Parent { get; protected set; }

    public string Path { get; protected set; }
    public FileSystemNode( FileSystemNode theParent, bool lazyLoadChildren ) {
        Parent = theParent;
        Children = new ObservableCollection<FileSystemNode>();
        if ( lazyLoadChildren ) {
            Children.Add( DummyNode );
        }
    }

    public override void Dispose() {
        while ( Children.Count > 0 ) {
            FileSystemNode node = Children[ 0 ];
            Children.Remove( node );
            node.Dispose();
        }
        GC.SuppressFinalize( this );
    }

    /// <summary>
    /// Helper method that encapsulates the code needed to expand a node that can
    /// contain folders.  Prevents us from duplicating this code in several child
    /// classes.
    /// </summary>
    protected void ExpandFolders() {
        try {
            foreach ( string folder in Directory.EnumerateDirectories( Path ) ) {
                Children.Add( new FolderNode( new DirectoryInfo( folder ), this ) );
            }
        } catch ( Exception ) { }
    }

    protected virtual void LoadChildren() {
        // Does nothing by default.
    }
}

/// <summary>
/// Represents a Disk Drive in the file system tree view.
/// </summary>
public class DiskDriveNode : FileSystemNode {

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/diskdrive.png", UriKind.Relative ); }
    }

    public DiskDriveNode( string drive, FileSystemNode parent )
        : base( parent, true ) {
        Name = drive;
        Path = drive;
    }

    protected override void LoadChildren() {
        ExpandFolders();
    }
}

/// <summary>
/// Represents a folder in the file system tree view.  Lazy loads its <see cref="Children"/> collection
/// with its child folders and documents, if the <see cref="LoadDocuments"/> property is true.
/// </summary>
public class FolderNode : FileSystemNode {

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/folder.png", UriKind.Relative ); }
    }

    public FolderNode( DirectoryInfo folder, FileSystemNode parent )
        : base( parent, true ) {
        Name = folder.Name;
        Path = folder.FullName;
    }

    protected override void LoadChildren() {
        ExpandFolders();
    }
}

/// <summary>
/// Represents the "My Computer" node in the file system tree view.
/// </summary>
public class MyComputerNode : FileSystemNode {

    public const string MYCOMPUTER_PATH = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/computer.png", UriKind.Relative ); }
    }

    public MyComputerNode( FileSystemNode parent )
        : base( parent, true ) {
        // Populate its fields.
        Name = Car.FolderPickerDialog_Computer;
        Path = MYCOMPUTER_PATH;
    }

    protected override void LoadChildren() {
        try {
            foreach ( string driveName in Directory.GetLogicalDrives() ) {
                Children.Add( new DiskDriveNode( driveName, this ) );
            }
        } catch ( IOException ) {
        } catch ( UnauthorizedAccessException ) {
        }
    }
}

/// <summary>
/// Represents the Entire Network node of the file system tree view.  Lazy loads the servers
/// that are visible to the user on the network into its Children collection.
/// </summary>
public class NetworkNode : FileSystemNode {

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/network.png", UriKind.Relative ); }
    }

    public NetworkNode( FileSystemNode theParent )
        : base( theParent, true ) {
        Name = Car.FolderPickerDialog_Network;
        Path = string.Empty;
    }

    protected override void LoadChildren() {
        try {
            ComputerEnumerator enumerator = new ComputerEnumerator {
                ComputerFilter = ComputerEnumerator.ServerTypes.SV_TYPE_ALL
            };
            foreach ( Share server in enumerator ) {
                Children.Add( new ServerNode( server, this ) );
            }
        } catch ( Exception ) { }
    }
}

/// <summary>
/// Represents the root of the entire file system tree view.  Does not lazy load its
/// children.  Does not display in the TreeView control.
/// </summary>
public class RootNode : FileSystemNode {

    public RootNode()
        : base( null, false ) {
        Name = Path = string.Empty;

        // Create the MyComputer node and add it to this node's children.
        Children.Add( new MyComputerNode( this ) );

        // Create the Entire Network node and add it to this node's children.
        Children.Add( new NetworkNode( this ) );
    }
}

/// <summary>
/// Represents a server in the tree view.  Lazy loads the shares exposed by the server
/// into its children collection.
/// </summary>
public class ServerNode : FileSystemNode {

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/computer.png", UriKind.Relative ); }
    }

    public ServerNode( Share share, FileSystemNode parent )
        : base( parent, true ) {
        Name = share.Name;
        Path = share.UNCPath;
    }

    protected override void LoadChildren() {
        ShareEnumerator enumerator = new ShareEnumerator {
            Server = Name
        };

        foreach ( Share share in enumerator ) {
            Children.Add( new ShareNode( share, this ) );
        }
    }
}

/// <summary>
/// Represents a single share in the file system tree view.  Lazy loads the folders in the share
/// into its children collection.
/// </summary>
public class ShareNode : FileSystemNode {

    public override Uri Icon {
        get { return new Uri( "pack://CustomControls:,,,/Resources/share.png", UriKind.Relative ); }
    }

    public ShareNode( Share share, FileSystemNode parent )
        : base( parent, true ) {
        Name = share.Name;
        Path = share.UNCPath;
    }

    protected override void LoadChildren() {
        ExpandFolders();
    }
}

}

这是TreeView 控件的 XAML:

<TreeView BorderThickness="2"
          FontSize="20"
          FontWeight="Bold"
          Grid.Column="1"
          Grid.Row="3"
          ItemsSource="{Binding Path=Children}"
          Margin="5"
          Name="FolderTree"
          SelectedItemChanged="FolderTree_SelectedItemChanged">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight" Value="Bold" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="FontWeight" Value="ExtraBold" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type vm:FileSystemNode}"
                                  ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <Image Name="PART_Image"
                       Height="{Binding ElementName=PART_Content, Path=ActualHeight}"
                       Source="{Binding Path=Icon}"
                       Width="{Binding ElementName=PART_Content, Path=ActualHeight}" />
                <ContentPresenter Content="{Binding}"
                                  Margin="5,0"
                                  Name="PART_Content" />
            </StackPanel>
            </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

整个窗口的DataContext 在代码隐藏中设置为RootNode 类的实例。

当我单步执行代码时,我可以看到RootNode 对象以及MyComputerNodeNetworkNode 被实例化。然后MyComputerNode 中的Icon 属性在无限递归中被一遍又一遍地调用。我知道这是一个无限递归,因为几秒钟后,进程以StackoverFlowException. 停止我所能想到的是Image 控件由于某种原因不喜欢我的URI,并一直试图加载它。

是的,我确信 URI 指向资源中的真实图像。

在我的一生中,我看不到这个递归调用的来源。我什至将Icon 属性的类型更改为字符串,但它仍然会发生。

我做错了什么?

【问题讨论】:

  • 那是一大堆代码。几次调用 Icon 后检查堆栈跟踪。图标本身似乎不是问题。
  • 今天早上,我尝试从 XAML 中注释掉整个 &lt;TreeView.Resources&gt; 部分,问题就消失了。不管是什么,似乎和HierarchicalDataTemplate有关。

标签: c# wpf xaml recursion treeview


【解决方案1】:

我已经解决了这个问题。

问题出在Binding 中的ContentPresenterHeirarchicalDataTemplate。它只是设置为&lt;Binding /&gt;,这是错误的。

我已更改HeirarchicalDataTemplate

<HierarchicalDataTemplate DataType="{x:Type vm:FileSystemNode}"
                          ItemsSource="{Binding Children}">
    <StackPanel Orientation="Horizontal">
        <Image Name="PART_Image"
               Height="35"
               Source="Resources/computer.png"
               Width="35" />
        <TextBlock Margin="5,0"
                   Name="PART_Content"
                   Text="{Binding Path=Name}" />
    </StackPanel>
</HierarchicalDataTemplate>

这不会引发 StackOverflowException。

【讨论】:

    猜你喜欢
    • 2011-02-09
    • 2014-10-09
    • 2013-01-03
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-02
    相关资源
    最近更新 更多