一路下来,ANode的大体结构基本要完工了,我之所以说这是个结构,因为,这是一次

渐进式开发,各个部分的处理方式,大体上已经确定下来了,如有后续内容,将依照现在

的模式去做。

关于 动画 的 触发

今天说一下代码中触发动画,这个话题其实很短,那就加上,ANode的一个简单测试环境

其实也是,后面我们UI主窗口的一个基本结构。

 

从代码中触发动画其实只有一句话,简单到不行。

BeginStoryboard(Me.Resources.Item("SelectedStatus"))

SelectedStatus就是我们当初在Xaml中定义的画板,用BeginStoryboard执行一下,就

Ok。

 

ANode的事件触发,目前的几种状态触发,我都是通过代码实现的,因为,在触发的同时

要抛出事件,还有设置状态标识,为了统一起见,我都放在代码中实现了。

还是列表的形式来说明各个状态是在什么情况下出现的吧

ANode的状态触发条件

 

 

 

状态

触发条件

附加条件

响应

选中状态

鼠标进入ANode

非编辑状态

1.触发动画


2.设置当前ANode为活动


(关系到失去焦点的ANode,恢复正常状态)


3.抛出OnSelected事件

 

鼠标在NodeGrid上抬起


1.触发动画


2.抛出DragEnd事件

拖放状态

鼠标在NodeGrid上按下


1.触发动画


2.抛出DragStart事件

编辑状态

鼠标在NodeTextInput上抬起


1.触发动画


2.设置IsEdit标志位

正常状态

自定义方法SetNormal


1.触发动画


2.设置IsEdit=False

 

在这里要先说明一下,ANode在主界面上,会有很多很多,但是,每次选中的只有一个,

不会存在多选的形式(至少目前,我所预期的方式不会),那么当ANode被选中时,将取消

上一个被选中的ANode,在这里我用了一个Shared方法,来实现同一的管理。

1     Private Shared ActiveNode As ANode
 2 
 3     Private Shared Sub SetActiveNode(ByVal n As ANode)
 4         If Not ActiveNode Is Nothing Then
 5             If Not ActiveNode.Equals(n) Then
 6                 ActiveNode.ZIndex = 0
 7                 ActiveNode.SetNormal()
 8             End If
 9         End If
10         ActiveNode = n
11         If Not n Is Nothing Then
12             n.ZIndex = 1
13         End If
14     End Sub
15 
16     Public Shared Sub SetAllNormal()
17         SetActiveNode(Nothing)
18     End Sub

一个静态的变量标识这整个ANode群的活动单元,一个私有的SetActiveNode,

用来当当前单元被选中时将自己设置为活动单元,并取消前一个单元的活动状态。

并且,加了一个SetAllNormal方法,给外部提供的全部取消活动的法。

 

以下是各个状态相关的方法和变量的代码

 

1 
 2 #Region "   选择状态    "
 3     Public Event OnSelected(ByVal sender As ANode)
 4 
 5     Private Sub NodeGrid_MouseEnter(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles NodeGrid.MouseEnter
 6         If IsEdit Then Exit Sub
 7         SetActiveNode(Me)
 8         BeginStoryboard(Me.Resources.Item("SelectedStatus"))
 9         RaiseEvent OnSelected(Me)
10     End Sub
11 #End Region
12 
13 #Region "   拖放状态   "
14     Public Event OnDragStart(ByVal sender As ANode, ByVal DragPoint As Point)
15     Public Event OnDragEnd(ByVal sender As ANode)
16 
17     Private Sub NodeGrid_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles NodeGrid.MouseDown
18         BeginStoryboard(Me.Resources.Item("DragStatus"))
19         RaiseEvent OnDragStart(Me, e.GetPosition(sender))
20         e.Handled = True
21     End Sub
22 
23     Private Sub NodeGrid_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles NodeGrid.MouseUp
24         BeginStoryboard(Me.Resources.Item("SelectedStatus"))
25         RaiseEvent OnDragEnd(Me)
26     End Sub
27 #End Region
28 
29 #Region "   编辑状态    "
30     Private IsEdit As Boolean = False
31     Private Sub NodeTextInput_PreviewMouseDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles NodeTextInput.PreviewMouseDown
32         BeginStoryboard(Me.Resources.Item("EditStatus"))
33         IsEdit = True
34     End Sub
35 #End Region
36 
37 #Region "   普通状态    "
38     Public Sub SetNormal()
39         BeginStoryboard(Me.Resources.Item("NormalStatus"))
40         IsEdit = False
41     End Sub
42 #End Region
43 
44 #Region "   ActiveNode  "
45     Private Shared ActiveNode As ANode
46 
47     Private Shared Sub SetActiveNode(ByVal n As ANode)
48         If Not ActiveNode Is Nothing Then
49             If Not ActiveNode.Equals(n) Then
50                 ActiveNode.ZIndex = 0
51                 ActiveNode.SetNormal()
52             End If
53         End If
54         ActiveNode = n
55         If Not n Is Nothing Then
56             n.ZIndex = 1
57         End If
58     End Sub
59 
60     Public Shared Sub SetAllNormal()
61         SetActiveNode(Nothing)
62     End Sub
63 #End Region

至此,ANode貌似就差不多介绍完了

哦,还有几个属性,Text,Background,Foreground,ZIndex

为了可以支持绑定,这些属性都是DependencyProperty。

代码也贴一下。

1 
  2 #Region "   Text  DependencyProperty "
  3     ''' <summary>
  4     ''' PropertyComment
  5     ''' </summary>
  6     ''' <remarks></remarks>
  7     Public Shared ReadOnly TextProperty As DependencyProperty = _
  8         DependencyProperty.Register(
  9             "Text", GetType(String), GetType(ANode), New PropertyMetadata( _
 10                 "", New PropertyChangedCallback(AddressOf TextPropertyChanged_CallBack)))
 11 
 12     Public Property Text() As String
 13         Get
 14             Return GetValue(TextProperty)
 15         End Get
 16         Set(ByVal Value As String)
 17             SetValue(TextProperty, Value)
 18         End Set
 19     End Property
 20 
 21     Public Shared Sub TextPropertyChanged_CallBack(ByVal dp As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
 22 
 23     End Sub
 24 #End Region
 25 
 26 #Region "   BackgroundColor  DependencyProperty "
 27     ''' <summary>
 28     ''' PropertyComment
 29     ''' </summary>
 30     ''' <remarks></remarks>
 31     Public Shared ReadOnly BackgroundColorProperty As DependencyProperty = _
 32         DependencyProperty.Register(
 33             "BackgroundColor", GetType(Brush), GetType(ANode), New PropertyMetadata( _
 34                 Brushes.LightBlue, New PropertyChangedCallback(AddressOf BackgroundColorPropertyChanged_CallBack)))
 35 
 36     Public Property BackgroundColor() As Brush
 37         Get
 38             Return GetValue(BackgroundColorProperty)
 39         End Get
 40         Set(ByVal Value As Brush)
 41             SetValue(BackgroundColorProperty, Value)
 42         End Set
 43     End Property
 44 
 45     Public Shared Sub BackgroundColorPropertyChanged_CallBack(ByVal dp As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
 46         Dim an As ANode = CType(dp, ANode)
 47         an.NodeBackground.Background = e.NewValue
 48         an.NodeBackground.BorderBrush = e.NewValue
 49         With CType(e.NewValue, SolidColorBrush).Color
 50             Dim brightness As Integer = Math.Max(.R, Math.Max(.G, .B))
 51             If brightness < 127 Then
 52                 an.ForegroundColor = Brushes.White
 53             Else
 54                 an.ForegroundColor = Brushes.Black
 55             End If
 56         End With
 57     End Sub
 58 #End Region
 59 
 60 #Region "   ForegroundColor  DependencyProperty "
 61     ''' <summary>
 62     ''' PropertyComment
 63     ''' </summary>
 64     ''' <remarks></remarks>
 65     Public Shared ReadOnly ForegroundColorProperty As DependencyProperty = _
 66         DependencyProperty.Register(
 67             "ForegroundColor", GetType(Brush), GetType(ANode), New PropertyMetadata( _
 68                 Brushes.Black, New PropertyChangedCallback(AddressOf ForegroundColorPropertyChanged_CallBack)))
 69 
 70     Public Property ForegroundColor() As Brush
 71         Get
 72             Return GetValue(ForegroundColorProperty)
 73         End Get
 74         Set(ByVal Value As Brush)
 75             SetValue(ForegroundColorProperty, Value)
 76         End Set
 77     End Property
 78 
 79     Public Shared Sub ForegroundColorPropertyChanged_CallBack(ByVal dp As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
 80         Dim an As ANode = CType(dp, ANode)
 81         an.NodeText.Foreground = e.NewValue
 82     End Sub
 83 #End Region
 84 
 85 #Region "   ZIndex  DependencyProperty "
 86     ''' <summary>
 87     ''' PropertyComment
 88     ''' </summary>
 89     ''' <remarks></remarks>
 90     Public Shared ReadOnly ZIndexProperty As DependencyProperty = _
 91         DependencyProperty.Register(
 92             "ZIndex", GetType(Double), GetType(ANode), New PropertyMetadata( _
 93                 0.0, New PropertyChangedCallback(AddressOf ZIndexPropertyChanged_CallBack)))
 94 
 95     Public Property ZIndex() As Double
 96         Get
 97             Return GetValue(ZIndexProperty)
 98         End Get
 99         Set(ByVal Value As Double)
100             SetValue(ZIndexProperty, Value)
101         End Set
102     End Property
103 
104     Public Shared Sub ZIndexPropertyChanged_CallBack(ByVal dp As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
105 
106     End Sub
107 #End Region

Ok,ANode就完事儿了。

那么就搞一个对ANode的测试页面吧。

测试页 的 构建

建立一个Wpf应用程序工程

MainWindowViewModel

1 Imports System.Collections.ObjectModel
 2 
 3 Public Class MainWindowViewModel
 4     Inherits NotificationObject
 5     '
 6     'NodeList As ObservableCollection(Of Node)
 7     '
 8     Private mNodeList As ObservableCollection(Of Node)
 9     Public Property NodeList() As ObservableCollection(Of Node)
10         Get
11             If mNodeList Is Nothing Then
12                 mNodeList = New ObservableCollection(Of Node)
13             End If
14             Return mNodeList
15         End Get
16         Set(ByVal Value As ObservableCollection(Of Node))
17             mNodeList = Value
18             RaisePropertyChanged("NodeList")
19         End Set
20     End Property
21 
22     Public Sub New()
23         Dim newNode As New Node
24         newNode.Text = "新主题"
25         newNode.Top = 100
26         newNode.Left = 50
27         newNode.ZIndex = 0
28         newNode.Background = Brushes.LightCoral
29         NodeList.Add(newNode)
30         Dim node2 As New Node
31         node2.Text = "副本"
32         node2.Left = 200
33         node2.Top = 100
34         node2.ZIndex = 0
35         node2.Background = Brushes.Black
36         NodeList.Add(node2)
37         Dim node3 As New Node
38         With node3
39             .Text = "副本"
40             .Left = 300
41             .Top = 100
42             .ZIndex = 0
43             .Background = Brushes.Black
44         End With
45 
46         NodeList.Add(node3)
47     End Sub
48 
49 End Class
50

有一个用于绑定的NodeList属性。

 

为了简便起见,我自定义了NotificationObject类,而并没有引入Prism工具,

在ViewModel文件夹中,以后还是再建一个文件夹存放吧,现在稍显凌乱,也无伤大雅

关于NotificationObject,给不了解的同学补充一点东西吧,看代码。

1 Public Class NotificationObject
 2     Implements ComponentModel.INotifyPropertyChanged
 3 
 4     Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
 5 
 6     Public Sub RaisePropertyChanged(ByVal propertyName As String)
 7         RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
 8     End Sub
 9 End Class
10

MVVM中,为了让绑定的属性跟控件属性建立真正的联系也就是说,当被绑定属性(ViewModel

或Model中的属性)变化时,在控件上有所体现,Model或ViewModel必须实现

INotifyPropertyChanged接口,以便于通知控件属性变化,在ViewModel或Model中的属性

要这样写

1 
 2     Private mText As String
 3     Public Property Text() As String
 4         Get
 5             Return mText
 6         End Get
 7         Set(ByVal Value As String)
 8             mText = Value
 9             RaisePropertyChanged("Text")
10         End Set
11     End Property
12

第9行,就是用来通知控件的。当然这里也要强调,控件属性必须是DependencyProperty才

能够被绑定。

Node类 对应与ANode,实现了几个需要被存储,和用于操作ANode在界面上的效果的几个属性。

这里,还有待于探讨,因为为了简便实现,Node参与了UI层的东西,而配合操作ANodeUI元素的

东西应该出现在ViewModel层。

 

1 
 2 Public Class Node
 3     Inherits NotificationObject
 4 
 5     '
 6     'Text As String
 7     '
 8     Private mText As String
 9     Public Property Text() As String
10         Get
11             Return mText
12         End Get
13         Set(ByVal Value As String)
14             mText = Value
15             RaisePropertyChanged("Text")
16         End Set
17     End Property
18 
19     '
20     'Left As Double
21     '
22     Private mLeft As Double
23     Public Property Left() As Double
24         Get
25             Return mLeft
26         End Get
27         Set(ByVal Value As Double)
28             mLeft = Value
29             RaisePropertyChanged("Left")
30         End Set
31     End Property
32 
33     '
34     'Top As Double
35     '
36     Private mTop As Double
37     Public Property Top() As Double
38         Get
39             Return mTop
40         End Get
41         Set(ByVal Value As Double)
42             mTop = Value
43             RaisePropertyChanged("Top")
44         End Set
45     End Property
46 
47     '
48     'ZIndex As integer
49     '
50     Private mZIndex As Integer = 0
51     Public Property ZIndex() As Integer
52         Get
53             Return mZIndex
54         End Get
55         Set(ByVal Value As Integer)
56             mZIndex = Value
57             RaisePropertyChanged("ZIndex")
58         End Set
59     End Property
60 
61     '
62     'Background As Brush
63     '
64     Private mBackground As Brush = Brushes.LightBlue
65     Public Property Background() As Brush
66         Get
67             Return mBackground
68         End Get
69         Set(ByVal Value As Brush)
70             mBackground = Value
71             RaisePropertyChanged("Background")
72         End Set
73     End Property
74 
75 End Class

 

跨界的属性也只是ZIndex而已,如果实在没办法,就这样吧,唉唉。

 

MainWindowViewModel

1 Imports System.Collections.ObjectModel
 2 
 3 Public Class MainWindowViewModel
 4     Inherits NotificationObject
 5     '
 6     'NodeList As ObservableCollection(Of Node)
 7     '
 8     Private mNodeList As ObservableCollection(Of Node)
 9     Public Property NodeList() As ObservableCollection(Of Node)
10         Get
11             If mNodeList Is Nothing Then
12                 mNodeList = New ObservableCollection(Of Node)
13             End If
14             Return mNodeList
15         End Get
16         Set(ByVal Value As ObservableCollection(Of Node))
17             mNodeList = Value
18             RaisePropertyChanged("NodeList")
19         End Set
20     End Property
21 
22     Public Sub New()
23         Dim newNode As New Node
24         newNode.Text = "新主题"
25         newNode.Top = 100
26         newNode.Left = 50
27         newNode.ZIndex = 0
28         newNode.Background = Brushes.LightCoral
29         NodeList.Add(newNode)
30         Dim node2 As New Node
31         node2.Text = "副本"
32         node2.Left = 200
33         node2.Top = 100
34         node2.ZIndex = 0
35         node2.Background = Brushes.Black
36         NodeList.Add(node2)
37         Dim node3 As New Node
38         With node3
39             .Text = "副本"
40             .Left = 300
41             .Top = 100
42             .ZIndex = 0
43             .Background = Brushes.Black
44         End With
45 
46         NodeList.Add(node3)
47     End Sub
48 
49 End Class
50

 

看看MainWindow的代码吧

先是Xaml

1 <Window x:Class="MainWindow" x:Name="mainWindow"
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:ct="clr-namespace:AMindMapControls;assembly=AMindMapControls"
 5     Title="MainWindow" Height="350" Width="525">
 6     <Grid Background="Transparent">        
 7         <ItemsControl Name="NodeLayer" ItemsSource="{Binding NodeList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
 8             <ItemsControl.Template>
 9                 <ControlTemplate TargetType="{x:Type ItemsControl}">
10                     <ItemsPresenter/>
11                 </ControlTemplate>
12             </ItemsControl.Template>            
13             <ItemsControl.ItemContainerStyle>
14                 <Style TargetType="{x:Type ContentPresenter}">
15                     <Setter Property="Canvas.Left" Value="{Binding Path=Left}"></Setter>
16                     <Setter Property="Canvas.Top" Value="{Binding Path=Top}"></Setter>
17                     <Setter Property="Canvas.ZIndex" Value="{Binding Path=ZIndex}"></Setter>
18                 </Style>
19             </ItemsControl.ItemContainerStyle>
20             <ItemsControl.ItemTemplate>
21                 <DataTemplate>
22                     <ct:ANode x:Name="Node" Text="{Binding Text}"                               
23                               OnDragStart="ANode_OnDragStart"
24                               OnDragEnd="ANode_OnDragEnd"   
25                               ZIndex="{Binding ZIndex, Mode=OneWayToSource}"
26                               BackgroundColor ="{Binding Background}"                              
27                               >                       
28                        
29                     </ct:ANode>
30                 </DataTemplate>
31             </ItemsControl.ItemTemplate>
32             <ItemsControl.ItemsPanel>
33                 <ItemsPanelTemplate>
34                     <Canvas Name="NodeCanvas" Background="Transparent"
35                             MouseMove="NodeCanvas_MouseMove"
36                             MouseDown="NodeCanvas_MouseDown">
37                     </Canvas>
38                 </ItemsPanelTemplate>
39                 </ItemsControl.ItemsPanel>
40         </ItemsControl>
41     </Grid>
42 </Window>
43

Canvas是一个方便通过坐标来控制控件位置的容器,所以,像MindMap还是最好用Canvas

来实现,但是问题就来了,Canvas并不能绑定列表或表格形式的数据。而我们的Node是

列表形式的,也是为了方便存储。所以采用ItemsContorl来实现Canvas的列表数据绑定,

为什么要这么做呢,首先,从操作上看,ANode的个数不是固定的,而且会反复的增加和减少

如果自己管理ANode的增加和减少的话,将是不胜其烦的事情,而列表绑定,就变得非常轻松

了,只要对后台模型进行操作,即可实现UI层的对象的增添。也简化每个ANode对应后台的

绑定过程,像我这种懒人,简直是不二之选,哪怕打破UI和Model的松耦合,实际上,目前来看

处理有一个迂回的绑定(详见另一Post WPF 苦逼的迂回绑定),也没有其他的问题。

 

分解一下,MainWindow.Xaml

最外层Grid,背景被设置为Transparent,如果不设置任何背景色,将无法获得鼠标事件的支持。

我们还要拖动ANode。接下来是ItemsContorl,对于ItemsContorl和Canvas的关系,当然还有

ANode,请参考另三个Pos 将ObservableCollection(Of T) 数据 绑定到 Canvas (2)(3)

 

好吧,MainWindow.Xaml没什么好讲的了。

看看后台

1 Imports AMindMapControls
 2 
 3 Class MainWindow
 4     Private vm As New MainWindowViewModel
 5 
 6     Public Sub New()
 7 
 8         ' 此调用是设计器所必需的。
 9         InitializeComponent()
10 
11         ' 在 InitializeComponent() 调用之后添加任何初始化。
12         Me.DataContext = vm
13     End Sub
14 
15 #Region "   拖放  "
16     Private DragPoint As Point
17     Private DragNode As ANode
18 
19     Private Sub NodeCanvas_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseEventArgs)
20         If DragNode Is Nothing Then Exit Sub
21         With CType(DragNode.DataContext, Node)
22             .Left = e.GetPosition(sender).X - DragPoint.X
23             .Top = e.GetPosition(sender).Y - DragPoint.Y
24         End With
25     End Sub
26 
27     Private Sub ANode_OnDragStart(ByVal sender As AMindMapControls.ANode, ByVal p As Point)
28         DragPoint = p
29         DragNode = sender
30     End Sub
31 
32     Private Sub ANode_OnDragEnd(ByVal sender As AMindMapControls.ANode)
33         DragNode = Nothing
34     End Sub
35 #End Region
36 
37     Private Sub NodeCanvas_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
38         ANode.SetAllNormal()
39     End Sub
40 
41 End Class
42

Wpf的事件传递 真是太复杂了,所以,用OnDragStart和OnDragEnd事件来标记拖拽的开始

和结束,并且在界面空白处按下鼠标,取消所有ANode的活动状态,测试完成,效果很理想

 

下期预告,为 脑图节点,建立后台模型和对模型进行基本管理。其实Node就是模型。