2012-06-08 23 views
2

得到更新,我有一个很大的问题,递归不更新任何孩子的,如果我了的ItemSource()与。 我编写了一个小示例WPF应用程序来解释刺激性行为。的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>(); 
    } 
} 

在表示层中,我需要更多属性才能在树视图中查看它。我无法改变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绑定到一个文件夹的子项的计数。

问题这里,在第二树视图TextBlock的显示计数= 1在条目中的后3秒“2”。但没有[+]出现在TreeView中。 [+]在条目“3”中。虽然。上层TreeView很棒!

谁能告诉我什么是这里的问题?我如何让[+]出现在底部TreeView的第二项中?

我知道有其他的方法与在TreeViewFolder属性(继承,反射,...),但不适合实际应用展开文件夹。 这里的主要问题是,一个文件夹只存在一次为整个应用程序,在这里,相应的TreeViewFolder存在具有不同特性的N次...

在此先感谢 罟

+0

如果我使用icommand而不是你的task.factory来做你的添加文件夹的东西它为我工作。在这种情况下,我也摆脱了转换器。 – blindmeis

+0

感谢您的意见。但我必须使用转换器,因为我需要TreeView的所有级别的TreeViewFolder实例。事实上,如果你只是在ItemSource绑定中删除了“,Converter = {StaticResource FtTVFConverter}”,那么我知道这一点。但这不是我所需要的... – Soko

回答

1

看来这里的问题是WPF无法将ObservableCollection转换为ObservableCollection。 Convert()方法中的IEnumerable的targetType为该问题提供了一个很好的提示。我找不到告诉WPF请求ObservableCollection的方法。即使我返回一个ObservableCollection WPF不会像一个一样处理它...

因此,经过大量的尝试和错误,我想出了解决问题的解决方法。 基本上它使用集合的Count属性来触发WPF中的更新。

这里是在战后初期的XAML的改变部分:

   <HierarchicalDataTemplate.ItemsSource> 
        <MultiBinding Converter="{StaticResource FtTVFMultiConverter}"> 
         <Binding Path="Folder.Children" /> 
         <Binding Path="Folder.Children.Count" /> 
        </MultiBinding> 
       </HierarchicalDataTemplate.ItemsSource> 

FtTVFMultiConverter是IMultiValueConverter接口的实现只转换(A类):

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     var inputFolders = values[0] as ObservableCollection<Folder>; 
     if (inputFolders == null) return null; 

     var outputTreeViewFolders = new ObservableCollection<TreeViewFolder>(); 

     foreach (var folder in inputFolders) { 
      outputTreeViewFolders.Add(new TreeViewFolder { Folder = folder }); 
     } 

     return outputTreeViewFolders; 
    } 

这样的一个变化ViewModel中的源由Folder.Children.Count属性的更改触发。因此,调用Convert方法来传递新添加的Childs。

0

你说...

该转换器是没有什么特别的。它实现了一个字典,以保持 的轨道,是一个媒体链接所有TreeViewFolder文件夹,并能 转换一个文件夹或IEnumerable的到TreeViewFolder或 IEnumerable的。未执行反向转换。

但是那什么导致了这一切,不是它!通过将可观察集合转换为字典(这是一种不可观察的IEnumerable),您已经失去了WPF GUI的持续更新能力!

要么你需要实现一个自定义词典与INotifyCollectionChanged接口,然后当你处理Folders.CollectionChanged事件,你根据什么观察集合中被改变同步您的字典和提高自己的CustomDictionarty.CollectionChanged事件。

+0

嗨AngelWPF,感谢您的意见。我在我的问题中包含了Convert方法 - 请看看。当然,我返回一个ObservableCollection 。问题是,调试器告诉我,targetType是一个IEnumerable !?该字典只是一对一的关系。这只是一种表现优化。 Thanx Soko – Soko