我想制作一个显示服务器和文件夹的TreeView.根据我的需要,我开设了2节课:
-文件夹
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()模拟该方法可能需要一段时间.
-服务器:文件夹
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();
});
});
}
}
我想让UI在冻结treeView Item时不冻结(2种情况:root扩展,文件夹扩展)
暂且
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:如果您还有其他意见,我很乐意在这里提出;))
解决方法:
谢谢加里,但我发现了另一个解决方案,几乎可以解决所有问题:)
首先,我添加一个空类CustomTreeViewITem
class CustomTreeViewItem
{ }
此类在XAML中使用.
我更改了文件夹类中的一些内容:
-将CompositeCollection替换为ObservableCollection
-使Folder继承自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);
}
});
});
}
多亏了HierarchicalDataTemplate,它使我能够自定义每个TreeViewClass(自定义,文件夹,服务器).