MainWindow.xaml
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="900" Visibility="Visible">
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml;
namespace Topographies
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public List
public double totalWidth;
public double totalHight;
public double buttonWidth;
public double buttonHeight;
public double plusButtonWidth;
public double plusButtonHeight;
public double levelHight;
public double minHorizontalSpace;
public double minVerticalSpace;
public double fontSize;
public SolidColorBrush LinesStroke;
public double LinesStrokeThickness;
public DispatcherTimer _doubleClickTimer;
public double drawingScale = 1;
public MainWindow()
{
InitializeComponent();
LoadXMLFile();
}
private void Rescale()
{
MyCanvas.Children.Clear();
buttonWidth = 150 * drawingScale;
buttonHeight = 80 * drawingScale;
minHorizontalSpace = 10 * drawingScale;
minVerticalSpace = 10 * drawingScale;
plusButtonWidth = 12 * drawingScale;
plusButtonHeight = 12 * drawingScale;
fontSize = 10 * drawingScale;
LinesStrokeThickness = 1 * drawingScale;
levelHight = buttonHeight + minVerticalSpace * 2;
Refresh();
}
private void Refresh()
{
MyCanvas.Width = buttonWidth;
MyCanvas.Height = buttonHeight;
totalWidth = MyCanvas.Width;
totalHight = MyCanvas.Height;
persons[0].MinChildWidth = buttonWidth + minHorizontalSpace;
persons[0].StartX = 0;
persons[0].X = persons[0].MinChildWidth / 2;
SetLevel(persons[0], 1);
CalculateWidth(persons[0]);
CalculateX(persons[0]);
DrawNode(persons[0]);
}
#region Load XML
private void LoadXMLFile()
{
XmlDocument doc = new XmlDocument();
string currentdir = Directory.GetCurrentDirectory();//获取当前路径
string targetXmlUrl =
@"../../OrgChart_Data.xml";
doc.Load(targetXmlUrl); //加载Xml文件
XmlElement rootElem = doc.DocumentElement; //获取根节点
if (rootElem != null)
{
Person firstNode = Person.GetPerson("OrgChartRootNode", "My Organization", "", "", "", "", "");
firstNode.MinChildWidth = totalWidth;
persons.Add(firstNode);
XmlNodeList personNodes = rootElem.GetElementsByTagName("Person"); //获取person子节点集合
foreach (XmlNode node in personNodes)
{
XmlNodeList attributes = ((XmlElement) node).ChildNodes; //获取name属性值
Person person = new Person();
foreach (XmlNode attribute in attributes)
{
XmlElement attElement = (XmlElement) attribute;
if (attElement.Name == "ID")
{
string Id = attElement.InnerText;
person.ID = Id;
}
if (attElement.Name == "Name")
{
string name = attElement.InnerText;
person.Name = name;
}
if (attElement.Name == "Title")
{
string title = attElement.InnerText;
person.Title = title;
}
if (attElement.Name == "Department")
{
string department = attElement.InnerText;
person.Department = department;
}
if (attElement.Name == "Extension")
{
string etension = attElement.InnerText;
person.Extension = etension;
}
if (attElement.Name == "Email")
{
string email = attElement.InnerText;
person.Email = email;
}
if (attElement.Name == "ManagerID")
{
string managerID = attElement.InnerText;
person.ManagerID = managerID;
}
}
if (string.IsNullOrEmpty(person.ManagerID))
{
person.ManagerID = "OrgChartRootNode";
}
persons.Add(person);
}
Rescale();
}
}
#endregion
#region Calculate Values
private void SetLevel(Person parent, int level)
{
// Set the node level
parent.Level = level;
// Calculate the total height based on the number of levels
if (totalHight < levelHight * (level + 2))
{
totalHight = levelHight * (level + 2);
MyCanvas.Height = totalHight;
}
// Select the closed items under this parent
var resultN = from n in persons
where n.ManagerID == parent.ID && n.Opened == false
select n;
// Get the closed nodes number
parent.HiddenSubNodes = resultN.Count();
// Select the opend nodes under this parent
var result = from n in persons
where n.ManagerID == parent.ID && n.Opened == true
select n;
Person[] nodes = result.ToArray();
// Get the Opend nodes number
parent.SubNodes = nodes.Length;
// Call the child nodes
for (int i = 0; i < nodes.Length; i++)
{
nodes[i].NodeOrder = i + 1;
nodes[i].MinChildWidth = buttonWidth + minHorizontalSpace;
SetLevel(nodes[i], parent.Level + 1);
}
}
private void CalculateWidth(Person parent)
{
if (parent.SubNodes > 0)
{
var result = from n in persons
where n.ManagerID == parent.ID && n.Opened == true
orderby n.NodeOrder
select n;
Person[] nodes = result.ToArray();
double width = 0;
for (int i = 0; i < nodes.Length; i++)
{
// calculate the width of the child before adding it to the parent
CalculateWidth(nodes[i]);
// calculate the new width
width = width + nodes[i].MinChildWidth;
}
if (width > parent.MinChildWidth)
{
parent.MinChildWidth = width;
if (MyCanvas.Width < width)
{
MyCanvas.Width = width;
//totalWidth = width;
}
}
}
}
private void CalculateX(Person parent)
{
if (parent.SubNodes > 0)
{
var result = from n in persons
where n.ManagerID == parent.ID && n.Opened == true
orderby n.NodeOrder
select n;
Person[] nodes = result.ToArray();
// Calculate the startX for each node
double start = parent.StartX;
for (int i = 0; i < nodes.Length; i++)
{
nodes[i].StartX = start;
nodes[i].X = nodes[i].StartX + nodes[i].MinChildWidth / 2;
CalculateX(nodes[i]);
start = start + nodes[i].MinChildWidth;
}
// realign the parent node to the middle of the child nodes
if (nodes.Length > 1)
{
parent.X = (nodes[0].X + nodes[nodes.Length - 1].X) / 2;
}
else // root element
{
parent.X = nodes[0].X;
}
}
}
#endregion
#region Draw
private void DrawNode(Person parent)
{
// Check if the current node is the parent node or not
if (parent.ManagerID == "")
{
AddBox(MyCanvas, parent.X, parent.Level * levelHight, null, parent.ID, parent.Name, parent.Title, parent.Department, parent.Extension, false, true, parent.HiddenSubNodes > 0);
}
// Get the child nodes
var results = from n in persons
where n.ManagerID == parent.ID && n.Opened == true
select n;
foreach (Person p in results)
{
AddBox(MyCanvas, p.X, p.Level * levelHight, parent.X, p.ID, p.Name, p.Title, p.Department, p.Extension, true, p.SubNodes > 0, p.HiddenSubNodes > 0);
DrawNode(p);
}
}
public void DrawLine(Canvas canvas, double x1, double y1, double x2, double y2)
{
Line line = new Line();
line.X1 = x1;
line.Y1 = y1;
line.X2 = x2;
line.Y2 = y2;
line.Stroke = Util.GetColorFromHex("#FF6495ed");
line.StrokeThickness = LinesStrokeThickness;
canvas.Children.Add(line);
}
public void AddBox(Canvas canvas, double x, double y, double? parentX, string ID, string name, string title, string department, string extension, bool root, bool subNodes, bool hiddenSubNodes)
{
NodeBox nb = new NodeBox(drawingScale);
//nb.Name = ID;
nb.EmployeeName = name;
nb.Title = title;
nb.Department = department;
nb.Extension = extension;
nb.Width = buttonWidth;
nb.Height = buttonHeight;
nb.SetValue(Canvas.LeftProperty, x - nb.Width / 2);
nb.Target = "plus" + ID;
nb.SetValue(Canvas.TopProperty, y);
nb.ButtonBase.Click += btn_Click;
// handle the double click actions
_doubleClickTimer = new DispatcherTimer();
_doubleClickTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
_doubleClickTimer.Tick += new EventHandler(delegate { _doubleClickTimer.Stop(); });
nb.MouseLeftButtonDown += new MouseButtonEventHandler(delegate
{
if (_doubleClickTimer.IsEnabled)
{
_doubleClickTimer.Stop();
btnClicked(ID);
}
else
{
_doubleClickTimer.Start();
}
});
canvas.Children.Add(nb);
// The line above the box
if (root)
DrawLine(canvas, x-3, y - minVerticalSpace, x-3, y);
// Draw the horizontal line to the parent
if (parentX != null)
DrawLine(canvas, x-3, y - minVerticalSpace, Convert.ToDouble(parentX-3), y - minVerticalSpace);
// Draw the line under the box
nb.ButtonBase.IsChecked = true;
// display the small plus
if (hiddenSubNodes)
{
Button btn = new Button();
btn.Name = "plus" + ID;
btn.FontSize = fontSize / 2;
btn.Click += new RoutedEventHandler(btn_Click);
btn.Height = plusButtonHeight;
btn.Width = plusButtonWidth;
btn.Content = "+";
btn.SetValue(Canvas.LeftProperty, x - btn.Width / 2);
btn.SetValue(Canvas.TopProperty, y + buttonHeight + minVerticalSpace - minVerticalSpace / 2);
// canvas.Children.Add(btn);
nb.ButtonBase.IsChecked = false;
}
if(subNodes && !hiddenSubNodes)
//if (subNodes)
DrawLine(canvas, x - 3, y + buttonHeight, x - 3, y + buttonHeight + minVerticalSpace);
if (!subNodes && !hiddenSubNodes)
{
nb.ButtonBase.Visibility = Visibility.Hidden;
}
}
#endregion
#region Events
void btn_Click(object sender, RoutedEventArgs e)
{
ToggleButton clicked = (ToggleButton)sender;
btnClicked(clicked.Tag.ToString().Replace("plus", ""));
Debug.WriteLine(clicked.IsChecked);
}
public void btnClicked(string clickedButtonName)
{
for (int i = 0; i < persons.Count; i++)
{
if (persons[i].ID == clickedButtonName)
{
MyCanvas.Children.Clear();
var results = from n in persons
where n.ManagerID == persons[i].ID
select n;
persons[i].Collapsed = !persons[i].Collapsed;
foreach (Person p in results)
{
p.Opened = !persons[i].Collapsed;
}
Refresh();
return;
}
}
}
#endregion
private void printPos(UIElement el)
{
int x = (int)Canvas.GetLeft(el);
int y = (int)Canvas.GetTop(el);
// textPos.Text = string.Format("x:{0} y:{1}", x, y);
}
private bool _isDrag = false;
private Point _dragOffset;
private void ele_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement el = sender as UIElement;
if (el != null)
{
_isDrag = true;
_dragOffset = e.GetPosition(el);
el.CaptureMouse();
}
}
private void ele_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDrag == true)
{
UIElement el = sender as UIElement;
el.ReleaseMouseCapture();
_isDrag = false;
}
}
private void ele_MouseMove(object sender, MouseEventArgs e)
{
if (_isDrag == true)
{
Point pt = Mouse.GetPosition(board);
UIElement el = sender as UIElement;
Canvas.SetLeft(el, pt.X - _dragOffset.X);
Canvas.SetTop(el, pt.Y - _dragOffset.Y);
printPos(el);
}
}
}
}
UserCortrol:
NodeBox.xaml
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="140" Height="80">
NodeBox.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace Topographies
{
///
/// NodeBox.xaml 的交互逻辑
///
public partial class NodeBox : UserControl
{
private double _fontSize = 10;
public NodeBox(double scale)
{
_Scale = scale;
InitializeComponent();
tbEmployeeName.FontSize = _fontSize * Scale;
tbEmployeeName.SetValue(Canvas.TopProperty, 5 * scale);
tbEmployeeName.SetValue(Canvas.LeftProperty, 5 * scale);
tbEmployeeName.Height = 20 * scale;
tbEmployeeName.Width = 130 * scale;
tbEmployeeName.TextWrapping = TextWrapping.NoWrap;
tbEmployeeName.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, 130 * scale, 20 * scale) };
tbTitle.FontSize = _fontSize * Scale;
tbTitle.SetValue(Canvas.TopProperty, 20 * scale);
tbTitle.SetValue(Canvas.LeftProperty, 5 * scale);
tbTitle.Height = 20 * scale;
tbTitle.Width = 130 * scale;
tbTitle.TextWrapping = TextWrapping.NoWrap;
tbTitle.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, 130 * scale, 20 * scale) };
tbDepartment.FontSize = _fontSize * Scale;
tbDepartment.SetValue(Canvas.TopProperty, 35 * scale);
tbDepartment.SetValue(Canvas.LeftProperty, 5 * scale);
tbDepartment.Height = 20 * scale;
tbDepartment.TextWrapping = TextWrapping.NoWrap;
tbDepartment.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, 130 * scale, 20 * scale) };
tbExtension.FontSize = _fontSize * Scale;
tbExtension.SetValue(Canvas.TopProperty, 60 * scale);
tbExtension.SetValue(Canvas.LeftProperty, 5 * scale);
tbExtension.Height = 20 * scale;
tbExtension.TextWrapping = TextWrapping.NoWrap;
tbExtension.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, 130 * scale, 20 * scale) };
recBorder.StrokeThickness = 2 * scale;
recBorder.RadiusX = 5 * scale;
recBorder.RadiusY = 5 * scale;
recBorder.Width = this.Width * scale;
recBorder.Height = this.Height * scale;
}
private double _Scale = 1;
public double Scale
{
get
{
return _Scale;
}
set
{
_Scale = value;
}
}
private string _ID;
public string ID
{
get
{
return _ID;
}
set
{
_ID = value;
}
}
private string _EmployeeName;
public string EmployeeName
{
get
{
return _EmployeeName;
}
set
{
_EmployeeName = value;
tbEmployeeName.Text = _EmployeeName;
}
}
private string _Title;
public string Title
{
get
{
return _Title;
}
set
{
_Title = value;
tbTitle.Text = _Title;
}
}
private string _Target;
public string Target
{
get
{
return _Target;
}
set
{
_Target = value;
ButtonBase.Tag = _Target;
}
}
private string _Department;
public string Department
{
get
{
return _Department;
}
set
{
_Department = value;
tbDepartment.Text = _Department;
}
}
private string _Extension;
public string Extension
{
get
{
return _Extension;
}
set
{
_Extension = value;
if (!string.IsNullOrEmpty(_Extension))
{
tbExtension.Text = "ext. :" + _Extension;
}
}
}
private void canvMain_MouseEnter(object sender, MouseEventArgs e)
{
//this.mouseEnter.Begin();
}
private void canvMain_MouseLeave(object sender, MouseEventArgs e)
{
// this.mouseLeave.Begin();
}
}
}
Person.cs
using System;
namespace Topographies
{
public class Person
{
public string ID;
public string Name;
public string ManagerID;
public string Title;
public string Department;
public string Extension;
public string Email;
#region More Fileds
public int Level = 1;
public int SubNodes = 0;
public int HiddenSubNodes = 0;
public int NodeOrder = 1;
public double MinChildWidth;
public double X;
public double StartX;
public Boolean Opened = false;
public Boolean Collapsed = true;
#endregion
public static Person GetPerson(string id, string name, string managerID, string title, string department, string extension, string email)
{
Person p = new Person();
p.ID = id;
p.Name = name;
p.ManagerID = managerID;
p.Title = title;
p.Department = department;
p.Extension = extension;
p.Email = email;
return p;
}
}
}
Utilcs.cs
using System;
using System.Windows.Media;
namespace Topographies
{
public static class Util
{
///
/// Generate a color based on HEX values
///
///
///
public static SolidColorBrush GetColorFromHex(string myColor)
{
return new SolidColorBrush(
Color.FromArgb(
Convert.ToByte(myColor.Substring(1, 2), 16),
Convert.ToByte(myColor.Substring(3, 2), 16),
Convert.ToByte(myColor.Substring(5, 2), 16),
Convert.ToByte(myColor.Substring(7, 2), 16)
)
);
}
}
}
OrgChart_Data.xml