Hi,
I would like to make a TreeView that displays servers and folders. According to my needs, I made 2 classes :
-- Folder
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() simulates that the method can take a while.
-- Server : folder
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;
}
}
Here is my TreeView Code :
XAML part :
<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>
Code Behind :
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();
});
});
}
}
I would like to make the UI not freeze while expending a treeView Item ( 2 cases : root expending, folder expending)
For the time being 1- I don't see the "Loading..." when I expend the root node I tried something like this, but there is a Exception : the thread must be in STA mode :
// 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- When a node is expended, I have the "Loading..." and after a while, the UI is updated. During this time, the UI is frozen : the user is unable to move the window.
Could you help me please ?
(PS : If you have any other comments, I'd be glad to here them ;) )