Hi guys, here I am posting a new article for loading the RadTreeView Control on demand (Lazy Loading) using MVVM Pattern.
Lets jump into coding!!!
Create new Silverlight project in Visual studio (I am using silverlight 3.0 with Visual studio 2008 with SP1). My project name is RTVOnDemadLoadingMVVM.
Let visual studio to create web project along with your silverlight project. So You will be having two projects in same solution like below:
Server Side Implementation
Create a table named Tree in one of your SQL Server database using the following script:
1: CREATE TABLE [dbo].[Tree](
2: [ID] [int] IDENTITY(1,1) NOT NULL,
3: [Title] [varchar](50) NULL,
4: [ParentID] [int] NULL,
5: CONSTRAINT [PK_Tree] PRIMARY KEY CLUSTERED
6: (
7: [ID] ASC
8: )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
9: ) ON [PRIMARY]
Create two folders in RTVOnDemadLoadingMVVM.Web project
- Data
- WCFServices
Add a LINQ to SQL Classes named TreeDataClasses.dbml in your data folder.
Go to Server explorer and connect to the database containing table Tree.
Once it is connected, drag and drop the Tree table to the TreeDataClasses.dbml work space.
Add a WCF service file to your WCFServices folder, I have given WCFService.svc as name.
Go to your web.config file and change binding="wsHttpBinding" to binding="basicHttpBinding"
Following is the code for my service project:
IWCFService.cs
1: [ServiceContract]
2: public interface IWCFService
3: {
4: [OperationContract]
5: List<Tree> GetItemsByID(int parentID);
6: }
WCFService.svc.cs
1: public class WCFService : IWCFService
2: {
3: public List<Tree> GetItemsByID(int parentID)
4: {
5: TreeDataClassesDataContext db = new TreeDataClassesDataContext();
6: List<Tree> result = db.Trees.Where(r => r.ParentID == parentID).ToList<Tree>();
7: return result;
8: }
9: }
Client Side Implementation
If you are done with the server side add the service reference in the silverlight project.
Right click on add reference => Add Service Reference => Click on Discover, Once the service is listed, select the service and give a name for your reference. I have given TreeWCFService.
As I am using MVVM pattern in my project, Lets create 3 folders in Silverlight project
- Library
- Model
- ViewModel
- View
In Library you will be having two classes for handling the Expanding Command and Click Command. For this we need to create separate dependency properties.
- RadTreeViewItemExpanded.cs
- RadTreeViewItemSelected.cs
Following is the code for RadTreeViewItemExpanded.cs
1: public class ItemExpandedCommandBehavior : CommandBehaviorBase<RadTreeView>
2: {
3: public ItemExpandedCommandBehavior(RadTreeView targetObject)
4: : base(targetObject)
5: {
6: targetObject.LoadOnDemand += new EventHandler<Telerik.Windows.RadRoutedEventArgs>(targetObject_LoadOnDemand);
7: }
8:
9: void targetObject_LoadOnDemand(object sender, Telerik.Windows.RadRoutedEventArgs e)
10: {
11: RadTreeViewItem item = e.OriginalSource as RadTreeViewItem;
12: base.CommandParameter = item.Item;
13: base.ExecuteCommand();
14: }
15: }
16:
17: public static class ItemExpanded
18: {
19: public static readonly DependencyProperty ItemExpandedBehaviorProperty =
20: DependencyProperty.RegisterAttached(
21: "ItemExpandedBehaviorProperty", typeof(ItemExpandedCommandBehavior),
22: typeof(ItemExpandedCommandBehavior), null);
23:
24: #region CommandProperty
25:
26: public static readonly DependencyProperty CommandProperty =
27: DependencyProperty.RegisterAttached(
28: "Command", typeof(ICommand), typeof(ItemExpanded),
29: new PropertyMetadata(CommandProperty_Changed));
30:
31: public static ICommand GetCommand(DependencyObject obj)
32: {
33: return (ICommand)obj.GetValue(CommandProperty);
34: }
35:
36: public static void SetCommand(DependencyObject obj, ICommand value)
37: {
38: obj.SetValue(CommandProperty, value);
39: }
40:
41: private static void CommandProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
42: {
43: RadTreeView targetObject = dependencyObject as RadTreeView;
44:
45: if (targetObject != null)
46: GetOrCreateBehavior(targetObject).Command = e.NewValue as ICommand;
47: }
48:
49: #endregion
50:
51: private static ItemExpandedCommandBehavior GetOrCreateBehavior(RadTreeView targetObject)
52: {
53: ItemExpandedCommandBehavior behavior = targetObject.GetValue(ItemExpandedBehaviorProperty) as ItemExpandedCommandBehavior;
54:
55: if (behavior == null)
56: {
57: behavior = new ItemExpandedCommandBehavior(targetObject);
58: targetObject.SetValue(ItemExpandedBehaviorProperty, behavior);
59: }
60:
61: return behavior;
62: }
63: }
Follwing is the code for RadTreeViewItemSelected.cs
1: public class ItemClickedCommandBehavior : CommandBehaviorBase<RadTreeView>
2: {
3: public ItemClickedCommandBehavior(RadTreeView targetObject)
4: : base(targetObject)
5: {
6: targetObject.Selected += new EventHandler<Telerik.Windows.RadRoutedEventArgs>(targetObject_Selected);
7: }
8:
9: void targetObject_Selected(object sender, Telerik.Windows.RadRoutedEventArgs e)
10: {
11: RadTreeViewItem raTVItem = e.OriginalSource as RadTreeViewItem;
12: base.CommandParameter = raTVItem.Item;
13: base.ExecuteCommand();
14: }
15: }
16:
17: public static class ItemClicked
18: {
19: public static readonly DependencyProperty ItemClickedBehaviorProperty =
20: DependencyProperty.RegisterAttached(
21: "ItemClickedBehaviorProperty", typeof(ItemClickedCommandBehavior),
22: typeof(ItemClickedCommandBehavior), null);
23:
24: #region CommandProperty
25:
26: public static readonly DependencyProperty CommandProperty =
27: DependencyProperty.RegisterAttached(
28: "Command", typeof(ICommand), typeof(ItemClicked),
29: new PropertyMetadata(CommandProperty_Changed));
30:
31: public static ICommand GetCommand(DependencyObject obj)
32: {
33: return (ICommand)obj.GetValue(CommandProperty);
34: }
35:
36: public static void SetCommand(DependencyObject obj, ICommand value)
37: {
38: obj.SetValue(CommandProperty, value);
39: }
40:
41: private static void CommandProperty_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
42: {
43: RadTreeView targetObject = dependencyObject as RadTreeView;
44:
45: if (targetObject != null)
46: GetOrCreateBehavior(targetObject).Command = e.NewValue as ICommand;
47: }
48:
49: #endregion
50:
51: private static ItemClickedCommandBehavior GetOrCreateBehavior(RadTreeView targetObject)
52: {
53: ItemClickedCommandBehavior behavior = targetObject.GetValue(ItemClickedBehaviorProperty) as ItemClickedCommandBehavior;
54:
55: if (behavior == null)
56: {
57: behavior = new ItemClickedCommandBehavior(targetObject);
58: targetObject.SetValue(ItemClickedBehaviorProperty, behavior);
59: }
60:
61: return behavior;
62: }
63: }
In model create a model class file called RTVItem.cs which will represent each node of the tree view. Following is the code for my model class:
1: public class RTVItem : INotifyPropertyChanged
2: {
3: #region Private Variables
4: private int _id;
5: private string _title;
6: private ObservableCollection<RTVItem> _rTVItems;
7: #endregion
8:
9: #region Public Properties
10: public int ID
11: {
12: get
13: {
14: return _id;
15: }
16: set
17: {
18: _id = value;
19: NotifyPropertyChanged("ID");
20: }
21: }
22: public string Title
23: {
24: get
25: {
26: return _title;
27: }
28: set
29: {
30: _title = value;
31: NotifyPropertyChanged("Title");
32: }
33: }
34: public ObservableCollection<RTVItem> RTVItems
35: {
36: get
37: {
38: return _rTVItems;
39: }
40: set
41: {
42: _rTVItems = value;
43: NotifyPropertyChanged("RTVItems");
44: }
45: }
46: #endregion
47:
48: #region INotifyPropertyChanged Members
49:
50: public event PropertyChangedEventHandler PropertyChanged;
51: private void NotifyPropertyChanged(String propertyName)
52: {
53: if (PropertyChanged != null)
54: {
55: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
56: }
57: }
58:
59: #endregion
60: }
In View Model folder create a class called ViewModelRTV.cs, here you can implement your logic for populating the tree from the database. Following is the code for the view model class:
1: public class ViewModelRTV : INotifyPropertyChanged
2: {
3: #region Private Variables
4: private ObservableCollection<RTVItem> _rTVItems;
5: private RTVItem _selectedRTVItem;
6: #endregion
7:
8: #region Public Properties
9:
10: public RTVItem SelectedRTVItem
11: {
12: get
13: {
14: return _selectedRTVItem;
15: }
16: set
17: {
18: _selectedRTVItem = value;
19: NotifyPropertyChanged("SelectedRTVItem");
20: }
21: }
22:
23: public ObservableCollection<RTVItem> RTVItems
24: {
25: get
26: {
27: return _rTVItems;
28: }
29: set
30: {
31: _rTVItems = value;
32: NotifyPropertyChanged("RTVItems");
33: }
34: }
35:
36: public ICommand OnExpanded { get; set; }
37: public ICommand OnItemClick { get; set; }
38: #endregion
39:
40: #region Constructor(s)
41: public ViewModelRTV()
42: {
43: this.RTVItems = new ObservableCollection<RTVItem>();
44:
45: this.RTVItems.Add(new RTVItem
46: {
47: Title = "I am the Master",
48: RTVItems = new ObservableCollection<RTVItem>()
49: });
50:
51: SelectedRTVItem = RTVItems[0];
52: this.OnExpanded = new DelegateCommand<object>(o => this.OnExpanded_Click(o));
53: this.OnItemClick = new DelegateCommand<object>(o => this.OnItemClick_Click(o));
54: }
55: #endregion
56:
57: #region Member Functions
58: private void OnExpanded_Click(object argument)
59: {
60: // implememntation for device group tree structure
61:
62: SelectedRTVItem = argument as RTVItem;
63: WCFServiceClient _sClient = new WCFServiceClient();
64: _sClient.GetItemsByIDCompleted += new EventHandler<GetItemsByIDCompletedEventArgs>(_sClient_GetItemsByIDCompleted);
65: _sClient.GetItemsByIDAsync(SelectedRTVItem.ID);
66: }
67:
68: void _sClient_GetItemsByIDCompleted(object sender, GetItemsByIDCompletedEventArgs e)
69: {
70: if (e.Result != null)
71: {
72: ObservableCollection<Tree> listTreeItems = (ObservableCollection<Tree>)e.Result;
73: foreach (Tree item in listTreeItems)
74: {
75: SelectedRTVItem.RTVItems.Add(new RTVItem
76: {
77: Title = item.Title,
78: RTVItems = new ObservableCollection<RTVItem>(),
79: ID = item.ID,
80: });
81: }
82: }
83: }
84:
85: private void OnItemClick_Click(object argument)
86: {
87: this.SelectedRTVItem = argument as RTVItem;
88: // implement code for expanding tree (lazy loading)
89: }
90:
91: #endregion
92:
93: #region INotifyPropertyChanged Members
94: public event PropertyChangedEventHandler PropertyChanged;
95: private void NotifyPropertyChanged(String propertyName)
96: {
97: if (PropertyChanged != null)
98: {
99: PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
100: }
101: }
102: #endregion
103: }
In View create a Silverlight user control named UCRadTreeView.xaml, here you will the define the UI for your application in XAML. Following is the xaml code for the View:
1: <UserControl.Resources>
2: <Style x:Key="ExpanderStyle1" TargetType="ToggleButton">
3: <Setter Property="IsEnabled" Value="True" />
4: <Setter Property="IsTabStop" Value="False" />
5: <Setter Property="Cursor" Value="Hand" />
6: <Setter Property="Template">
7: <Setter.Value>
8: <ControlTemplate TargetType="ToggleButton">
9: <Grid>
10: <VisualStateManager.VisualStateGroups>
11: <VisualStateGroup x:Name="CommonStates">
12: <VisualState x:Name="Normal">
13:
14: </VisualState>
15: <VisualState x:Name="MouseOver">
16: <Storyboard>
17: <DoubleAnimation Duration="0:0:0.05"
18: Storyboard.TargetName="Button"
19: Storyboard.TargetProperty="Opacity" To="0" />
20: <DoubleAnimation Duration="0:0:0.05"
21: Storyboard.TargetName="ButtonOver"
22: Storyboard.TargetProperty="Opacity" To="1" />
23: </Storyboard>
24: </VisualState>
25: </VisualStateGroup>
26: <VisualStateGroup x:Name="CheckStates">
27: <VisualState x:Name="Checked">
28: <Storyboard>
29: <DoubleAnimation Duration="0:0:0.05"
30: Storyboard.TargetName="CollapsedVisual"
31: Storyboard.TargetProperty="Opacity" To="0" />
32: <DoubleAnimation Duration="0:0:0.05"
33: Storyboard.TargetName="CollapsedVisualOver"
34: Storyboard.TargetProperty="Opacity" To="0" />
35: </Storyboard>
36: </VisualState>
37:
38: <VisualState x:Name="Unchecked">
39: <Storyboard>
40: <DoubleAnimation Duration="0:0:0.05"
41: Storyboard.TargetName="CollapsedVisual"
42: Storyboard.TargetProperty="Opacity" To="1" />
43: <DoubleAnimation Duration="0:0:0.05"
44: Storyboard.TargetName="CollapsedVisualOver"
45: Storyboard.TargetProperty="Opacity" To="1" />
46: </Storyboard>
47: </VisualState>
48: </VisualStateGroup>
49: </VisualStateManager.VisualStateGroups>
50:
51: <Grid x:Name="Button" Margin="0,7,4,0" HorizontalAlignment="Right"
52: VerticalAlignment="Top" Width="11" Height="11">
53: <Grid.Background>
54: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
55: <GradientStop Color="#3F047BA5" Offset="0.996" />
56: <GradientStop Color="#00000000" Offset="0" />
57: </LinearGradientBrush>
58: </Grid.Background>
59: <Rectangle Stroke="#FF000000" HorizontalAlignment="Left"
60: VerticalAlignment="Top" Width="11" Height="11" />
61:
62: <Rectangle x:Name="CollapsedVisual" Fill="#FF000000"
63: HorizontalAlignment="Left" VerticalAlignment="Top" Width="1"
64: Height="5" Margin="5,3,0,0" />
65: <Rectangle Fill="#FF000000" VerticalAlignment="Top"
66: HorizontalAlignment="Left" Height="1" Width="5"
67: Margin="3,5,0,0" />
68: </Grid>
69:
70: <Grid x:Name="ButtonOver" Margin="0,7,4,0" HorizontalAlignment="Right"
71: VerticalAlignment="Top" Width="11" Height="11">
72: <Rectangle Stroke="#FF167497" HorizontalAlignment="Left"
73: VerticalAlignment="Top" Width="11" Height="11">
74: <Rectangle.Fill>
75: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
76: <GradientStop Color="#26167497" Offset="1" />
77: <GradientStop Color="#00167497" Offset="0" />
78: </LinearGradientBrush>
79: </Rectangle.Fill>
80: </Rectangle>
81: <Rectangle x:Name="CollapsedVisualOver" Fill="#FF167497"
82: HorizontalAlignment="Left" VerticalAlignment="Top" Width="1"
83: Height="5" Margin="5,3,0,0" />
84: <Rectangle Fill="#FF167497" VerticalAlignment="Top"
85: HorizontalAlignment="Left" Height="1" Width="5"
86: Margin="3,5,0,0" />
87: </Grid>
88: </Grid>
89: </ControlTemplate>
90: </Setter.Value>
91: </Setter>
92: </Style>
93: </UserControl.Resources>
94: <Grid x:Name="LayoutRoot" Background="White">
95: <Grid.ColumnDefinitions>
96: <ColumnDefinition/>
97: <ColumnDefinition/>
98: </Grid.ColumnDefinitions>
99: <telerikNavigation:RadTreeView
100: dpprop:ItemExpanded.Command="{Binding OnExpanded}"
101: dpprop:ItemClicked.Command="{Binding OnItemClick}"
102: IsLineEnabled="True"
103: ItemsSource="{Binding RTVItems}"
104: ExpanderStyle="{StaticResource ExpanderStyle1}"
105: IsLoadOnDemandEnabled="True"
106: Grid.Column="0"
107: >
108: <telerikNavigation:RadTreeView.ItemTemplate>
109: <telerik:HierarchicalDataTemplate ItemsSource="{Binding RTVItems}">
110: <StackPanel>
111: <TextBlock Text="{Binding Title}" />
112: </StackPanel>
113: </telerik:HierarchicalDataTemplate>
114: </telerikNavigation:RadTreeView.ItemTemplate>
115: </telerikNavigation:RadTreeView>
116: <telerikNavigation:RadTreeView
117: dpprop:ItemExpanded.Command="{Binding OnExpanded}"
118: dpprop:ItemClicked.Command="{Binding OnItemClick}"
119: IsLineEnabled="True"
120: ItemsSource="{Binding ClientDeviceGroups}"
121: ExpanderStyle="{StaticResource ExpanderStyle1}"
122: IsLoadOnDemandEnabled="True"
123: Grid.Column="1"
124: >
125: <telerikNavigation:RadTreeView.ItemTemplate>
126: <telerik:HierarchicalDataTemplate ItemsSource="{Binding ClientDeviceGroups}">
127: <StackPanel>
128: <TextBlock Text="{Binding DeviceGroupName}" />
129: </StackPanel>
130: </telerik:HierarchicalDataTemplate>
131: </telerikNavigation:RadTreeView.ItemTemplate>
132: </telerikNavigation:RadTreeView>
133: </Grid>
Include the UCRadTreeView.xaml in MainPage.xaml. Following is the code for MainPage.xaml:
1: <UserControl x:Class="RTVOnDemadLoadingMVVM.MainPage"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5: xmlns:local="clr-namespace:RTVOnDemadLoadingMVVM.View"
6: mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
7: <Grid x:Name="LayoutRoot">
8: <local:UCRadTreeView></local:UCRadTreeView>
9: </Grid>
10: </UserControl>
Initialize your view model and set the data context of view in MainPage.xaml.cs. Following is the code for MainPage.xaml.cs:
1: public partial class MainPage : UserControl
2: {
3: public ViewModelRTV _vMRTV;
4: public MainPage()
5: {
6: InitializeComponent();
7: _vMRTV = new ViewModelRTV();
8: this.DataContext = _vMRTV;
9: }
10: }
Press F5………. You are done!!!!
Click here to find the complete source code for this article.
If you find any problem, please feel free to ask questions!!!
Thanks Guys!!!