【发布时间】:2012-06-08 05:55:12
【问题描述】:
我有一个大问题,如果我是 binding 带有 converter 的 ItemSource (observablecollection),递归 treeview 不会更新任何孩子。 我制作了一个小示例 WPF 应用程序来解释令人讨厌的行为。
首先我有一个名为 Folder 的类。文件夹集合是从真实应用程序中的 DataLayer 交付的。这是简单的示例类:
// Represents a Folder from the database
public class Folder
{
public string Label { get; set; }
// Data recursion
public ObservableCollection<Folder> Children { get; set; }
public Folder()
{
Children = new ObservableCollection<Folder>();
}
}
在表示层中,我需要更多属性才能在树视图中查看它。我无法更改文件夹类。
// Hold additional data to a Folder
public class TreeViewFolder
{
public Folder Folder { get; set; }
public bool IsExpanded { get; set; }
}
在我的示例中,我有一个 ViewModel 类,它生成一些根级别的测试数据。通过一个任务,我模拟了 3 秒后测试数据的变化:添加一个根文件夹和一个子文件夹。
// ViewModel to MainWindow.xaml
public class ViewModel
{
public ObservableCollection<Folder> Folders { get; set; }
public ObservableCollection<TreeViewFolder> TreeViewFolders { get; set; }
public ViewModel()
{
// childfolder
var childs = new ObservableCollection<Folder>();
childs.Add(new Folder{Label="3.1"});
// Generate some test data
Folders = new ObservableCollection<Folder>
{
new Folder {Label = "1."},
new Folder {Label = "2."},
new Folder {Label = "3.", Children = childs},
new Folder {Label = "4."}
};
// Add the test data to new TreeViewFolders
TreeViewFolders = new ObservableCollection<TreeViewFolder>
{
new TreeViewFolder{Folder = Folders[0]},
new TreeViewFolder{Folder = Folders[1]},
new TreeViewFolder{Folder = Folders[2]},
new TreeViewFolder{Folder = Folders[3]}
};
// This is the UI Thread
Execute.InitializeWithDispatcher();
// Wait for 3 seconds...
Task.Factory.StartNew(() =>
System.Threading.Thread.Sleep(3000))
// ...and then add a new main folder and one child folder
.ContinueWith((pre) =>
Execute.InvokeOnUIThread(() => {
Folders.Add( new Folder() {Label = "5."});
TreeViewFolders.Add(new TreeViewFolder{Folder = Folders[4]});
Folders[1].Children.Add(new Folder {Label = "2.1"});
}));
}
}
转换器实现了一个字典来跟踪所有已经是 TreeViewFolder 的文件夹,并且能够将一个文件夹或 ObservableCollection 转换为 TreeViewFolder 或 ObservableCollection。没有实现反向转换。
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//DictionaryLookUp just holds a Folder <-> TreeViewFolder relation in a Dictionary<Folder, TreeViewFolder> for performance issues
// Single object
if (targetType == typeof(TreeViewFolder)) {
var inputFolder = value as Folder;
if (inputFolder == null) return null;
return DictionaryLookUp(inputFolder);
}
// Collection of Folders
// WPF SAYS IT WANTS AN IENUMERABLE??? I GIVE AN OBSERVABLECOLLECTION
if (targetType == typeof(IEnumerable)) {
var inputFolders = value as IEnumerable<Folder>;
if (inputFolders == null) return null;
var outputFolders = new ObservableCollection<TreeViewFolder>();
foreach (var folder in inputFolders) {
outputFolders.Add(DictionaryLookUp(folder));
}
return outputFolders;
}
//Error!
return null;
}
在 MainWindow.xaml 中,我只有两个 TreeView。第一个再次只是为了证明当我使用原始文件夹(而不是 TreeViewFolder)时一切正常。第二个 TreeView 正是我所需要的。
<Window x:Class="TreeViewConverterBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewConverterBinding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<local:FolderToTreeViewFolderConverter x:Key="FtTVFConverter" />
</Window.Resources>
<StackPanel >
<Label Content="TreeView for Folders: Binding without Converter" />
<TreeView ItemsSource="{Binding Folders}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:Folder}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Label}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Label Content="TreeView for TreeViewFolders: Binding to same Source with Converter" />
<TreeView ItemsSource="{Binding TreeViewFolders}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:TreeViewFolder}"
ItemsSource="{Binding Folder.Children, Converter={StaticResource FtTVFConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Folder.Label}"/>
<TextBlock Text=" Folder.Children.Count=" />
<TextBlock Text="{Binding Folder.Children.Count}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
</Window>
此外,我将一个特殊的 TextBlock 绑定到文件夹的子项计数。
这里的问题是,在第二个 TreeView 中,TextBlock 在条目“2”的 3 秒后显示 Count=1。但树视图中没有出现 [+]。 [+] 在条目“3”中。尽管。上面的 TreeView 效果很好!
谁能告诉我这里有什么问题?如何使 [+] 出现在底部 TreeView 的第二个条目中?
我知道还有其他方法可以使用 TreeViewFolder 中的属性(继承、反射等)扩展文件夹,但不适用于实际应用程序。 这里的主要问题是,一个文件夹对于整个应用程序只存在一次,而相应的 TreeViewFolder 存在 n 次具有不同的属性...
提前致谢 索科
【问题讨论】:
-
如果我使用 icommand 而不是你的 task.factory 来添加文件夹内容,它对我有用。在这种情况下,我也摆脱了转换器。
-
感谢您的意见。但是我必须使用转换器,因为我需要 TreeView 的所有级别中的 TreeViewFolder 实例。事实上,如果您只是删除 ItemSource 绑定中的“,Converter={StaticResource FtTVFConverter}”,一切正常 - 我知道这一点。但这不是我需要的......
标签: treeview binding observablecollection converter wpf binding treeview observablecollection converter