2015-02-11 51 views
0

我需要一个TreeView来表示存储在SQL Server CE数据库中的多个表的一些分层数据。之前,数据存储在xml中,在启动时很简单的反序列化,一切都很好。现在我被要求将数据移动到数据库,并且遇到了几个问题。如何在延迟加载的WPF MVVM TreeView中搜索?

我的第一个问题是,从数据库中检索多个项目并从这些项目构建一个TreeView ViewModel需要相当长的时间(仍然不确定什么是更长的时间 - 获取项目或构建此树)。所以我实现了延迟加载,现在只有在TreeViewItem正在扩展时我才会收到项目。

现在,我需要对所有节点执行文本搜索,但要使其工作,必须加载所有节点。 我试图加载所有这些,但树加载时UI冻结。在BackgroundWorker内部执行此操作对我来说也是不可能的,因为这些项目存储在ObservableCollection中,我得到“InvalidOperationException”。使用Dispatcher有助于此,但它也冻结UI ...

从我的TreeViewItem虚拟机的摘录如下,如果需要更多的代码,请让我知道。也许我对我的设计完全错误,所以任何意见都非常感谢!

public class TreeViewItemViewModel: DisplayableItem, IItemsHost 
    { 
     internal static DummyTreeViewItemViewModel _dummy = new DummyTreeViewItemViewModel(); 

     public TreeViewItemViewModel(){} 

     public TreeViewItemViewModel(IDisplayableItem displayableItem) 
     { 
      Data = displayableItem; 
     } 

     public TreeViewItemViewModel(IDisplayableItem displayableItem, IDisplayableItem parent) 
      :this(displayableItem) 
     { 
      Parent = parent as TreeViewItemViewModel; 
     } 

     private TreeViewItemViewModel _parent; 
     public TreeViewItemViewModel Parent 
     { 
      get { return _parent; } 
      set { _parent = value; InvokePropertyChanged(new PropertyChangedEventArgs("Parent")); } 
     } 

     private IDisplayableItem _data; 
     public new IDisplayableItem Data 
     { 
      get { return _data; } 
      set { _data = value; InvokePropertyChanged(new PropertyChangedEventArgs("Data")); } 
     } 

     private bool _isSelected; 
     public new bool IsSelected 
     { 
      get { return _isSelected; } 
      set { _isSelected = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsSelected")); } 
     } 

     private bool _isEnabled=true; 
     public new bool IsEnabled 
     { 
      get { return _isEnabled; } 
      set { _isEnabled = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsEnabled")); } 
     } 

     private bool _isVisible = true; 
     public new bool IsVisible 
     { 
      get { return _isVisible; } 
      set { _isVisible = value; InvokePropertyChanged(new PropertyChangedEventArgs("IsVisible")); } 
     } 

     private void FillItems() 
     { 
      if (Items.Contains(_dummy)) 
      { 
       Items.Remove(_dummy); 
       var itemshost = Data as IItemsHost; 
       if (itemshost != null) 
       { 
        _items = new ObservableCollection<IDisplayableItem>(); 
        foreach (var item in itemshost.Items)//getting 'Items' actually requesting them from a database 
        { 
         var treeItem = new TreeViewItemViewModel(item, this); 
         _items.Add(treeItem); 
        } 
        InvokePropertyChanged(new PropertyChangedEventArgs("Items")); 
       } 
      } 
     } 

     protected bool _isExpanded; 
     public bool IsExpanded 
     { 
      get { return _isExpanded; } 
      set 
      { 
       if(value) 
       { 
        FillItems(); 
       } 
       _isExpanded = value; 
       InvokePropertyChanged(new PropertyChangedEventArgs("IsExpanded"));    
      } 
     } 

     protected SObservableCollection<IDisplayableItem> _items = new SObservableCollection<IDisplayableItem>(); 
     public SObservableCollection<IDisplayableItem> Items 
     { 
      get 
      { 
       var itemshost = Data as IItemsHost; 
       if (itemshost != null) 
       { 
        if (_items.Count == 0 && itemshost.Items.Count > 0) 
         _items.Add(_dummy); 
       } 
       return _items; 
      } 
      set { _items = value; InvokePropertyChanged(new PropertyChangedEventArgs("Items")); } 
     } 

更新:对于那些谁也寻找一个类似的解决方案 - 我的问题是在我的查询方法。每当我需要执行查询时,我都不应该打开新的SQL Server CE连接...

回答

2

如何保存整个层次结构的扁平表示并让您的搜索逻辑查询此表的新数据库表?当你插入/更新/删除其他表中的记录时,显然需要保持这张表更新。

新表格中的每条记录都需要包含有关项目在层次结构中的位置的一些信息,以便当您返回搜索结果时,只能加载并填充包含“匹配”的树节点。

0

由于从数据库中读取数据库是异步完成的,所以性能瓶颈应该从ViewModel构建View。我建议以下方法:

  1. 从数据库中读取所有必要的模型数据在一个异步调用,并将它们存储在一个名为SearchHelper的对象。
  2. 向您创建的每个ViewModel添加一个简单属性(Model.Id或Model的哈希码),以便查找特定模型的等效视图模型。
  3. 创建只有可见ViewModels。 (仅适用于ViewModel的延迟加载)
  4. 使用SearchHelper查找搜索查询的匹配项,然后使用结果的Id或哈希代码,可以轻松找到它们的等效视图模型。

请考虑:

  1. 一旦加载,SearchHelper不更新自身,所以你可能需要手动更新。
  2. 要使此方法具有最佳性能,请尝试避免迭代所有节点。相反,存储跟踪项目的序列(它们的索引或Id)以便在视图模型中查找它们。如果每个模型项目都知道它的父项,那么回溯应该很容易。
+0

我已经使用'System.Diagnostics.Stopwatch'做了一个简单的测试,发现即使不使用视图(在控制台应用程序中),迭代所有树项目和子项目需要大约30秒,并填充它们全部。这不好,但我不知道该怎么做这:( – lena 2015-02-12 07:25:34