spidercontrol.aspx.htm

来自「WPF星型网状拓扑图绘制 WPF星型网状拓扑图绘制」· HTM 代码 · 共 928 行 · 第 1/5 页

HTM
928
字号
<span class="code-keyword">namespace</span> SpiderTreeControl.Diagram
{
    <span class="code-SummaryComment">///</span><span class="code-comment"> <span class="code-SummaryComment">&lt;</span><span class="code-SummaryComment">summary</span><span class="code-SummaryComment">&gt;</span></span>
    <span class="code-SummaryComment">///</span><span class="code-comment"> Interaction logic for DragViewer.xaml</span>
    <span class="code-SummaryComment">///</span><span class="code-comment"> <span class="code-SummaryComment">&lt;</span><span class="code-SummaryComment">/</span><span class="code-SummaryComment">summary</span><span class="code-SummaryComment">&gt;</span></span>
    <span class="code-keyword">public</span> <span class="code-keyword">partial</span> <span class="code-keyword">class</span> DragViewer : UserControl
    {

        <span class="code-keyword">public</span> DragViewer()
        {
            InitializeComponent();
            <span class="code-keyword">this</span>.Loaded+=delegate
            {
                LoadDiagramNodes();
            };
        }

        <span class="code-keyword">public</span> <span class="code-keyword">void</span> LoadDiagramNodes()
        {

            DiagramNode root = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">Root"</span>, <span class="code-keyword">null</span>, <span class="code-string">"</span><span class="code-string">../Images/DiagramRootNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>,<span class="code-string">"</span><span class="code-string">this is the root node"</span>);

            DiagramNode a = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">A"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node A"</span>);
            DiagramNode b = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">B"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node B"</span>);
            DiagramNode c = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">C"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node C"</span>);
            DiagramNode d = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">D"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node D"</span>);
            DiagramNode e = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">E"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node E"</span>);
            DiagramNode f = <span class="code-keyword">new</span> DiagramNode(<span class="code-string">"</span><span class="code-string">F"</span>, root, <span class="code-string">"</span><span class="code-string">../Images/DiagramNode.png"</span>, <span class="code-string">"</span><span class="code-string">Dummy1View"</span>, <span class="code-string">"</span><span class="code-string">this is node F"</span>);

            diagramViewer.RootNode = root;
            diagramViewer.FrictionScrollViewer = <span class="code-keyword">this</span>.sv;
        }
    }
}
</pre>
<p>Were individual <code>DiagramNode</code> objects are created and the relationship 
  between them are establish by passing the relavant <code>DiagramNode</code> 
  in as a constructor parameter to another <code>DiagramNode</code>. Obviously 
  in the case of the root <code>DiagramNode</code>, this value is null. The last 
  thing that is done is that the embedded <code>DiagramViewer</code> has its <code>RootNode</code> 
  property set to the root <code>DiagramNode</code>. There is also a requirement 
  to set the embedded <code>DiagramViewer</code>s <code>FrictionScrollViewer</code> 
  property, such that the <code>DiagramViewer</code>s layout algorithm can react 
  to new <code>ScrollViewer</code> positions should the user move the <code>ScrollViewer</code> 
  or drag the diagram.</p>
<h3>DiagramViewer class</h3>
<p>Is where all the layout of contained <code>DiagramNode</code>s occurs. This 
  is a simple user control that contains a single <code>TreeCanvas</code> that 
  holds the actual collection of <code>DiagramNode</code>s, and draws the lines 
  between them, which is discussed below. The <code>DiagramViewer</code> also 
  listens to events from the <code>DiagramNode</code>s such as Selected/Collapsed/Expanded, 
  where it will perform the layout based on the node selection.</p>
<p>The DiagramViewer uses a radial algorithm to lay out the collection of child 
  nodes around a parent node. This is done using standard trigonometry maths. 
  The basic idea is that each <code>DiagramNode</code> is given a bounding circle 
  to ensure that all nodes have a uniform size, and then an angle between nodes 
  is calculated. </p>
<p><img src="SpiderControl.aspx_files/bounds.png" width="520" height="470"></p>
<p>&nbsp;</p>
<p>This process is done on the <code>NodeExpanded()</code>, as shown below:</p>
<div class="SmallText" id="premain3" style="width: 100%; cursor: pointer;"><img preid="3" src="SpiderControl.aspx_files/minus.gif" id="preimg3" width="9" height="9"><span preid="3" style="margin-bottom: 0pt;" id="precollapse3"> Collapse</span></div><pre style="margin-top: 0pt;" id="pre3" lang="cs"><span class="code-keyword">private</span> <span class="code-keyword">void</span> NodeExpanded(DiagramNode sender, RoutedEventArgs eventArguments)
{
    rootNode.Location = <span class="code-keyword">new</span> Point(
        (<span class="code-keyword">double</span>)GetValue(Canvas.ActualWidthProperty) / <span class="code-digit">2</span>.<span class="code-digit">0</span>,
        (<span class="code-keyword">double</span>)GetValue(Canvas.ActualHeightProperty) / <span class="code-digit">2</span>.<span class="code-digit">0</span>);


    MakeChildrenVisible(sender);

    <span class="code-keyword">if</span> (sender.DiagramParent != <span class="code-keyword">null</span>)
    {
        sender.DiagramParent.Visibility = Visibility.Visible;
        <span class="code-keyword">foreach</span> (DiagramNode sibling <span class="code-keyword">in</span> sender.DiagramParent.DiagramChildren)
        {
            <span class="code-keyword">if</span> (sibling != sender)
                sibling.Visibility = Visibility.Collapsed;
        }
        <span class="code-keyword">if</span> (sender.DiagramParent.DiagramParent != <span class="code-keyword">null</span>)
            sender.DiagramParent.DiagramParent.Visibility = Visibility.Collapsed;
    }

    <span class="code-keyword">if</span> (sender.DiagramChildren.Count &gt; <span class="code-digit">0</span>)
    {
        <span class="code-keyword">double</span> startAngle = CalculateStartAngle(sender);
        <span class="code-keyword">double</span> angleBetweenChildren = (sender == rootNode ? Math.PI * <span class="code-digit">2</span>.<span class="code-digit">0</span> : Math.PI) / 
					((<span class="code-keyword">double</span>)sender.DiagramChildren.Count - <span class="code-digit">0</span>);

        <span class="code-keyword">double</span> legDistance = CalculateLegDistance(sender, angleBetweenChildren);

        <span class="code-keyword">for</span> (<span class="code-keyword">int</span> i = <span class="code-digit">0</span>; i &lt; sender.DiagramChildren.Count; ++i)
        {
            DiagramNode child = sender.DiagramChildren[i];
            child.Selected += <span class="code-keyword">new</span> NodeStateChangedHandler(NodeSelected);
            child.Expanded += <span class="code-keyword">new</span> NodeStateChangedHandler(NodeExpanded);
            child.Collapsed += <span class="code-keyword">new</span> NodeStateChangedHandler(NodeCollapsed);

            Point parentLocation = sender.Location;

            child.Location = <span class="code-keyword">new</span> Point(
                parentLocation.X + Math.Cos(startAngle + angleBetweenChildren * (<span class="code-keyword">double</span>)i) * legDistance,
                parentLocation.Y + Math.Sin(startAngle + angleBetweenChildren * (<span class="code-keyword">double</span>)i) * legDistance);

            <span class="code-keyword">foreach</span> (DiagramNode childsChild <span class="code-keyword">in</span> child.DiagramChildren)
            {
                childsChild.Visibility = Visibility.Collapsed;
            }
        }
    }

    BaseCanvas.InvalidateArrange();
    BaseCanvas.UpdateLayout();
    BaseCanvas.InvalidateVisual();
}
</pre>
<p>The above process also relies on another process, which is the process which 
  works out the leg distances (the length of the line to draw from one node to 
  its child) between nodes. </p>
<p>This is as shown below:</p>
<pre lang="cs"><span class="code-keyword">private</span> <span class="code-keyword">static</span> <span class="code-keyword">double</span> CalculateLegDistance(DiagramNode sender, <span class="code-keyword">double</span> angleBetweenChildren)
{
    <span class="code-keyword">double</span> legDistance = <span class="code-digit">1</span>.<span class="code-digit">0</span>;
    <span class="code-keyword">double</span> childToChildMinDistance = <span class="code-digit">1</span>.<span class="code-digit">0</span>;
    <span class="code-keyword">foreach</span> (DiagramNode child <span class="code-keyword">in</span> sender.DiagramChildren)
    {
        legDistance = Math.Max(legDistance, 
			sender.BoundingCircle + child.BoundingCircle);
        <span class="code-keyword">foreach</span> (DiagramNode otherChild <span class="code-keyword">in</span> sender.DiagramChildren)
        {
            <span class="code-keyword">if</span> (otherChild != child)
            {
                childToChildMinDistance = 
				Math.Max(childToChildMinDistance, 
				child.BoundingCircle + otherChild.BoundingCircle);
            }
        }
    }

    legDistance = Math.Max(
        legDistance,
        (childToChildMinDistance / <span class="code-digit">2</span>.<span class="code-digit">0</span>) / Math.Sin(angleBetweenChildren / <span class="code-digit">2</span>.<span class="code-digit">0</span>));
    <span class="code-keyword">return</span> legDistance;
}
</pre>
<p>&nbsp;</p>
<h3>TreeCanvas class</h3>
<p>This is a specialized <code>Canvas</code> control that simply draws the lines 
  between all the <code>DiagramNode</code>s, currently shown within the <code>DiagramViewer</code>. 
  This is done by overriding the <code>OnRender()</code> method of the Canvas 
  control. This is shown below:</p>
<pre lang="cs"><span class="code-keyword">protected</span> <span class="code-keyword">override</span> <span class="code-keyword">void</span> OnRender(System.Windows.Media.DrawingContext dc)
{
    <span class="code-keyword">base</span>.OnRender(dc);
    <span class="code-keyword">foreach</span> (UIElement uiElement <span class="code-keyword">in</span> Children)
    {
        <span class="code-keyword">if</span> (uiElement <span class="code-keyword">is</span> DiagramNode)
        {
            DiagramNode node = (DiagramNode)uiElement;

            <span class="code-keyword">if</span> (node.Visibility == Visibility.Visible)
            {
                <span class="code-keyword">if</span> (node.DiagramParent != <span class="code-keyword">null</span> &amp;&amp; 
                    node.DiagramParent.Visibility == Visibility.Visible)
                {
                    dc.DrawLine(<span class="code-keyword">new</span> Pen(Brushes.Black, <span class="code-digit">2</span>.<span class="code-digit">0</span>), 
                        node.Location, node.DiagramParent.Location);
                }
            }
        }
    }
}
</pre>
<h3>DiagramNode class</h3>
<p>This is a pretty standard WPF UserControl that represents a single <code>DiagramNode</code> 
  within the <code>DiagramViewer</code>. Its pretty standard stuff really a few 
  buttons for the expanded/collapsed and selected states. Lets see the XAML for 
  it shall we.</p>
<div class="SmallText" id="premain6" style="width: 100%; cursor: pointer;"><img preid="6" src="SpiderControl.aspx_files/minus.gif" id="preimg6" width="9" height="9"><span preid="6" style="margin-bottom: 0pt;" id="precollapse6"> Collapse</span></div><pre style="margin-top: 0pt;" id="pre6" lang="xml"><span class="code-keyword">&lt;</span><span class="code-leadattribute">UserControl</span> <span class="code-attribute">x:Class</span><span class="code-keyword">="</span><span class="code-keyword">SpiderTreeControl.Diagram."</span>
    <span class="code-attribute">xmlns</span><span class="code-keyword">="</span><span class="code-keyword">http://schemas.microsoft.com/winfx/2006/xaml/presentation"</span>
    <span class="code-attribute">xmlns:x</span><span class="code-keyword">="</span><span class="code-keyword">http://schemas.microsoft.com/winfx/2006/xaml"</span>
    <span class="code-attribute">xmlns:diagram</span><span class="code-keyword">="</span><span class="code-keyword">clr-namespace:SpiderTreeControl.Diagram"</span>  
             
    <span class="code-attribute">Height</span><span class="code-keyword">="</span><span class="code-keyword">80"</span> <span class="code-attribute">Width</span><span class="code-keyword">="</span><span class="code-keyword">90"</span> 
    <span class="code-attribute">Background</span><span class="code-keyword">="</span><span class="code-keyword">Transparent"</span>
    <span class="code-attribute">BorderBrush</span><span class="code-keyword">="</span><span class="code-keyword">Transparent"</span><

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?