2009-08-03 52 views
3

私は、サーバーとフォルダを表示するTreeViewを作りたいと思います。 - フォルダ[C#] [WPF] UIをフリーズせずに非同期TreeViewを作成する方法は?

class Folder 
{ 
    // Hidden attributes 
    public String ElementID { get; set; } 

    // Attributes displayed in the treeview 
    public String ElementName { get; set; } 

    // This collection is binded with the GUI defined in XAML 
    public CompositeCollection Children { get; set; } 

    public BitmapImage Image {get; set; } 

    // Constructor 
    public Folder() 
    { 
     // Fill the treeview with a temporary child as text 
     Children = new CompositeCollection(); 
     Children.Add(new TextBlock() 
     { 
      Text = "Loading...", 
      FontStyle = FontStyles.Italic 
     }); 
    } 

    // Fill the Children collection 
    public void LoadChildren() 
    { 
     // Clear the Children list 
     Children.Clear(); 

     // Populate the treeview thanks to the bind 
     foreach (Folder folder in this.GetChildren()) 
     { 
      Children.Add(folder); 
     } 
    } 

    // Get the Folder Children as Folder 
    protected List<Folder> GetChildren() 
    { 
     System.Threading.Thread.Sleep(1000); 

     List<Folder> resu = new List<Folder>(); 

     Folder f1 = new Folder(); 
     f1.ElementID = "1"; 
     f1.ElementName = "folder 1"; 

     Folder f2 = new Folder(); 
     f2.ElementID = "2"; 
     f2.ElementName = "folder 2"; 

     Folder f3 = new Folder(); 
     f3.ElementID = "3"; 
     f3.ElementName = "folder 3"; 

     Folder f4 = new Folder(); 
     f4.ElementID = "4"; 
     f4.ElementName = "folder 4"; 

     resu.Add(f1); 
     resu.Add(f2); 
     resu.Add(f3); 
     resu.Add(f4); 

     return resu; 
    } 

のThread.sleep()メソッドは、しばらく時間がかかることをシミュレートし

:私のニーズによると、私は2つのクラスを作りました。

- サーバー:フォルダ

class Server : Folder 
{ 
    // Get the Servers list 
    public static List<Server> GetServers() 
    { 
     System.Threading.Thread.Sleep(1500); 

     // Create a list of Servers 
     List<Server> servers = new List<Server>(); 

     Server s1 = new Server(); 
     s1.ElementID = "1"; 
     s1.ElementName = "Server 1"; 

     Server s2 = new Server(); 
     s2.ElementID = "2"; 
     s2.ElementName = "Server 2"; 

     Server s3 = new Server(); 
     s3.ElementID = "3"; 
     s3.ElementName = "Server 3"; 

     Server s4 = new Server(); 
     s4.ElementID = "4"; 
     s4.ElementName = "Server 4"; 

     servers.Add(s1); 
     servers.Add(s2); 
     servers.Add(s3); 
     servers.Add(s4); 

     return servers; 
    } 
} 
ここ

は私のTreeViewコントロールのコードです:

XAMLの一部:背後に

<Window x:Class="WpfApplication1.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:ar="clr-namespace:WpfApplication1" 
DataContext="{Binding RelativeSource={RelativeSource Self}}" 
Title="Browser" Height="327" Width="250"> 
<Window.Resources> 
    <HierarchicalDataTemplate DataType="{x:Type ar:Folder}" ItemsSource="{Binding Path=Children}" > 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="Images\TreeView\folder.png" Height="15" Width="15" /> 
      <TextBlock Text="{Binding Path=ElementName}" /> 
     </StackPanel> 
    </HierarchicalDataTemplate> 
    <HierarchicalDataTemplate DataType="{x:Type ar:Server}" ItemsSource="{Binding Path=Children}"> 
     <StackPanel Orientation="Horizontal"> 
      <Image Source="Images\TreeView\server.png" Height="15" Width="15" /> 
      <TextBlock Text="{Binding Path=ElementName}" /> 
     </StackPanel> 
    </HierarchicalDataTemplate> 
</Window.Resources> 
<Grid> 
     <TreeView HorizontalAlignment="Stretch" Width="230" Name="treeView"> 
     <TreeViewItem Header="Servers" x:Name="root" x:FieldModifier="private"> 
       <TreeViewItem TextBlock.FontStyle="Italic" 
         Header="Loading..."/> 
     </TreeViewItem> 
    </TreeView>   
</Grid> 

コード:

public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     InitializeComponent(); 

     // Add an event in order to know when an TreeViewItem is Expanded 
     AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(treeItemExpanded), true); 
    } 

    // Event when a treeitem expands 
    private void treeItemExpanded(object sender, RoutedEventArgs e) 
    {   
     // Get the source 
     var item = e.OriginalSource as TreeViewItem; 

     // If the item source is a Simple TreeViewItem 
     if (item == null) 
      return; 

     if (item.Name == "root") 
     { 
      List<Server> servers = new List<Server>(); 

      servers = Server.GetServers(); 

      root.Items.Clear(); 

      // Fill the treeview with the servers 
      root.ItemsSource = servers;     
     }   

     // Get data from item as Folder (also works for Server) 
     var treeViewElement = item.DataContext as Folder; 

     // If there is no data 
     if (treeViewElement == null) 
      return; 

     // Load Children (populate the treeview) 
     ThreadPool.QueueUserWorkItem(delegate 
     { 
      Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate 
      { 
       treeViewElement.LoadChildren(); 

      }); 
     }); 
    } 
} 

私は、TreeView項目(2例:ルート費やし、フォルダ費やす)消費しながら、UIがフリーズないしたいと思い、私は「ロードを...」が表示されない の1-当面

を私はルートノード を消費するとき、私はこのような何かを試してみましたが、例外があります:スレッドがSTAモードである必要があります

// Load Children (populate the treeview) 
ThreadPool.QueueUserWorkItem(delegate 
{ 
    List<Server> servers = Server.GetServers(); 

    Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate 
    { 
     root.Items.Clear(); 

     // Fill the treeview with the servers 
     root.ItemsSource = servers; 
    }); 
}); 

2 - ノードが費やされた場合、私は「ロードを持っています...しばらくするとUIが更新されます。この間、UIはフリーズしています。ユーザーはウィンドウを移動できません。

お願いします。

(PS:あなたは、他のコメントがある場合、私はそれらをここで喜んでいると思います;))

答えて

1

フォルダクラスからこれを削除します。

あなたがする許可
 Children.Add(new TextBlock() 
     { 
      Text = "Loading...", 
      FontStyle = FontStyles.Italic 
     }); 

  ThreadPool.QueueUserWorkItem(delegate 
      { 
       List<Server> servers = new List<Server>(); 

       servers = Server.GetServers(); 

       this.Dispatcher.Invoke((Action)delegate 
       { 
        item.Items.Clear(); 

        // Fill the treeview with the servers 
        item.ItemsSource = servers; 

       }); 
      }); 
3

はあなたのゲイリーありがとう、私は私の広告は、ほとんどすべて:)

・ファーストを解決し、別の解決策を見つけました空のクラスCustomTreeViewITem

class CustomTreeViewItem 
{ } 

このクラスはXAMLで使用されます。

は、私は、フォルダクラスでいくつか変更: - のObservableCollection にCompositeCollectionを交換 - フォルダがCustomTreeViewItem から継承します - 私はサーバ・クラスを変更しなかった

class Folder : CustomTreeViewItem 
{ 
    // Hidden attributes 
    public String ElementID { get; set; } 

    // Attributes displayed in the treeview 
    public String ElementName { get; set; } 

    public ObservableCollection<CustomTreeViewItem> Children { get; set; } // This collection is binded with the GUI defined in XAML 

    // Constructor 
    public Folder() 
    { 
     // Fill the treeview with a temporary child as text 
     Children = new ObservableCollection<CustomTreeViewItem>(); 
     Children.Add(new CustomTreeViewItem()); 
    } 

    // Get the Folder Children as Folder 
    // Method overriden by the Server class 
    public List<Folder> GetChildren() 
    { 
     System.Threading.Thread.Sleep(5000); 

     List<Folder> resu = new List<Folder>(); 

     Folder f1 = new Folder(); 
     f1.ElementID = "1"; 
     f1.ElementName = "folder 1"; 

     Folder f2 = new Folder(); 
     f2.ElementID = "2"; 
     f2.ElementName = "folder 2"; 

     Folder f3 = new Folder(); 
     f3.ElementID = "3"; 
     f3.ElementName = "folder 3"; 

     Folder f4 = new Folder(); 
     f4.ElementID = "4"; 
     f4.ElementName = "folder 4"; 

     resu.Add(f1); 
     resu.Add(f2); 
     resu.Add(f3); 
     resu.Add(f4); 

     return resu; 
    } 

コンストラクタを変更し

今、私のXAMLは、次のようになります。

<Window x:Class="WpfApplication1.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:ar="clr-namespace:WpfApplication1" 
DataContext="{Binding RelativeSource={RelativeSource Self}}" 
Title="Browser" Height="327" Width="250"> 
<Window.Resources> 
    <HierarchicalDataTemplate DataType="{x:Type ar:Folder}" ItemsSource="{Binding Path=Children}"> 
     <TextBlock Text="{Binding Path=ElementName}" /> 
    </HierarchicalDataTemplate> 
    <HierarchicalDataTemplate DataType="{x:Type ar:Server}" ItemsSource="{Binding Path=Children}"> 
     <TextBlock Text="{Binding Path=ElementName}" /> 
    </HierarchicalDataTemplate> 
    <HierarchicalDataTemplate DataType="{x:Type ar:CustomTreeViewItem}"> 
      <TextBlock Text="Loading..." /> 
    </HierarchicalDataTemplate> 
</Window.Resources> 
<Grid> 
    <TreeView HorizontalAlignment="Stretch" Width="230" Name="treeView"> 
     <TreeViewItem Header="Servers" x:Name="root" x:FieldModifier="private"> 
      <ar:CustomTreeViewItem/> 
     </TreeViewItem> 
    </TreeView> 
</Grid> 

そして、背後に私のコード:

private void treeItemExpanded(object sender, RoutedEventArgs e) 
    { 
     // Get the source 
     var item = e.OriginalSource as TreeViewItem; 

     // If the item source is a Simple TreeViewItem 
     if (item == null) 
     // then Nothing 
     { return; } 

     if (item.Name == "root") 
     { 
      // Load Children (populate the treeview) 
      ThreadPool.QueueUserWorkItem(delegate 
      { 
       List<Server> servers = Server.GetServers(); 

       Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate 
       { 
        root.Items.Clear(); 

        // Fill the treeview with the servers 
        root.ItemsSource = servers; 
       }); 
      }); 
     } 

     // Get data from item as Folder (also works for Server) 
     Folder treeViewElement = item.DataContext as Folder; 

     // If there is no data 
     if (treeViewElement == null) 
     { 
      return; 
     } 
     // Load Children (populate the treeview) 
     ThreadPool.QueueUserWorkItem(delegate 
     { 

      // Clear the Children list 
      var children = treeViewElement.GetChildren(); 

      // Populate the treeview thanks to the bind 

      Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate 
      { 
       treeViewElement.Children.Clear(); 

       foreach (Folder folder in children) 
       { 
        treeViewElement.Children.Add(folder); 
       } 

      }); 
     }); 
    } 

それはすべての単一のTreeViewClass(カスタム、フォルダ、サーバ)のカスタムに私を可能にし、HierarchicalDataTemplateのおかげ。

0

私はいくつかの訂正が必要だと思います: XAMLはエクスパンダイベントメソッドを指す必要があります。私はそれをTreeViewItem要素に入れました。

<TreeViewItem Header="Servers" x:Name="root" x:FieldModifier="private" Expanded="treeItemExpanded"> 

コードビーンドは、2つの場所で「ルート」を参照します。私はそれが「アイテム」であるべきだと思う。

root.Items.Clear(); 
// Fill the treeview with the servers 
root.ItemsSource = servers; 

item.Items.Clear(); 
// Fill the treeview with the servers 
item.ItemsSource = servers; 

マンとなり、私はここに私のツリービューの金鉱山を発見したと思います。 方向感謝!!!!!! 私はTreeViewとHierarchicalDataTemplatesとObservableCollectionsの両方のスレッディングをうまく示しています。非常に素晴らしい! SWEET!

関連する問題