返回首页

示例代码
目录 {A}  60;  0;
本文探讨了使用和实施这是用来显示和编辑网络,图形和流图。NetworkView,我把它称为,是启发和标准WPF有许多相似之处,如控制。文章和示例代码显示了如何使用控制代码。 这篇文章是安排在两个主要部分。
检查NetworkView用法与两个示例项目的演练。这部分和参考部分是不够的,如果你只是想使用NetworkView。
的实施细节。如果你想使自己的修改,以NetworkView或如果你想了解我的方法,开发一个复杂的,这将是有益的
在文章的结尾是一个描述暴露出来的公共属性,方法和命令NetworkView。
我已经涵盖一些WPF的技术:最近。NetworkView和示例应用程序,使所有这些技术的使用。我不会详细覆盖这些技术。相反,在适当的情况下,我会参考以前的文章。
NetworkView是为了应用程序特定的支持。这是可能的,虽然我不建议这样做,编程方式或在XAML中使用NetworkView没有视图模型。这是类似如其他WPF TreeView控件您也可以通过没有视图模型。使用视图模型,它的目的是要使用的方式,所以这是我们将关注。简单的示例使用一个简单的视图模型,而先进的样品扩展视图模型,并增加了新功能。乔什 - 史密斯有很大的显示视图模型可以提高你的经验与WPF的TreeView。
这个屏幕截图显示了在先进的样品中创建一个简单的图形。主窗口显示上图的内容和它下面的概述窗口显示整个画布。
{S0}
我会假设你已经知道C#中,至少有一个使用的基本知识 。了解也将有助于虽然我会尽我所能,以填补在沿途的一些细节,并提供学习资源。
乔什 - 史密斯和萨沙理发是一个良好的开端,如果你想要一个WPF底漆。是满有趣的WPF的信息片段,并有一些伟大的图表,可以帮助解释WPF的概念。
乔什 - 史密斯也有一个{A35}如果你已经知道了WPF和MVVM位,我可以推荐阅读{A36},我刚刚读到它,发现它有帮助的进展我的MVVM的知识和经验。{A37}
我创建了一个爱好项目,该项目所需的图形编辑NetworkView控制。我几乎没有使用WPF的以前,所以想使用它的学习经验。我以为,使用WPF会比尝试它更容易{A38}。它在许多方面是比较容易,虽然我花了更长的时间比我预期,在我的头上WPF的权利,这主要是来了解WPF的设计理念。
正如我爬上了学习曲线,我意识到,WPF是复杂的,有时让你的脑袋要爆炸,但大部分很有意义。WPF是一个复杂和强大的的野兽,在我看来还需要很多改进,但到目前为止,这是最好的方式发展,我曾尝试的UI。
在NetworkView,我的第一次尝试通过黑客在一起的组合一个UserControl和来自不同的自定义UI元素{A39}。本文中的代码了我NetworkView第二次尝试,虽然它的发展已经了一段时间的进展,并已通过的演变,重构和改善。{A40}
NetworkView目的是可视化和编辑图表,流动图表和网络。它几乎不用说,你应该能够添加和删除节点,移动节点约,建立多节点之间的连接。我希望它有高reusablilty和在其他WPF控件相同的方式进行定制。这包括使用{A41},{A42}{A43}自定义网络的内容。 为NetworkView API的目标是要recognisably类似的标准WPF控件。然而,它在几个关键的方式不同,但在可能的情况下,按照建立好的规范。
为了让事情变得简单我有没有试图实现任何种类的{A44}。不幸的是,这可能会限制可以加载到控制网络的大小。{A45}
本节介绍的主要概念和组件。
网络节点和连接的集合:   ; & #160;
节点的主要组件:  60; {S2}   ;
通过连接节点是相互联系的:   ; &# 160; {S3}  0;
连接器,可以附加连接的节点上的锚点。
 60; {S4}的  60;
一个节点可以有一个或多个连接。每个连接都有两个端点每个可能锚定连接器。但是,当用户拖动出一个新的连接只有一个终点是固定连接器另一种是固定的鼠标光标的当前位置。
NetworkView本身的地方没有任何限制,连接的方向性然而,它标签作为源和目标连接的两端,但这些标签的含义由应用程序定义。
在这两个示例项目的源和目标标签具有以下涵义:
{五}
节点,连接器和连接都是完全可换肤的使用方式,{A46}数据模板。 事实上,有没有预设任何的图形表示这是东西,完全是特定应用。{A47}
本节给出了一个解决方案中的样本项目的摘要。提取NetworkViewSampleCode.zip和在Visual Studio中打开NetworkViewSampleCode.2008.sln(我仍然使用VS 2008,但对于VS 2010中,您可以使用NetworkViewSampleCode.2010.sln)。NetworkView,示例应用程序和所有支持代码在此解决方案。
{中六}
这是一个很好的点,为您构建和运行SimpleSample和AdvancedSample。这两个示例应用程序启动的自述!窗口指示的功能和输入绑定。
下面是项目的摘要(按字母顺序排列):  60; AdornedControl  60; 包含研究AdornedControl {A48}。  60; 先进的样品使用此控件来显示 删除连接和删除节点的鼠标悬停按钮。 &# 160;   ; AdvancedNetworkModel   ; AdvancedSample使用视图的模型。  0; & #160; AdvancedSample & #160; 先进的示例应用程序。基础上的功能 介绍了简单的示例,并增加了更多的功能和改进的视觉效果。 0; &# 160;  0; NetworkUI &# 160; 包含NetworkView和支持UI元素类。  0; SimpleNetworkModel & #160;  0; SimpleSample使用视图的模型。 &# 160; SimpleSample   ; &# 160;简单的示例应用程序。这表明NetworkView使用 & #160; 只有基本功能和视觉效果。  60; & #160; UTILS & #160;  0; 实用工具类和其他项目所用的方法。  0; &# 160; ZoomAndPan  0; & #160; 包含研究ZoomAndPanControl  0; {A49}。 这是通过AdvancedSample使用添加到NetworkView缩放和平移功能。 {A50}
本节是从NetworkUI项目的主类的概述(一些帮助{七} & #160;
NetworkView是主类 包括直接在XAML。 它源于 {A52}。
& #160; 节点和连接指定  60; 要显示在网络节点和连接的集合。 这些都类似于   ; {A53}  0; 财产 。 & #160;  60;
  ; NodesSource和ConnectionsSource   ; 指定的数据源(又名视图模型),是用来填充节点和连接。 这些都类似于 {A55} &# 160; 财产 &# 160; ItemsControl的。 & #160;  60;
NetworkView有其他属性,方法,事件和命令,我们将看看 &# 160; 在对这篇文章的过程中。 &# 160; 的完整列表,请参阅在文章的末尾。  0; & #160; {S8}  60;
NodeItem是UI元素,表示在节点 {A57}。 它源于   ; {A58}和节点集合是由  0; {A59}  60; 这是嵌入式NetworkView的视觉树。   ;
最终ListBoxItem的来自 {A60} 这给NodeItem的能力 {A61}。 &# 160;   ;
NodeItem已用于定位节点内的属性X和Y  0; 在 &# 160;{A62} 主机节点。 zIndex属性用于设置节点的前端到订购。  0;  0; {S9}
&# 160; ConnectorItem是代表了连接器的UI元素 视觉树。它源于ContentControl的,也可以作为主机应用程序定义的视觉内容。& #160;  60;
  ; ConnectorItem的主要目的是要找出一个节点的视觉树内的连接器 & #160; 并允许应用程序的视图模型检索(通过数据绑定)该连接器的位置, 或者,我把它称为连接器的热点。  60; ConnectorItem转换的中心点坐标 &# 160; 然后父NetworkView和价值体系 &# 160; 使用应用程序的视图模型作为一个连接的终点。 & #160;
ConnectorItem也用于定义连接器的外观。 {A63}
这部分的文章探讨了两个示例应用程序。
SimpleSample演示了一个简单的流程图或数据流图。视图模型提供数据NetworkView和UI是使用样式和数据模板。每个节点是有限的四个连接,每个节点的边缘。通过上下文菜单创建新的节点。新的连接创建拖动两个连接器之间的连接。用户可以拖动现有节点移动它们,也可以删除它们。为节点,连接器和连接的视觉效果硬要把单纯的。
AdvancedSample使用更复杂的视图模式,增加了功能,更令人印象深刻的视觉效果。现在有两种类型的连接器:输入和输出。每个节点可以有任意数量的连接器。现在,用户可以删除连接。我使用的adorners显示删除连接删除节点按钮,当用户鼠标悬停连接或节点。缩放和平移功能,让我们可以轻松地浏览网络是大于可见区域。{A64}
如果你还没有准备好,打开NetworkViewSampleCode.sln确保SimpleSample设置为启动项目,然后建立和运行应用程序。花一些时间来探索应用程序。
本演练是使用NetworkView的第一个例子。在查看模型的快速查找后,我们将着眼于如何将数据绑定视图模型到NetworkView在XAML。XAML后,我们将打开代码,我们将看到如何创建和删除节点。然后我们将看看如何创建连接。
下面是一个例子简单的示例网络:
{S10}
这是最引人注目的,视觉效果很简单。此外,每个节点只有四个连接器,不能多也不能少。当创造上没有任何限制的连接的连接器可以连接到这可以创建两个连接器之间的任何一个连接。
视图模型
此图显示了从SimpleNetworkModel项目的视图模型类:
& #160; {S11} &# 160;  0; &# 160; NetworkViewModel  60;   ; & #160;  60;   ; & #160; 节点和连接的容器。 &# 160;  0; NodeViewModel   ; & #160;  60;   ; & #160;
一个名为网络中的节点和节点的连接器的容器。   ; & #160;  60;
 0; Name属性获取/设置节点的名称。 &# 160;  0;
& #160; X和Y是数据绑定的,我们很快就会看到 & #160; X和Y和的NodeItem因此 内NetworkView的节点的位置。 & #160;   ;
IsSelected是数据绑定到IsSelected属性,NodeItem & #160; 继承ListBoxItem的。这允许我们以编程方式控制,并确定   ; 节点的选择状态。   ;
 0; 连接器是连接器的集合,指定&# 160; 节点的连接锚点。虽然这是一个集合 &# 160; 连接器,它必须始终只包含四个连接器。 &# 160; 这是为了简化视图模型上放置一个limitiation &# 160; 节点的数据模板。 & #160;  0;
  ; AttachedConnections检索集合 目前连接到节点的连接。  60;   ; & #160;  0; ConnectorViewModel  0; &# 160;  0;
& #160; 一个附加连接的节点上的定位点。 &# 160;  0;
  ; ParentNode引用连接器的节点拥有。 &# 160;
AttachedConnection引用的连接,如果有的话,是  0; 目前连接(或挂靠)连接器。
& #160; 热点是连接器,又称作为连接器的热点的位置。   ; 此值的计算方法是ConnectorItem & #160; 然后推到通过数据绑定模型。  0; &# 160;   ; & #160; ConnectionViewModel  60;   ; & #160;  60;   ;
一个连接两个节点之间(两个连接器)。 &# 160;  0;
  ; SourceConnector和DestConnector 参考连接点的连接器。 & #160; 这是说他们是 & #160; 连接的源和目的地。 &# 160;  0;
  ; 一个连接不断监视其来源和目的地连接器  60; 为了保持SourceConnectorHotspot和DestConnectorHotspot   ; 目前为每个连接器的热点同步。 &# 160;  0; &# 160;
您可能会注意到,我在查看模型中使用的ImpObservableCollection类。这是我自己改进型{A65},增加了一些方便的功能和事件。虽然它不是由NetworkView需要,你可以使用任何来自收集{A66}。
引用NetworkView大会
任何自定义控件包含控制大会必须导入XAML文件。展开在解决方案资源管理器并打开MainWindow.xaml SimpleSample的项目。这个文件的开始近NetworkUI命名空间是进口:

    

    <Window x:Class="SampleCode.MainWindow"

        ...

        xmlns:NetworkUI="clr-namespace:NetworkUI;assembly=NetworkUI"

        ...

        >

        <-- ... -->

    </Window>


该项目还必须参考NetworkUI项目:主窗口查看模型
在XAML中实例化主窗口的视图模型:{C}
我的班级中的大多数生活在一个文件中,具有相同的名称。所以,你可以找到在MainWindowViewModel.cs MainWindowViewModel。MainWindowViewModel有一个NetworkViewModel实例。在这两个示例项目,为MainWindowViewModel的构造函数调用PopulateWithTestData填充,用一个小例子的数据集的网络。
当然还有其他{A67}他们可能会在其他情况下更合适。然而,对于本文中,我认为这是最简单的实例在XAML视图模型,并将其分配直接主窗口的DataContext。
视图模型与NetworkView使用数据绑定:
    

    <NetworkUI:NetworkView

        x:Name="networkControl"

        ...

        NodesSource="{Binding Network.Nodes}"

        ConnectionsSource="{Binding Network.Connections}"

        ...

        />


NetworkView和视图模型之间的关系是由这个图说明({A68}感谢):
{S13}
NetworkView被包裹在一个{A69}可以看到在主窗口的视觉树下面的图(到VS 2010的可视化):
{S14}
节点的样式
NodeItem风格的数据并将其绑定到NodeViewModel:
数据绑定X,Y,和IsSelected让我们来控制,通过每个节点的视图模型中的地位和选择状态,。由于数据绑定视图模型的变化自动更新UI。对于这项工作,当然,视图模型类必须实现{A70}。
节点的DataTemplate NodeViewModel数据模板定义每个节点的大小,外观和布局:
    

    <DataTemplate

        DataType="{x:Type NetworkModel:NodeViewModel}"

        >

        <Grid

            Width="120"

            Height="60"

            >



            <!-- This rectangle is the main visual for the node. -->

            <Rectangle

                Stroke="Black"

                Fill="White"

                RadiusX="4"

                RadiusY="4"

                />



            <!-- 

            This grid contains the node's name and connectors.

            The margin is negative so that the connectors overlap the body of the node and it's selection border.

            -->

            <Grid

                Margin="-8"

                >

                <Grid.ColumnDefinitions>

                    <ColumnDefinition Width="Auto" />

                    <ColumnDefinition Width="*" MinWidth="10" />

                    <ColumnDefinition Width="Auto" />

                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>

                    <RowDefinition Height="Auto" />

                    <RowDefinition Height="*" /> 

                    <RowDefinition Height="Auto" />                                

                </Grid.RowDefinitions>



                <!-- The name of the node. -->

                <TextBlock

                    Grid.Column="1"

                    Grid.Row="1"

                    Text="{Binding Name}"

                    HorizontalAlignment="Center"

                    VerticalAlignment="Center"

                    />



                <!-- ... Define the node's four connectors, one on each edge of the node. ... -->

                

            </Grid>

        </Grid>

    </DataTemplate>


视觉树节点组成一个3 × 3格重叠的矩形。此网格呈现节点的名称和它的四个连接器。一个连接是摆在每个矩形的边缘。网格的利润率是负的原因是纯粹的审美,它使边缘连接器,很好地与节点的边界重叠。
ConnectorItem确定每个节点的视觉树中的四个连接器。作为一个例子是第一个连接器:
    

    <NetworkUI:ConnectorItem 

        Grid.Row="0"

        Grid.Column="1"

        DataContext="{Binding Connectors[0]}"

        />


"{A71}数据绑定使用数组索引符号绑定到连接器集合中的第一个元素。其他三个连接器,我都没有显示,数据绑定到集合中的其他三个要素。
此图显示了节点的父NetworkView的视觉树内的视觉树:
{S15}
连接器类型
ConnectorItem风格的数据结合的连接器的热点视图模型定义了一个控制模板:
    

    <Style 

        TargetType="{x:Type NetworkUI:ConnectorItem}"

        >

        

        <!-- 

        Data-binding for the connector hotspot.

        ConnectorItem automatically computes its center points and assings this value

        to the 'Hotspot' property.  This data-binding then 'pushes' the value into the application

        view-model.

        -->

        <Setter 

            Property="Hotspot"

            Value="{Binding Hotspot, Mode=OneWayToSource}"

            />

        

        <!-- The visual template. -->

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate 

                    TargetType="{x:Type NetworkUI:ConnectorItem}"

                    >

                    <Rectangle

                        Stroke="Black"

                        Fill="White"                            

                        Cursor="Hand"

                        Width="12"

                        Height="12"

                        RadiusX="1"

                        RadiusY="1"

                        />

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>


由于此图显示了一个连接器的可视化树很简单:
{S16}
热点数据绑定推动热点连接器的值,计算ConnectorItem,视图模型。正如在我的文章讨论设置{A73}到{A74}推动从UI视图模型,而不是更常见的其他方式的价值。
此图说明了数据绑定的热点:
{S17}
连接的DataTemplate
ConnectionViewModel数据模板视觉代表作为一个指示箭头的连接:
    <DataTemplate

        DataType="{x:Type NetworkModel:ConnectionViewModel}"

        >



        <!-- The connection is represented by a simple arrow. -->

        <local:Arrow

            Stroke="Black"

            StrokeThickness="2"

            Fill="Black"

            Start="{Binding SourceConnectorHotspot}"

            End="{Binding DestConnectorHotspot}"

            IsHitTestVisible="False"

            />

    </DataTemplate>


箭类派生自{A75}是一个简单的应用程序特定的视觉。如果你想开始简单的东西使用标准WPF{A76}:
我不打算详细的箭头类的,虽然我应该提到到 一个伟大的{A77}帮我找出如何在WPF中的箭头。
此图表示如何查看模型提供了箭头的开始和结束点: {S18}
为了保持其SourceConnectorHotspot或DestConnectorHotspot属性最新ConnectionViewModel监控其源和目标连接器连接器的热点的变化。它通过处理ConnectorViewModel的HotspotUpdated事件。
请注意,"箭"有其{A78}属性设置为False。箭头无形的命中测试手段,连接与底层连接器重叠不干预用户能够拖出一个新的连接。
正如我们已经讨论了同步的主要方面之间的连接器和连接的终点我目前的一个总结下面的图表:
{S19}
"守则"我们在XAML中完成,现在是时候将代码。正如我下面良好的MVVM的原则,你会看到,没有太多的代码在MainWindow.xaml.cs。事件和命令处理程序将工作委托给视图模型。
通过ViewModel的属性访问视图的模型是:
    /// <summary>

    /// Convenient accessor for the view-model.

    /// </summary>

    public MainWindowViewModel ViewModel

    {

        get

        {

            return (MainWindowViewModel)this.DataContext;

        }

    }


删除节点
该DeleteSelectedNodes {A79}当用户按下删除键,当一个或多个节点被选中执行。
该命令转发到视图模型:
    private void DeleteSelectedNodes_Executed(object sender, ExecutedRoutedEventArgs e)

    {

        this.ViewModel.DeleteSelectedNodes();

    }


视图模型列举的所有节点,并删除选定的:
    public void DeleteSelectedNodes()

    {

        // Take a copy of the nodes list so we can delete nodes while iterating.

        var nodesCopy = this.Network.Nodes.ToArray();



        foreach (var node in nodesCopy)

        {

            if (node.IsSelected)

            {

                DeleteNode(node);

            }

        }

    }


它是这样的功能,我们需要知道每个节点的选择状态这是原因{A80}数据绑定在NodeItem风格。
删除节点并不仅仅意味着从网络中删除,所有连接到节点的连接也将被删除:
    public void DeleteNode(NodeViewModel node)

    {

        //

        // Remove all connections attached to the node.

        //

        this.Network.Connections.RemoveRange(node.AttachedConnections);



        //

        // Remove the node from the network.

        //

        this.Network.Nodes.Remove(node);

    }


创建新的节点
上下文菜单的创建节点调用CreateNode命令。
再次命令转发到视图模型:
    

    private void CreateNode_Executed(object sender, ExecutedRoutedEventArgs e)

    {

        Point newNodeLocation = Mouse.GetPosition(networkControl);

        this.ViewModel.CreateNode("New Node!", newNodeLocation);

    }


在这种情况下,命令处理程序做了少量的工作,以确定鼠标光标的位置它传递到视图模型:
    

    public NodeViewModel CreateNode(string name, Point nodeLocation)

    {

        var node = new NodeViewModel(name);

        node.X = nodeLocation.X;

        node.Y = nodeLocation.Y;



        //

        // Create the default set of four connectors.

        //

        node.Connectors.Add(new ConnectorViewModel());

        node.Connectors.Add(new ConnectorViewModel());

        node.Connectors.Add(new ConnectorViewModel());

        node.Connectors.Add(new ConnectorViewModel());



        //

        // Add the new node to the view-model (and therefore to the UI).

        //

        this.Network.Nodes.Add(node);



        return node;

    }


连接拖动事件
添加和删​​除节点,是相当简单的东西。允许用户创建节点之间的连接是一个稍微复杂一些。
这些都是必须处理的事件来实现连接的创建和拖动:
    <NetworkUI:NetworkView

        ...

        ConnectionDragStarted="networkControl_ConnectionDragStarted"

        ConnectionDragging="networkControl_ConnectionDragging"

        ConnectionDragCompleted="networkControl_ConnectionDragCompleted"

        />

NetworkView不明白视图模型的结构,所以它必须委托显著倍的应用,通过这些活动,让视图模型构建及转化为适当的。
要创建一个新的的连接用户拖动一个连接器。这就提出了ConnectionDragStarted事件这提示应用程序来创建和添加新的连接到其视图模型。在简单的示例源连接设置拖出来的连接器。
定期提出ConnectionDragging事件是在拖动操作这提示应用程序更新的目的端连接。 当用户已经下跌了目的端连接后,拖动操作结束无论是在另一个连接器,或在空的空间。在这两种情况下ConnectionDragCompleted事件引发提示应用程序敲定新的连接​​或取消。当连接完成时,它有它的目的端锚定的接头下降。
ConnectionDragStartednetworkControl_ConnectionDragStarted并转发到视图模型前,少量的工作:
    private void networkControl_ConnectionDragStarted(object sender, ConnectionDragStartedEventArgs e)

    {

        var draggedOutConnector = (ConnectorViewModel)e.ConnectorDraggedOut;

        var curDragPoint = Mouse.GetPosition(networkControl);



        //

        // Delegate the real work to the view model.

        //

        var connection = this.ViewModel.ConnectionDragStarted(draggedOutConnector, curDragPoint);



        //

        // Must return the view-model object that represents the connection via the event args.

        // This is so that NetworkView can keep track of the object while it is being dragged.

        //

        e.Connection = connection;

    }


拖出的连接器和当前鼠标位置传递给ConnectionDragStarted它通过消除任何现有的连接开始拖出来的连接器已连接到:
下一步创建一个ConnectionViewModel实例源连接锚拖出来的连接器:
    public ConnectionViewModel ConnectionDragStarted(ConnectorViewModel draggedOutConnector, Point curDragPoint)

    {

        // ... remove existing connection ...



        //

        // Create a new connection to add to the view-model.

        //

        var connection = new ConnectionViewModel();





        //

        // Link the source connector to the connector that was dragged out.

        //

        connection.SourceConnector = draggedOutConnector;



        // ... rest of the method ...

    }


ConnectionViewModel SourceConnector二传手内(同样DestConnector)一些重要的工作是做是必要的,看看和快速题外话:
    

    /// <summary>

    /// The source connector the connection is attached to.

    /// </summary>

    public ConnectorViewModel SourceConnector

    {

        get

        {

            return sourceConnector;

        }

        set

        {

            if (sourceConnector == value)

            {

                return;

            }



            if (sourceConnector != null)

            {

                Trace.Assert(sourceConnector.AttachedConnection == this);



                sourceConnector.AttachedConnection = null;

                sourceConnector.HotspotUpdated -= new EventHandler<eventargs>(sourceConnector_HotspotUpdated);

            }



            sourceConnector = value;



            if (sourceConnector != null)

            {

                Trace.Assert(sourceConnector.AttachedConnection == null);



                sourceConnector.AttachedConnection = this;

                sourceConnector.HotspotUpdated += new EventHandler<eventargs>(sourceConnector_HotspotUpdated);

                this.SourceConnectorHotspot = sourceConnector.Hotspot;

            }



            OnPropertyChanged("SourceConnector");

        }

    }


请注意,连接热点值是从源头上连接器的复制热点连接的SourceConnectorHotspot属性的属性。回想一下从连接的数据模板,这个属性数据绑定到箭UI元素的开始。HotspotUpdated事件挂钩,以便它可以连接保持与连接器的热点同步SourceConnectorHotspot。
现在返回到ConnectionDragStarted源连接设置,但目前还没有任何目的地的连接器,并不会出现,直到用户已完成拖动连接。由于DestConnector尚未DestConnectorHotspot也没有设置,必须设置当前鼠标的位置:
最后,新的连接被添加到视图模型和连接对象返回到主窗口的代码:
   

    public ConnectionViewModel ConnectionDragStarted(ConnectorViewModel draggedOutConnector, Point curDragPoint)

    {

        // ... remove existing connection ...



        // ... create new connection and set source connector ...





        // .. set DestConnectorHotspot from currnet mouse position ...



        //

        // Add the new connection to the view-model.

        //

        this.Network.Connections.Add(connection);



        return connection;

    }


通过ConnectionDragStarted事件参数返回连接对象和NetworkView不断跟踪这个对象,而拖动操作正在进行中。
此时,一个新的连接被创建并添加到视图模型。连接源端固定在拖出来的连接器和目的端设置为当前鼠标位置。
ConnectionDragging
networkControl_ConnectionDragging被称为定期在拖动操作。转发ConnectionDragging这只是保持目标结束当前鼠标位置固定连接:
   

    public void ConnectionDragging(ConnectionViewModel connection, Point curDragPoint)

    {

        //

        // Update the destination connector hotspot while the user is dragging the connection.

        //

        connection.DestConnectorHotspot = curDragPoint;

    }


ConnectionDragCompletednetworkControl_ConnectionDragCompleted转发ConnectionDragCompleted如果拖动操作首先检查被取消。如果是这样,新的连接被删除从视图模型和没有更多的需要做的:
连接没有被取消,该方法将继续并删除任何现有的连接,已连接到目标连接器:
最后,连接完成其目的端锚固被丢弃的连接器,从现在DestConnectorHotspot自动同步与目标连接器热点:
    public void ConnectionDragCompleted(ConnectionViewModel newConnection, ConnectorViewModel connectorDraggedOut, ConnectorViewModel connectorDraggedOver)

    {

        // ... if connection creation is canceled, remove the connection ...



        // ... remove existing connection ...



        //

        // Finalize the connection by attaching it to the connector

        // that the user dropped the connection on.

        //

        newConnection.DestConnector = connectorDraggedOver;

    }


我们现在简单的示例演练结束。我们已经覆盖NetworkView用法的最重要的方面,但有仍有不少已掩盖或不检查所有的位。这个演练应该是为自己的研究的出发点代码。有没有更好的方式来理解的代码比通过在关键位置设置断点,实际上是通过它加强。{A81}
现在,您应该设置为启动项目在NetworkViewSampleCode.sln AdvancedSample。如果您尚未这样做,建造和运行应用程序并需要几分钟的时间去探索。
从本文开头的截图显示了先进的样品在行动:
{S0}
它是立即明显的,更有趣,更复杂的视觉样式和模板。先进的样品,我前面的文章中开发的控制和技术的使用。事实上,这些条款沿着这篇文章的方式,而不是其他方式。先进的样品的主要变化可以概括如下: 视图的模型现在是一种数据流图,每个节点都有输入和输出接口。 现在的某些限制应用创造新的连接。 例如输入只能连接到输出,反之亦然。 试图连接在输出结果输入到一个输入或输出 接触不良反馈图标显示。 节点现在有一个固定数量的连接器,而不是任意的。 连接现在可以显式删除。鼠标悬停在连接器,并会出现一个按钮,可以点击删除连接。我还可以使用鼠标悬停按钮相同的排序 作为替代方法删除节点。 缩放和平移功能和新的概述窗口使用{A82}。 两者之间的主要窗口和概览窗口共享一个单一的视图模型。 有些样式和数据模板之间共享的主要窗口和使用的概述窗口 资源字典。
视图模型
此图显示了从AdvancedNetworkModel项目的视图模型类。他们都非常相似,简单的示例,我将只提及的差异。
{S21}  60;  0; NodeViewModel  0;
而不是只是一个单一的连接器收集,NodeViewModel现在已经InputConnectors和OutputConnectors & #160; 它们是单独的集合,包含输入和输出接口。 & #160;  60;   ; &# 160;
 60; 的ZIndex指定节点的位置在Z -顺序。这可 用来将给予最高的Z -索引节点一个节点到所有其他节点的前面。   ; 的ZIndex不过是不实际使用先进的样品中,我有只包括  0; 作为一个例子,因为我相信这将是有益的,有些人正在阅读。 &# 160;   ;
大小计算  60; 在应对 {A83} 节点和视图事件的价值推到 视图模型。在先进的样品使用节点的大小是使新创建的节点  ; 可以为本。 & #160; &# 160; ConnectorViewModel &# 160;
连接现在有一个是在UI中显示的名称。 &# 160; & #160;
&# 160; 先进的样品允许多个连接到一个单一的连接器和锚定  0; AttachedConnections是收集这些。  0; &# 160;  0; ConnectionViewModel
&# 160; 点是点的集合 &# 160; (我喜欢叫什么连接点) & #160; 贝塞尔曲线表示一个连接控制点  60; 在先进的样品。 &# 160;  0;
  ; 计算连接点   ; 每当SourceConnectorHotspot或DestConnectorHotspot发生了变化。   ;
共享资源词典
共享样式和数据模板都包含在{A84}合并到主窗口的资源:
    <Window.Resources>



        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>

                <!-- 

                Merge in the resource dictionary that is shared between the main window and the overview window.

                -->

                <ResourceDictionary 

                    Source="SharedVisualTemplates.xaml"

                    />

            </ResourceDictionary.MergedDictionaries>



            <!-- ... other resources ... -->



        </ResourceDictionary>



    <Window.Resources>            


让我们来看看一些SharedVisualTemplates.xaml的定义。
NodeItem风格
共享的NodeItem风格几乎是在简单的示例相同,所以我不会在这里重现。唯一增加的ZIndex,新的数据绑定,如上所述,作为一个例子,但实际上并不在此示例中使用。
连接器数据模板
先进的样品引入了不同类型的连接器等每种类型有不同的数据模板。输入和输出接口是面向节点的左,右两侧分别,因此是完全不同的安排。
数据输入接口模板上显示正确的连接视觉它的名字:
    <DataTemplate

        x:Key="inputConnectorTemplate"

        >

        <Grid

            Margin="0,2,0,0"

            >

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="*" />

            </Grid.ColumnDefinitions>



            <!-- The 'ConnectorItem' or anchor point for the connector. -->

            <NetworkUI:ConnectorItem 

                Grid.Column="0"

                Width="15"

                Height="15"

                Cursor="Hand"                    

                />



            <!-- The name of the connector. -->            

            <TextBlock

                Grid.Column="1"

                Margin="5,0,0,0"

                Text="{Binding Name}"

                VerticalAlignment="Center"

                />

        </Grid>

    </DataTemplate>


输出连接器的数据模板看起来非常相似,但与周围的其他方式的名称显示在左侧的连接器视觉。在输出连接器的数据模板,你会注意到展望在连接时连接的连接器,显示一个黑点。这是因为我认为它使连接更好看审美原因。这黑点,可以很容易地箭头视觉的一部分,但这并不能很好地工作。在先进的样品的连接需要处理鼠标输入(以显示删除连接鼠标悬停按钮)这意味着,如果我们提供部分连接(例如,如果黑点连接的一部分)在底层连接器的ITinterfers与用户的能力,拖出一个新的连接。由于这个原因,黑点 的部分,而不是连接的连接器。
现在,我们将继续寻找在MainWindow.xaml。交互式图形组件的样式和数据模板在主窗口中。类似的样式和数据模板中可以找到OverviewWindow.xaml实现非交互式的图形组件,因为你不能在图形交互概览窗口。
NetworkView定义
综观NetworkView在先进的样品的XAML中,您可以看到它不只是包裹在ScrollViewer的,简单的示例,它现在是包裹在一个ScrollViewer,一个ZoomAndPanControl{A85}。
下面是一个概述:
    <ScrollViewer

        ...

        >

        

        <ZoomAndPan:ZoomAndPanControl

            ...

            >

            <AdornerDecorator>                

                <Grid

                    ...

                    >



                    <NetworkUI:NetworkView

                        ...

                        />

                    

                    <Canvas

                        x:Name="dragZoomCanvas"

                        ...

                        >

                        <Border 

                            ...

                            />

                    </Canvas>

                    

                </Grid>

            </AdornerDecorator>

        </ZoomAndPan:ZoomAndPanControl>

    </ScrollViewer>


此图显示主窗口的视觉树:
{S22}
ZoomAndPanControl是从{A86}我不会去这里就可以进入细节。我只想说,它给人的能力进行缩放和平移的NetworkView。
AdornerDecorator创建一个新的{A87}下面的视觉树ZoomAndPanControl。{A88}用于显示删除连接和删除节点的鼠标悬停按钮也反馈图标。没有AdornerDecorator然后adorners会显示在默认的装饰器层以上视觉树ZoomAndPanControl。如果明确定义的,而不是使用这个装饰器层,这将意味着,adorners不会受到规模和偏移应用的转换ZoomAndPanControl(通过其{A89})因此不会出现在其余ZoomAndPanControl的内容相同的缩放级别。
dragZoomCanvas(后NetworkView)是用来呈现缩放矩形,允许用户拖出一个矩形缩放到。这种技术是在描述{A90}。
NodeViewModel数据模板
NodeViewModel数据模板类似简单的示例版本,但使用{A91}和介绍一个节点的任意数量的连接器使它更加复杂。
下面是一个概述:
    <!-- Define a data-template for the 'NodeViewModel' class. -->    

    <DataTemplate

        DataType="{x:Type NetworkModel:NodeViewModel}"

        >



        <!-- 

        An adorned control is used, to represent the node. 

        When the user hovers the mouse cursor over the node, the 

        'delete node' adorner pops up and allows them to delete the node.

        -->

        <ac:AdornedControl

            ...

            IsMouseOverShowEnabled="{Binding ElementName=networkControl, Path=IsNotDragging}"

            >



            <!-- ... node visuals ... -->



            <ac:AdornedControl.AdornerContent>



                <!-- 

                This is the content for the 'delete node' adorner that pops up when the user hovers the mouse over the node.

                It displays a button that the user can click to delete the node.

                -->

                

                <!-- ... delete node mouse-hover button -->

                

            </ac:AdornedControl.AdornerContent>

        </ac:AdornedControl>                    

    </DataTemplate>            


删除节点鼠标悬停按钮使用一个装饰器的理由请注意,IsMouseOverShowEnabled是数据绑定到NetworkView的IsNotDragging财产。这确保了鼠标悬停按钮是永远不会被拖动时显示一个节点或连接。
除了使用在装饰和连接的演示,一个节点的视觉效果是相似的简单的示例:
    

    <Grid

        MinWidth="120"



        Margin="10,6,10,6"

        SizeChanged="Node_SizeChanged"

        >



        <!-- This rectangle is the main visual for the node. -->



        <Rectangle

            Stroke="{StaticResource nodeBorderBrush}"

            StrokeThickness="1.3"

            RadiusX="4"

            RadiusY="4"

            Fill="{StaticResource nodeFillBrush}"

            />



        <!-- 

        This grid contains the node's connectors.

        The margin is negative so that the connectors overlap the body of the node and it's selection border.

        -->

        <Grid

            Margin="-6,4,-6,4"

            >

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="*" MinWidth="10" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <!-- spacer -->

                <RowDefinition Height="2" />                    

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>



            <!-- The name of the node. -->                

            <TextBlock

                Grid.Column="0"

                Grid.ColumnSpan="3"

                Grid.Row="0"

                Text="{Binding Name}"

                HorizontalAlignment="Center"

                VerticalAlignment="Center"

                />

                

                <!-- ... Visuals for connectors defined here ... -->                



            </Grid>

    </Grid>


SizeChanged事件处理节点意味着可以存储在视图模型的节点的实际大小。在访问的大小是指创建节点时,他们可以在鼠标位置为中心。 为节点现在有一个任意数量的输入和输出接口,连接器这两种类型提出使用:
    <!-- Displays the node's input connectors. -->                

    <ItemsControl

        Grid.Column="0"

        Grid.Row="2"

        ItemsSource="{Binding InputConnectors}"

        ItemTemplate="{StaticResource inputConnectorTemplate}"

        />



    <!-- Displays the node's output connectors. -->

    <ItemsControl

        Grid.Column="2"

        Grid.Row="2"

        ItemsSource="{Binding OutputConnectors}"

        ItemTemplate="{StaticResource outputConnectorTemplate}"

        />


数据绑定到每个ItemsControl中的ItemsSourceInputConnectors OutputConnectors节点的模型的属性。输入连接器被放置在左边的节点和网格列0输出接口在右侧第2栏。
此图显示在父节点和其连接的视觉树NetworkView的视觉树。
{S23}
删除节点鼠标悬停按钮
删除节点按钮节点的装饰器的内容。当用户将鼠标悬停鼠标多节点节点和右上面的按钮出现。
XAML定义基本上是简单的。这是一个定制的的按钮放置在画布和是一条连接节点的按钮:
早在XAML定义按钮控制模板。按一下按钮调用DeleteNode的命令符号:
    

    CommandParameter="{Binding}"


数据绑定按钮{A71}CommandParameter和当前节点的模型,以便指定要删除的节点。
ConnectorItem风格
ConnectorItem风格大同小异,因为它是简单的示例使我不会在这里重现。唯一不同的是,现在视觉与代表connnections{A95}而不是一个{A96}。OverviewWindow.xaml定义也有相应的样式。

 60;
  ; & #160;  0;
 60; &# 160;

"
在 和

























感谢





回答