Siverlight 自定义TreeView 显示带连接线的组织结构树

效果图

 

前台代码

<UserControl x:Class="Hotellight.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Hotellight"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
             
                <ResourceDictionary>
                    <!-- Put all the items inside a horizontally stacked panel -->
                    <ItemsPanelTemplate x:Key="ItemsPanel">
                        <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
                    </ItemsPanelTemplate>

                    <!-- TreeViewItem style to stack a header on top of children -->
                    <Style x:Key="ContainerStyle" TargetType="sdk:TreeViewItem">
                        <Setter Property="ItemsPanel" Value="{StaticResource ItemsPanel}"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="sdk:TreeViewItem">
                                    <StackPanel  VerticalAlignment="Top" HorizontalAlignment="Stretch">
                                        <Canvas>
                                            <Line X1="0" Y1="0" X2="0" Y2="0" StrokeDashArray="2 4" Stroke="#88444444" StrokeThickness="1" local:TreeViewItemConnectingLine.IsSingleConnectingLineOf="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneTime}"></Line>
                                        </Canvas>
                                        <ContentControl local:TreeViewItemConnectingLine.IsHeaderOf="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneTime}"
                                                            x:Name="HeaderContent"
                                                        Content="{TemplateBinding Header}"
                                                        ContentTemplate="{TemplateBinding HeaderTemplate}"
                                                        HorizontalAlignment="Center"
                                                        HorizontalContentAlignment="Center"
                                                        VerticalAlignment="Center"/>
                                     

                                        <ItemsPresenter Canvas.Left="100" Canvas.Top="80" x:Name="Items" VerticalAlignment="Top" HorizontalAlignment="Center"/>

                                    </StackPanel>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>

                    <!-- HeaderContent -->
                    <sdk:HierarchicalDataTemplate x:Key="DepartmentTemplate" ItemsSource="{Binding ChildNodes}" ItemContainerStyle="{StaticResource ContainerStyle}">
                        <Border Background="AliceBlue" Margin="4 8" Padding="2" BorderBrush="Blue" BorderThickness="1" CornerRadius="6">
                            <StackPanel HorizontalAlignment="Stretch">
                                <TextBlock  Text="{Binding Entity.Name}" Margin="6 4" HorizontalAlignment="Center" FontSize="12" FontWeight="Bold"/>
                            </StackPanel>
                        </Border>
                    </sdk:HierarchicalDataTemplate>

                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
           
        </ResourceDictionary>
     

    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:TreeView    ItemTemplate="{StaticResource DepartmentTemplate}"
                        ItemsPanel="{StaticResource ItemsPanel}"
                      ItemContainerStyle="{StaticResource ContainerStyle}"
                        ItemsSource="{Binding}">
          
        </sdk:TreeView>
    </Grid>
</UserControl>

数据类

View Code
  1   public class TreeNode<T> : GalaSoft.MvvmLight.ViewModelBase
2 where T:new()
3 {
4 private T _entity;
5 public T Entity
6 {
7 get
8 {
9 return _entity;
10 }
11 internal set
12 {
13 _entity = value;
14 RaisePropertyChanged("Entity");
15
16 }
17 }
18 private int _level=-1;
19 public int Level
20 {
21 get {
22 return _level;
23 }
24 set {
25 if (_level != value)
26 {
27 _level = value;
28 RaisePropertyChanged("Level");
29 }
30 }
31 }
32 public TreeNode(T entity)
33 {
34 Entity = entity;
35 _childNode = new ObservableCollection<TreeNode<T>>();
36 }
37 private TreeNode<T> _parent;
38 public TreeNode<T> Parent
39 {
40 get
41 {
42 return _parent;
43 }
44 set
45 {
46 if (_parent != value)
47 {
48 _parent = value;
49 RaisePropertyChanged("Parent");
50 }
51 }
52 }
53 private TreeNode<T> _nextNode;
54
55 public TreeNode<T> NextNode
56 {
57 get {
58 return _nextNode;
59 }
60 internal set {
61 if (_nextNode != value)
62 {
63 _nextNode = value;
64 RaisePropertyChanged("NextNode");
65 }
66 }
67 }
68 private TreeNode<T> _previouNode;
69
70 public TreeNode<T> PreviouNode
71 {
72 get {
73 return _previouNode;
74 }
75 internal set {
76 if (_previouNode != value)
77 {
78 _previouNode = value;
79 RaisePropertyChanged("PreviouNode");
80 }
81 }
82 }
83 private ObservableCollection<TreeNode<T>> _childNode;
84 public ObservableCollection<TreeNode<T>> ChildNodes
85 {
86 get
87 {
88 return _childNode;
89 }
90 internal set
91 {
92 _childNode = value;
93 RaisePropertyChanged("ChildNodes");
94 }
95 }
96 }
97 public class TreeCollection<T> : ObservableCollection<TreeNode<T>>
98 where T:new()
99 {
100 public const string DefaultKeyProperty = "ID";
101 public const string DefaultReferenceProperty = "ParentID";
102 internal PropertyInfo KeyProperty
103 {
104 get;
105 set;
106 }
107 internal PropertyInfo ReferenceProperty
108 {
109 get;
110 set;
111 }
112 public static Type TType
113 {
114 get;
115 set;
116 }
117
118 public TreeCollection(string keyProperty, string referenceProperty)
119 {
120 TType = typeof(T);
121 KeyProperty = TType.GetProperty(keyProperty);
122 ReferenceProperty =TType.GetProperty( referenceProperty);
123 T entity = new T();
124 TreeNode<T> node = new TreeNode<T>(entity);
125 this.Items.Add(node);
126 }
127 public TreeCollection(IEnumerable<T> rawDatas,string keyProperty,string referenceProperty):this(keyProperty,referenceProperty)
128 {
129 foreach (var i in rawDatas)
130 {
131 Add(i);
132 }
133 }
134 public TreeCollection(IEnumerable<T> rawDatas)
135 : this(rawDatas, DefaultKeyProperty, DefaultReferenceProperty)
136 { }
137 public TreeCollection() : this(DefaultKeyProperty, DefaultReferenceProperty)
138 { }
139 public TreeNode<T> RootNode
140 {
141 get
142 {
143 return this.Items.Where(i => i.Parent == null).FirstOrDefault();
144 }
145 }
146 public TreeNode<T> Add(T entity)
147 {
148 foreach (var i in this.Items)
149 {
150 var val = KeyProperty.GetValue(i.Entity, null);
151 if(val != null && val.Equals(KeyProperty.GetValue(entity,null)))
152 {
153 return i;
154 }
155 }
156 var parent = FindParentNode(RootNode, entity)??RootNode;
157 var node = new TreeNode<T>(entity);
158 node.Level = parent.Level + 1;
159 var len = parent.ChildNodes.Count();
160 if (len >0)
161 {
162 var prev = parent.ChildNodes[len - 1];
163 node.NextNode = prev.NextNode;
164 prev.NextNode = node;
165 node.PreviouNode = prev;
166 }
167 node.Parent = parent;
168 parent.ChildNodes.Add(node);
169 this.Items.Add(node);
170 return node;
171
172 }
173 public TreeNode<T> AddAt(T entity, int index)
174 {
175 if(index<0)
176 {
177 throw new ArgumentOutOfRangeException("index");
178 }
179
180 foreach (var i in this.Items)
181 {
182 var val = KeyProperty.GetValue(i.Entity, null);
183 if (val.Equals(KeyProperty.GetValue(entity, null)))
184 {
185 return i;
186 }
187 }
188 var parent = FindParentNode(RootNode, entity) ?? RootNode;
189 var node = new TreeNode<T>(entity);
190 node.Parent = parent;
191 var bfcount = parent.ChildNodes.Count;
192
193 var prevIndex = index - 1;
194 var nextIndex = index;
195 if (prevIndex > 0 && prevIndex < bfcount)
196 {
197 var prev = parent.ChildNodes[prevIndex];
198 if (prev != null)
199 {
200 node.PreviouNode = prev;
201 prev.NextNode = node;
202 }
203
204 }
205 if (nextIndex > 0 && nextIndex < bfcount)
206 {
207 var next = parent.ChildNodes[index];
208 if (next != null)
209 {
210 node.NextNode = next;
211 next.PreviouNode = node;
212 }
213 }
214 parent.ChildNodes.Insert(index, node);
215 this.Items.Add(node);
216 return node;
217 }
218
219 public TreeNode<T> Remove(T entity)
220 {
221 var val = KeyProperty.GetValue(entity, null);
222 var node = FindNodeByKeyProperty(val);
223 if (node != null)
224 {
225 if (node.PreviouNode != null)
226 {
227 node.PreviouNode.NextNode = node.NextNode;
228 }
229 node.Parent.ChildNodes.Remove(node);
230 node.Parent = null;
231 this.Items.Remove(node);
232 }
233 return node;
234 }
235 public void MoveTo(TreeNode<T> node, TreeNode<T> parent,int index)
236 {
237 if (parent == null)
238 {
239 return;
240 }
241 var entity = node.Entity;
242 Remove(entity);
243 var val = KeyProperty.GetValue(parent, null);
244 ReferenceProperty.SetValue(entity, val,null);
245 AddAt(entity, index);
246 }
247 public TreeNode<T> this[int level, int index]
248 {
249 get
250 {
251 return this.Items.Where(i => i.Level == level).Skip(index).First();
252 }
253 }
254 public TreeNode<T> FindNodeByKeyProperty(object keyPropertyValue)
255 {
256
257 foreach (var i in this.Items)
258 {
259 if (KeyProperty.GetValue(i, null).Equals(keyPropertyValue))
260 {
261 return i;
262 }
263 }
264 return null;
265 }
266
267 internal TreeNode<T> FindParentNode(TreeNode<T> node,T entity)
268 {
269
270 var keyValue = KeyProperty.GetValue(node.Entity, null);
271 var refValue = ReferenceProperty.GetValue(entity, null);
272 if (refValue.Equals(keyValue))
273 {
274 return node;
275 }
276 else
277 {
278 TreeNode<T> temp = null;
279 foreach (var i in node.ChildNodes)
280 {
281 temp = FindParentNode(i, entity);
282 if (temp != null)
283 {
284 return temp;
285 }
286 }
287 }
288
289 return null;
290 }
291 }

 

后台树控件扩展代码

View Code
  1  public static class TreeViewItemConnectingLine
2 {
3 internal static TreeViewItemSingleLineInfo GetSingleConnectingLine(TreeViewItem element)
4 {
5 if (element == null)
6 {
7 throw new ArgumentNullException("element");
8 }
9 TreeViewItemSingleLineInfo info = element.GetValue(SingleConnectingLineProperty) as TreeViewItemSingleLineInfo;
10 if (info == null)
11 {
12 info = new TreeViewItemSingleLineInfo(element);
13 element.SetValue(SingleConnectingLineProperty, info);
14 }
15 return info;
16 }
17 internal static readonly DependencyProperty SingleConnectingLineProperty =
18 DependencyProperty.RegisterAttached(
19 "SingleConnectingLine",
20 typeof(TreeViewItemSingleLineInfo),
21 typeof(TreeViewItemConnectingLine),
22 new PropertyMetadata(null));
23 public static TreeViewItem GetIsSingleConnectingLineOf(Line element)
24 {
25 if (element == null)
26 {
27 throw new ArgumentNullException("element");
28 }
29 return element.GetValue(IsSingleConnectingLineOfProperty) as TreeViewItem;
30 }
31 public static void SetIsSingleConnectingLineOf(Line element, TreeViewItem value)
32 {
33 if (element == null)
34 {
35 throw new ArgumentNullException("element");
36 }
37 element.SetValue(IsSingleConnectingLineOfProperty, value);
38 }
39 public static readonly DependencyProperty IsSingleConnectingLineOfProperty =
40 DependencyProperty.RegisterAttached("IsSingleConnectingLineOf",
41 typeof(TreeViewItem),
42 typeof(TreeViewItemConnectingLine),
43 new PropertyMetadata(null, OnIsSingleConnectingLineOfPropertyChanged));
44 private static void OnIsSingleConnectingLineOfPropertyChanged(DependencyObject d,
45 DependencyPropertyChangedEventArgs e)
46 {
47 Line source = d as Line;
48 TreeViewItem value = e.NewValue as TreeViewItem;
49 if (value != null)
50 {
51 TreeViewItemSingleLineInfo info = GetSingleConnectingLine(value);
52 info.ConnectingLine = source;
53 }
54 else
55 {
56 value = e.OldValue as TreeViewItem;
57 if (value != null)
58 {
59 TreeViewItemSingleLineInfo info = GetSingleConnectingLine(value);
60 info.ConnectingLine = null;
61 }
62 }
63 }
64
65 public static TreeViewItem GetIsHeaderOf(FrameworkElement element)
66 {
67 if (element == null)
68 {
69 throw new ArgumentNullException("element");
70 }
71 return element.GetValue(IsHeaderOfProperty) as TreeViewItem;
72 }
73 public static void SetIsHeaderOf(FrameworkElement element, TreeViewItem value)
74 {
75 if (element == null)
76 {
77 throw new ArgumentNullException("element");
78 }
79 element.SetValue(IsHeaderOfProperty, value);
80 }
81 public static readonly DependencyProperty IsHeaderOfProperty =
82 DependencyProperty.RegisterAttached(
83 "IsHeaderOf",
84 typeof(TreeViewItem),
85 typeof(TreeViewItemConnectingLine),
86 new PropertyMetadata(null, OnIsHeaderOfPropertyChanged));
87 private static void OnIsHeaderOfPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
88 {
89 FrameworkElement source = d as FrameworkElement;
90 TreeViewItem value = e.NewValue as TreeViewItem;
91
92 if (value != null)
93 {
94 TreeViewItemSingleLineInfo info = GetSingleConnectingLine(value);
95 info.Header = source;
96 }
97 else
98 {
99 value = e.OldValue as TreeViewItem;
100 if (value != null)
101 {
102 TreeViewItemSingleLineInfo info = GetSingleConnectingLine(value);
103 info.Header = null;
104 }
105 }
106 }
107
108 }
109
110 public class TreeViewItemSingleLineInfo
111 {
112 public TreeViewItem Item { get; private set; }
113 public Line ConnectingLine { get; set; }
114 public FrameworkElement Header { get; set; }
115 public TreeViewItemSingleLineInfo(TreeViewItem item)
116 {
117 Debug.Assert(item != null, "item should not be null!");
118 Item = item;
119 item.LayoutUpdated += (s, e) => PositionConnectingLines("LayoutUpdated");
120 item.Expanded += (s, e) => PositionConnectingLines("Expanded");
121 item.Collapsed += (s, e) => PositionConnectingLines("Collapsed");
122 item.ItemContainerGenerator.ItemsChanged += (s, e) => PositionConnectingLines("ItemsChanged");
123
124 }
125
126 private void PositionConnectingLines(string eventName)
127 {
128 if (Item.GetIsRoot())
129 {
130
131 return;
132 }
133 if (ConnectingLine == null)
134 {
135 return;
136
137 }
138 var parentTreeViewItem = Item.GetParentTreeViewItem();
139 if (parentTreeViewItem == null) { return; }
140
141 var fromElement = TreeViewItemConnectingLine.GetSingleConnectingLine(parentTreeViewItem).Header;
142 if (fromElement == null)
143 {
144 ConnectingLine.Visibility = Visibility.Collapsed;
145 return;
146 }
147 Rect? bound = fromElement.GetBoundsRelativeTo(ConnectingLine);
148 ConnectingLine.X1 = bound.Value.X+bound.Value.Width/2;
149 ConnectingLine.Y1 = bound.Value.Y+bound.Value.Height/2;
150
151 bound = Header.GetBoundsRelativeTo(ConnectingLine);
152
153 ConnectingLine.X2 = bound.Value.X + bound.Value.Width / 2;
154 ConnectingLine.Y2 = bound.Value.Y + bound.Value.Height / 2;
155
156 ConnectingLine.Visibility = Visibility.Visible;
157
158 }
159 }

 

 


 

posted @ 2011-10-06 13:23  mayamoon  阅读(832)  评论(1)    收藏  举报