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"><</span><span class="code-SummaryComment">summary</span><span class="code-SummaryComment">></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"><</span><span class="code-SummaryComment">/</span><span class="code-SummaryComment">summary</span><span class="code-SummaryComment">></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> </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 > <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 < 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> </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> &&
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"><</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 + -
显示快捷键?