整理一下项目文件

This commit is contained in:
艾竹
2022-10-28 22:45:39 +08:00
parent 334297b074
commit 513937c1d6
598 changed files with 684 additions and 544 deletions

View File

@@ -0,0 +1,626 @@
using System;
using System.Windows.Media;
namespace AIStudio.Wpf.DiagramDesigner
{
/// <summary>
/// 类 名ColorHelper
/// 功 能提供从RGB到HSV/HSL色彩空间的相互转换
/// 日 期2015-02-08
/// 修 改2015-03-20
/// 作 者ls9512
/// </summary>
public static class ColorHelper
{
public static Color AdjustBrightness(Color c1, double factor)
{
double r = ((c1.R * factor) > 255) ? 255 : (c1.R * factor);
double g = ((c1.G * factor) > 255) ? 255 : (c1.G * factor);
double b = ((c1.B * factor) > 255) ? 255 : (c1.B * factor);
Color c = Color.FromArgb(c1.A, (byte)r, (byte)g, (byte)b);
return c;
}
/// <summary>
/// RGB转换HSV
/// </summary>
/// <param name="rgb"></param>
/// <returns></returns>
public static ColorHSV RgbToHsv(ColorRGB rgb)
{
float min, max, tmp, H, S, V;
float R = rgb.R * 1.0f / 255, G = rgb.G * 1.0f / 255, B = rgb.B * 1.0f / 255;
tmp = Math.Min(R, G);
min = Math.Min(tmp, B);
tmp = Math.Max(R, G);
max = Math.Max(tmp, B);
// H
H = 0;
if (max == min)
{
H = 0;
}
else if (max == R && G > B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 0;
}
else if (max == R && G < B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 360;
}
else if (max == G)
{
H = H = 60 * (B - R) * 1.0f / (max - min) + 120;
}
else if (max == B)
{
H = H = 60 * (R - G) * 1.0f / (max - min) + 240;
}
// S
if (max == 0)
{
S = 0;
}
else
{
S = (max - min) * 1.0f / max;
}
// V
V = max;
return new ColorHSV((int)H, (int)(S * 255), (int)(V * 255));
}
/// <summary>
/// HSV转换RGB
/// </summary>
/// <param name="hsv"></param>
/// <returns></returns>
public static ColorRGB HsvToRgb(ColorHSV hsv)
{
if (hsv.H == 360) hsv.H = 359; // 360为全黑原因不明
float R = 0f, G = 0f, B = 0f;
if (hsv.S == 0)
{
return new ColorRGB(hsv.V, hsv.V, hsv.V);
}
float S = hsv.S * 1.0f / 255, V = hsv.V * 1.0f / 255;
int H1 = (int)(hsv.H * 1.0f / 60), H = hsv.H;
float F = H * 1.0f / 60 - H1;
float P = V * (1.0f - S);
float Q = V * (1.0f - F * S);
float T = V * (1.0f - (1.0f - F) * S);
switch (H1)
{
case 0: R = V; G = T; B = P; break;
case 1: R = Q; G = V; B = P; break;
case 2: R = P; G = V; B = T; break;
case 3: R = P; G = Q; B = V; break;
case 4: R = T; G = P; B = V; break;
case 5: R = V; G = P; B = Q; break;
}
R = R * 255;
G = G * 255;
B = B * 255;
while (R > 255) R -= 255;
while (R < 0) R += 255;
while (G > 255) G -= 255;
while (G < 0) G += 255;
while (B > 255) B -= 255;
while (B < 0) B += 255;
return new ColorRGB((int)R, (int)G, (int)B);
}
/// <summary>
/// RGB转换HSL
/// </summary>
/// <param name="rgb"></param>
/// <returns></returns>
public static ColorHSL RgbToHsl(ColorRGB rgb)
{
float min, max, tmp, H, S, L;
float R = rgb.R * 1.0f / 255, G = rgb.G * 1.0f / 255, B = rgb.B * 1.0f / 255;
tmp = Math.Min(R, G);
min = Math.Min(tmp, B);
tmp = Math.Max(R, G);
max = Math.Max(tmp, B);
// H
H = 0;
if (max == min)
{
H = 0; // 此时H应为未定义通常写为0
}
else if (max == R && G > B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 0;
}
else if (max == R && G < B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 360;
}
else if (max == G)
{
H = H = 60 * (B - R) * 1.0f / (max - min) + 120;
}
else if (max == B)
{
H = H = 60 * (R - G) * 1.0f / (max - min) + 240;
}
// L
L = 0.5f * (max + min);
// S
S = 0;
if (L == 0 || max == min)
{
S = 0;
}
else if (0 < L && L < 0.5)
{
S = (max - min) / (L * 2);
}
else if (L > 0.5)
{
S = (max - min) / (2 - 2 * L);
}
return new ColorHSL((int)H, (int)(S * 255), (int)(L * 255));
}
/// <summary>
/// HSL转换RGB
/// </summary>
/// <param name="hsl"></param>
/// <returns></returns>
public static ColorRGB HslToRgb(ColorHSL hsl)
{
float R = 0f, G = 0f, B = 0f;
float S = hsl.S * 1.0f / 255, L = hsl.L * 1.0f / 255;
float temp1, temp2, temp3;
if (S == 0f) // 灰色
{
R = L;
G = L;
B = L;
}
else
{
if (L < 0.5f)
{
temp2 = L * (1.0f + S);
}
else
{
temp2 = L + S - L * S;
}
temp1 = 2.0f * L - temp2;
float H = hsl.H * 1.0f / 360;
// R
temp3 = H + 1.0f / 3.0f;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
R = temp3;
// G
temp3 = H;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
G = temp3;
// B
temp3 = H - 1.0f / 3.0f;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
B = temp3;
}
R = R * 255;
G = G * 255;
B = B * 255;
return new ColorRGB((int)R, (int)G, (int)B);
}
/// <summary>
/// RGB转换HSV
/// </summary>
/// <param name="rgb"></param>
/// <returns></returns>
public static ColorHSV ColorToHsv(Color rgb)
{
float min, max, tmp, H, S, V;
float R = rgb.R * 1.0f / 255, G = rgb.G * 1.0f / 255, B = rgb.B * 1.0f / 255;
tmp = Math.Min(R, G);
min = Math.Min(tmp, B);
tmp = Math.Max(R, G);
max = Math.Max(tmp, B);
// H
H = 0;
if (max == min)
{
H = 0;
}
else if (max == R && G > B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 0;
}
else if (max == R && G < B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 360;
}
else if (max == G)
{
H = H = 60 * (B - R) * 1.0f / (max - min) + 120;
}
else if (max == B)
{
H = H = 60 * (R - G) * 1.0f / (max - min) + 240;
}
// S
if (max == 0)
{
S = 0;
}
else
{
S = (max - min) * 1.0f / max;
}
// V
V = max;
return new ColorHSV((int)H, (int)(S * 255), (int)(V * 255));
}
/// <summary>
/// HSV转换RGB
/// </summary>
/// <param name="hsv"></param>
/// <returns></returns>
public static Color HsvToColor(ColorHSV hsv)
{
if (hsv.H == 360) hsv.H = 359; // 360为全黑原因不明
float R = 0f, G = 0f, B = 0f;
if (hsv.S == 0)
{
return Color.FromArgb(0xff, (byte)hsv.V, (byte)hsv.V, (byte)hsv.V);
}
float S = hsv.S * 1.0f / 255, V = hsv.V * 1.0f / 255;
int H1 = (int)(hsv.H * 1.0f / 60), H = hsv.H;
float F = H * 1.0f / 60 - H1;
float P = V * (1.0f - S);
float Q = V * (1.0f - F * S);
float T = V * (1.0f - (1.0f - F) * S);
switch (H1)
{
case 0: R = V; G = T; B = P; break;
case 1: R = Q; G = V; B = P; break;
case 2: R = P; G = V; B = T; break;
case 3: R = P; G = Q; B = V; break;
case 4: R = T; G = P; B = V; break;
case 5: R = V; G = P; B = Q; break;
}
R = R * 255;
G = G * 255;
B = B * 255;
while (R > 255) R -= 255;
while (R < 0) R += 255;
while (G > 255) G -= 255;
while (G < 0) G += 255;
while (B > 255) B -= 255;
while (B < 0) B += 255;
return Color.FromArgb(0xff, (byte)R, (byte)G, (byte)B);
}
/// <summary>
/// RGB转换HSL
/// </summary>
/// <param name="rgb"></param>
/// <returns></returns>
public static ColorHSL ColorToHsl(Color rgb)
{
float min, max, tmp, H, S, L;
float R = rgb.R * 1.0f / 255, G = rgb.G * 1.0f / 255, B = rgb.B * 1.0f / 255;
tmp = Math.Min(R, G);
min = Math.Min(tmp, B);
tmp = Math.Max(R, G);
max = Math.Max(tmp, B);
// H
H = 0;
if (max == min)
{
H = 0; // 此时H应为未定义通常写为0
}
else if (max == R && G > B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 0;
}
else if (max == R && G < B)
{
H = 60 * (G - B) * 1.0f / (max - min) + 360;
}
else if (max == G)
{
H = H = 60 * (B - R) * 1.0f / (max - min) + 120;
}
else if (max == B)
{
H = H = 60 * (R - G) * 1.0f / (max - min) + 240;
}
// L
L = 0.5f * (max + min);
// S
S = 0;
if (L == 0 || max == min)
{
S = 0;
}
else if (0 < L && L < 0.5)
{
S = (max - min) / (L * 2);
}
else if (L > 0.5)
{
S = (max - min) / (2 - 2 * L);
}
return new ColorHSL((int)H, (int)(S * 255), (int)(L * 255));
}
/// <summary>
/// HSL转换RGB
/// </summary>
/// <param name="hsl"></param>
/// <returns></returns>
public static Color HslToColor(ColorHSL hsl)
{
float R = 0f, G = 0f, B = 0f;
float S = hsl.S * 1.0f / 255, L = hsl.L * 1.0f / 255;
float temp1, temp2, temp3;
if (S == 0f) // 灰色
{
R = L;
G = L;
B = L;
}
else
{
if (L < 0.5f)
{
temp2 = L * (1.0f + S);
}
else
{
temp2 = L + S - L * S;
}
temp1 = 2.0f * L - temp2;
float H = hsl.H * 1.0f / 360;
// R
temp3 = H + 1.0f / 3.0f;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
R = temp3;
// G
temp3 = H;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
G = temp3;
// B
temp3 = H - 1.0f / 3.0f;
if (temp3 < 0) temp3 += 1.0f;
if (temp3 > 1) temp3 -= 1.0f;
B = temp3;
}
R = R * 255;
G = G * 255;
B = B * 255;
return Color.FromArgb(0xff, (byte)R, (byte)G, (byte)B);
}
}
#region RGB / HSV / HSL
/// <summary>
/// 类 名ColorHSL
/// 功 能H 色相 \ S 饱和度(纯度) \ L 亮度 颜色模型
/// 日 期2015-02-08
/// 修 改2015-03-20
/// 作 者ls9512
/// </summary>
public class ColorHSL
{
public ColorHSL(int h, int s, int l)
{
this._h = h;
this._s = s;
this._l = l;
}
private int _h;
private int _s;
private int _l;
/// <summary>
/// 色相
/// </summary>
public int H
{
get { return this._h; }
set
{
this._h = value;
this._h = this._h > 360 ? 360 : this._h;
this._h = this._h < 0 ? 0 : this._h;
}
}
/// <summary>
/// 饱和度(纯度)
/// </summary>
public int S
{
get { return this._s; }
set
{
this._s = value;
this._s = this._s > 255 ? 255 : this._s;
this._s = this._s < 0 ? 0 : this._s;
}
}
/// <summary>
/// 饱和度
/// </summary>
public int L
{
get { return this._l; }
set
{
this._l = value;
this._l = this._l > 255 ? 255 : this._l;
this._l = this._l < 0 ? 0 : this._l;
}
}
}
/// <summary>
/// 类 名ColorHSV
/// 功 能H 色相 \ S 饱和度(纯度) \ V 明度 颜色模型
/// 日 期2015-01-22
/// 修 改2015-03-20
/// 作 者ls9512
/// </summary>
public class ColorHSV
{
/// <summary>
/// 构造方法
/// </summary>
/// <param name="h"></param>
/// <param name="s"></param>
/// <param name="v"></param>
public ColorHSV(int h, int s, int v)
{
this._h = h;
this._s = s;
this._v = v;
}
private int _h;
private int _s;
private int _v;
/// <summary>
/// 色相
/// </summary>
public int H
{
get { return this._h; }
set
{
this._h = value;
this._h = this._h > 360 ? 360 : this._h;
this._h = this._h < 0 ? 0 : this._h;
}
}
/// <summary>
/// 饱和度(纯度)
/// </summary>
public int S
{
get { return this._s; }
set
{
this._s = value;
this._s = this._s > 255 ? 255 : this._s;
this._s = this._s < 0 ? 0 : this._s;
}
}
/// <summary>
/// 明度
/// </summary>
public int V
{
get { return this._v; }
set
{
this._v = value;
this._v = this._v > 255 ? 255 : this._v;
this._v = this._v < 0 ? 0 : this._v;
}
}
}
/// <summary>
/// 类 名ColorRGB
/// 功 能R 红色 \ G 绿色 \ B 蓝色 颜色模型
/// 所有颜色模型的基类RGB是用于输出到屏幕的颜色模式所以所有模型都将转换成RGB输出
/// 日 期2015-01-22
/// 修 改2015-03-20
/// 作 者ls9512
/// </summary>
public class ColorRGB
{
/// <summary>
/// 构造方法
/// </summary>
/// <param name="r"></param>
/// <param name="g"></param>
/// <param name="b"></param>
public ColorRGB(int r, int g, int b)
{
this._r = r;
this._g = g;
this._b = b;
}
private int _r;
private int _g;
private int _b;
/// <summary>
/// 红色
/// </summary>
public int R
{
get { return this._r; }
set
{
this._r = value;
this._r = this._r > 255 ? 255 : this._r;
this._r = this._r < 0 ? 0 : this._r;
}
}
/// <summary>
/// 绿色
/// </summary>
public int G
{
get { return this._g; }
set
{
this._g = value;
this._g = this._g > 255 ? 255 : this._g;
this._g = this._g < 0 ? 0 : this._g;
}
}
/// <summary>
/// 蓝色
/// </summary>
public int B
{
get { return this._b; }
set
{
this._b = value;
this._b = this._b > 255 ? 255 : this._b;
this._b = this._b < 0 ? 0 : this._b;
}
}
/// <summary>
/// 获取实际颜色
/// </summary>
/// <returns></returns>
public Color GetColor()
{
return Color.FromArgb(0xff, (byte)this._r, (byte)this._g, (byte)this._b);
}
}
#endregion
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace AIStudio.Wpf.DiagramDesigner
{
/// <summary>
/// 获取当前光标处颜色win8下wpf测试成功
/// </summary>
public class ColorPickerManager
{
/// <summary>
///
/// </summary>
/// <param name="x">鼠标相对于显示器的坐标X</param>
/// <param name="y">鼠标相对于显示器的坐标Y</param>
/// <returns></returns>
//public static System.Drawing.Color GetColor(int x, int y)
//{
// IntPtr hdc = GetDC(IntPtr.Zero);
// uint pixel = GetPixel(hdc, x, y);
// ReleaseDC(IntPtr.Zero, hdc);
// System.Drawing.Color color = System.Drawing.Color.FromArgb((int)(pixel & 0x000000FF), (int)(pixel & 0x0000FF00) >> 8, (int)(pixel & 0x00FF0000) >> 16);
// return color;
//}
/// <summary>
///
/// </summary>
/// <param name="x">鼠标相对于显示器的坐标X</param>
/// <param name="y">鼠标相对于显示器的坐标Y</param>
/// <returns></returns>
public static System.Windows.Media.Color GetColor(int x, int y)
{
IntPtr hdc = GetDC(IntPtr.Zero);
uint pixel = GetPixel(hdc, x, y);
ReleaseDC(IntPtr.Zero, hdc);
System.Windows.Media.Color color = System.Windows.Media.Color.FromRgb((byte)(pixel & 0x000000FF), (byte)((pixel & 0x0000FF00) >> 8), (byte)((pixel & 0x00FF0000) >> 16));
return color;
}
[DllImport("gdi32.dll")]
private static extern bool BitBlt(
IntPtr hdcDest, // 目标设备的句柄
int nXDest, // 目标对象的左上角的X坐标
int nYDest, // 目标对象的左上角的X坐标
int nWidth, // 目标对象的矩形的宽度
int nHeight, // 目标对象的矩形的长度
IntPtr hdcSrc, // 源设备的句柄
int nXSrc, // 源对象的左上角的X坐标
int nYSrc, // 源对象的左上角的X坐标
int dwRop // 光栅的操作值
);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateDC(
string lpszDriver, // 驱动名称
string lpszDevice, // 设备名称
string lpszOutput, // 无用,可以设定位"NULL"
IntPtr lpInitData // 任意的打印机数据
);
/// <summary>
/// 获取指定窗口的设备场景
/// </summary>
/// <param name="hwnd">将获取其设备场景的窗口的句柄。若为0则要获取整个屏幕的DC</param>
/// <returns>指定窗口的设备场景句柄出错则为0</returns>
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
/// <summary>
/// 释放由调用GetDC函数获取的指定设备场景
/// </summary>
/// <param name="hwnd">要释放的设备场景相关的窗口句柄</param>
/// <param name="hdc">要释放的设备场景句柄</param>
/// <returns>执行成功为1否则为0</returns>
[DllImport("user32.dll")]
private static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);
/// <summary>
/// 在指定的设备场景中取得一个像素的RGB值
/// </summary>
/// <param name="hdc">一个设备场景的句柄</param>
/// <param name="nXPos">逻辑坐标中要检查的横坐标</param>
/// <param name="nYPos">逻辑坐标中要检查的纵坐标</param>
/// <returns>指定点的颜色</returns>
[DllImport("gdi32.dll")]
private static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace AIStudio.Wpf.DiagramDesigner
{
public struct ConnectorInfo
{
public double DesignerItemLeft { get; set; }
public double DesignerItemTop { get; set; }
public Size DesignerItemSize { get; set; }
public Point Position { get; set; }
public ConnectorOrientation Orientation { get; set; }
}
}

View File

@@ -0,0 +1,406 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace AIStudio.Wpf.DiagramDesigner
{
public static class ControlExtession
{
#region TreeView操作扩展方法
//code:http://www.codeproject.com/Articles/36193/WPF-TreeView-tools
/// <summary>
/// Returns the TreeViewItem of a data bound object.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <returns>The TreeViewItem of the data bound object or null.</returns>
public static TreeViewItem GetItemFromObject(this TreeView treeView, object obj)
{
try
{
DependencyObject dObject = GetContainerFormObject(treeView, obj);
TreeViewItem tvi = dObject as TreeViewItem;
while (tvi == null)
{
dObject = VisualTreeHelper.GetParent(dObject);
tvi = dObject as TreeViewItem;
}
return tvi;
}
catch
{
}
return null;
}
private static DependencyObject GetContainerFormObject(ItemsControl item, object obj)
{
if (item == null)
return null;
DependencyObject dObject = null;
dObject = item.ItemContainerGenerator.ContainerFromItem(obj);
if (dObject != null)
return dObject;
var query = from childItem in item.Items.Cast<object>()
let childControl = item.ItemContainerGenerator.ContainerFromItem(childItem) as ItemsControl
select GetContainerFormObject(childControl, obj);
return query.FirstOrDefault(i => i != null);
}
/// <summary>
/// Selects a data bound object of a TreeView.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
public static void SelectObject(this TreeView treeView, object obj)
{
treeView.SelectObject(obj, true);
}
/// <summary>
/// Selects or deselects a data bound object of a TreeView.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <param name="selected">select or deselect</param>
public static void SelectObject(this TreeView treeView, object obj, bool selected)
{
var tvi = treeView.GetItemFromObject(obj);
if (tvi != null)
{
tvi.IsSelected = selected;
}
}
/// <summary>
/// Returns if a data bound object of a TreeView is selected.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <returns>Returns true if the object is selected, and false if it is not selected or obj is not in the tree.</returns>
public static bool IsObjectSelected(this TreeView treeView, object obj)
{
var tvi = treeView.GetItemFromObject(obj);
if (tvi != null)
{
return tvi.IsSelected;
}
return false;
}
/// <summary>
/// Returns if a data bound object of a TreeView is focused.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <returns>Returns true if the object is focused, and false if it is not focused or obj is not in the tree.</returns>
public static bool IsObjectFocused(this TreeView treeView, object obj)
{
var tvi = treeView.GetItemFromObject(obj);
if (tvi != null)
{
return tvi.IsFocused;
}
return false;
}
/// <summary>
/// Expands a data bound object of a TreeView.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
public static void ExpandObject(this TreeView treeView, object obj)
{
treeView.ExpandObject(obj, true);
}
/// <summary>
/// Expands or collapses a data bound object of a TreeView.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <param name="expanded">expand or collapse</param>
public static void ExpandObject(this TreeView treeView, object obj, bool expanded)
{
var tvi = treeView.GetItemFromObject(obj);
if (tvi != null)
{
tvi.IsExpanded = expanded;
if (expanded)
{
// update layout, so that following calls to f.e. SelectObject on child nodes will
// find theire TreeViewNodes
treeView.UpdateLayout();
}
}
}
/// <summary>
/// Returns if a douta bound object of a TreeView is expanded.
/// </summary>
/// <param name="treeView">TreeView</param>
/// <param name="obj">Data bound object</param>
/// <returns>Returns true if the object is expanded, and false if it is collapsed or obj is not in the tree.</returns>
public static bool IsObjectExpanded(this TreeView treeView, object obj)
{
var tvi = treeView.GetItemFromObject(obj);
if (tvi != null)
{
return tvi.IsExpanded;
}
return false;
}
/// <summary>
/// Retuns the parent TreeViewItem.
/// </summary>
/// <param name="item">TreeViewItem</param>
/// <returns>Parent TreeViewItem</returns>
public static TreeViewItem GetParentItem(this TreeViewItem item)
{
var dObject = VisualTreeHelper.GetParent(item);
TreeViewItem tvi = dObject as TreeViewItem;
while (tvi == null)
{
dObject = VisualTreeHelper.GetParent(dObject);
tvi = dObject as TreeViewItem;
}
return tvi;
}
#endregion
#region FindParent/FindChildren
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
return parent ?? TryFindParent<T>(parentObject);
}
//public static T FindParent<T>(DependencyObject child) where T : DependencyObject
//{
// //get parent item
// var parentObject = VisualTreeHelper.GetParent(child);
// //we've reached the end of the tree
// if (parentObject == null) return null;
// //check if the parent matches the type we're looking for
// var parent = parentObject as T;
// return parent ?? FindParent<T>(parentObject);
//}
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(this DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Keep in mind that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
//handle content elements separately
var contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
var fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//also try searching for parent in framework elements (such as DockPanel, etc)
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null) return parent;
}
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
/// <summary>
/// Analyzes both visual and logical tree in order to find all elements of a given
/// type that are descendants of the <paramref name="source"/> item.
/// </summary>
/// <typeparam name="T">The type of the queried items.</typeparam>
/// <param name="source">The root element that marks the source of the search. If the
/// source is already of the requested type, it will not be included in the result.</param>
/// <param name="forceUsingTheVisualTreeHelper">Sometimes it's better to search in the VisualTree (e.g. in tests)</param>
/// <returns>All descendants of <paramref name="source"/> that match the requested type.</returns>
public static IEnumerable<T> FindChildren<T>(this DependencyObject source, bool forceUsingTheVisualTreeHelper = false) where T : DependencyObject
{
if (source != null)
{
var childs = GetChildObjects(source, forceUsingTheVisualTreeHelper);
foreach (DependencyObject child in childs)
{
//analyze if children match the requested type
if (child != null && child is T)
{
yield return (T)child;
}
//recurse tree
foreach (T descendant in FindChildren<T>(child))
{
yield return descendant;
}
}
}
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetChild"/> method, which also
/// supports content elements. Keep in mind that for content elements,
/// this method falls back to the logical tree of the element.
/// </summary>
/// <param name="parent">The item to be processed.</param>
/// <param name="forceUsingTheVisualTreeHelper">Sometimes it's better to search in the VisualTree (e.g. in tests)</param>
/// <returns>The submitted item's child elements, if available.</returns>
public static IEnumerable<DependencyObject> GetChildObjects(this DependencyObject parent, bool forceUsingTheVisualTreeHelper = false)
{
if (parent == null) yield break;
if (!forceUsingTheVisualTreeHelper && (parent is ContentElement || parent is FrameworkElement))
{
//use the logical tree for content / framework elements
foreach (object obj in LogicalTreeHelper.GetChildren(parent))
{
var depObj = obj as DependencyObject;
if (depObj != null) yield return (DependencyObject)obj;
}
}
else
{
//use the visual tree per default
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
yield return VisualTreeHelper.GetChild(parent, i);
}
}
}
/// <summary>
/// Tries to locate a given item within the visual tree,
/// starting with the dependency object at a given position.
/// </summary>
/// <typeparam name="T">The type of the element to be found
/// on the visual tree of the element at the given location.</typeparam>
/// <param name="reference">The main element which is used to perform
/// hit testing.</param>
/// <param name="point">The position to be evaluated on the origin.</param>
public static T TryFindFromPoint<T>(UIElement reference, Point point)
where T : DependencyObject
{
var element = reference.InputHitTest(point) as DependencyObject;
if (element == null)
return null;
if (element is T)
return (T)element;
return TryFindParent<T>(element);
//DependencyObject hitObject = reference.InputHitTest(point) as DependencyObject;
//while (hitObject != null &&
// hitObject.GetType() != typeof(DesignerCanvas))
//{
// if (hitObject is T)
// {
// return (T)hitObject;
// }
// hitObject = VisualTreeHelper.GetParent(hitObject);
//}
//return null;
}
#endregion
}
}

View File

@@ -0,0 +1,187 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Input;
namespace AIStudio.Wpf.DiagramDesigner
{
public class ControlMouseDoubleClickCommandBehavior : Behavior<FrameworkElement>
{
#region Overrides
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
EnableDisableElement();
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
}
#endregion
#region Private Methods
private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//Get the ItemsControl and then get the item, and check there
//is an actual item, as if we are using a ListView we may have clicked the
//headers which are not items
//ItemsControl listView = sender as ItemsControl;
//DependencyObject originalSender = e.OriginalSource as DependencyObject;
//if (listView == null || originalSender == null) return;
//DependencyObject container =
// ItemsControl.ContainerFromElement
// (sender as ItemsControl, e.OriginalSource as DependencyObject);
//if (container == null ||
// container == DependencyProperty.UnsetValue) return;
//// found a container, now find the item.
//object activatedItem =
// listView.ItemContainerGenerator.ItemFromContainer(container);
//if (activatedItem != null)
//{
// Invoke(activatedItem, e);
//}
FrameworkElement control = sender as FrameworkElement;
DependencyObject originalSender = e.OriginalSource as DependencyObject;
if (control == null || originalSender == null) return;
if (e.ClickCount >= 2)
{
Invoke(control, e);
}
}
private static void OnCommandChanged(ControlMouseDoubleClickCommandBehavior thisBehaviour,
DependencyPropertyChangedEventArgs e)
{
if (thisBehaviour == null)
{
return;
}
if (e.OldValue != null)
{
((ICommand)e.OldValue).CanExecuteChanged -= thisBehaviour.OnCommandCanExecuteChanged;
}
ICommand command = (ICommand)e.NewValue;
if (command != null)
{
command.CanExecuteChanged += thisBehaviour.OnCommandCanExecuteChanged;
}
thisBehaviour.EnableDisableElement();
}
private bool IsAssociatedElementDisabled()
{
return AssociatedObject != null && !AssociatedObject.IsEnabled;
}
private void EnableDisableElement()
{
if (AssociatedObject == null || Command == null)
{
return;
}
AssociatedObject.IsEnabled = Command.CanExecute(this.CommandParameter);
}
private void OnCommandCanExecuteChanged(object sender, EventArgs e)
{
EnableDisableElement();
}
#endregion
#region Protected Methods
/// <param name="parameter">The EventArgs of the fired event.</param>
protected void Invoke(object clickedItem, MouseButtonEventArgs parameter)
{
if (IsAssociatedElementDisabled())
{
return;
}
ICommand command = Command;
if (command != null && command.CanExecute(CommandParameter))
{
command.Execute(new EventToCommandArgs(clickedItem, command,
CommandParameter, (EventArgs)parameter));
}
}
#endregion
#region DPs
#region CommandParameter
/// <summary>
/// Identifies the <see cref="CommandParameter" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(ControlMouseDoubleClickCommandBehavior),
new PropertyMetadata(null,
(s, e) =>
{
ControlMouseDoubleClickCommandBehavior sender = s as ControlMouseDoubleClickCommandBehavior;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This is a DependencyProperty.
/// </summary>
public object CommandParameter
{
get { return (Object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
#endregion
#region Command
/// <summary
/// >
/// Identifies the <see cref="Command" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(ControlMouseDoubleClickCommandBehavior),
new PropertyMetadata(null,
(s, e) => OnCommandChanged(s as ControlMouseDoubleClickCommandBehavior, e)));
/// <summary>
/// Gets or sets the ICommand that this trigger is bound to. This
/// is a DependencyProperty.
/// </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#endregion
#endregion
}
}

View File

@@ -0,0 +1,184 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Input;
namespace AIStudio.Wpf.DiagramDesigner
{
public class ControlMouseLeftButtonDownCommandBehavior : Behavior<FrameworkElement>
{
#region Overrides
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
EnableDisableElement();
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObject_PreviewMouseLeftButtonDown;
}
#endregion
#region Private Methods
private void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//Get the ItemsControl and then get the item, and check there
//is an actual item, as if we are using a ListView we may have clicked the
//headers which are not items
//ItemsControl listView = sender as ItemsControl;
//DependencyObject originalSender = e.OriginalSource as DependencyObject;
//if (listView == null || originalSender == null) return;
//DependencyObject container =
// ItemsControl.ContainerFromElement
// (sender as ItemsControl, e.OriginalSource as DependencyObject);
//if (container == null ||
// container == DependencyProperty.UnsetValue) return;
//// found a container, now find the item.
//object activatedItem =
// listView.ItemContainerGenerator.ItemFromContainer(container);
//if (activatedItem != null)
//{
// Invoke(activatedItem, e);
//}
FrameworkElement control = sender as FrameworkElement;
DependencyObject originalSender = e.OriginalSource as DependencyObject;
if (control == null || originalSender == null) return;
Invoke(control, e);
}
private static void OnCommandChanged(ControlMouseLeftButtonDownCommandBehavior thisBehaviour,
DependencyPropertyChangedEventArgs e)
{
if (thisBehaviour == null)
{
return;
}
if (e.OldValue != null)
{
((ICommand)e.OldValue).CanExecuteChanged -= thisBehaviour.OnCommandCanExecuteChanged;
}
ICommand command = (ICommand)e.NewValue;
if (command != null)
{
command.CanExecuteChanged += thisBehaviour.OnCommandCanExecuteChanged;
}
thisBehaviour.EnableDisableElement();
}
private bool IsAssociatedElementDisabled()
{
return AssociatedObject != null && !AssociatedObject.IsEnabled;
}
private void EnableDisableElement()
{
if (AssociatedObject == null || Command == null)
{
return;
}
AssociatedObject.IsEnabled = Command.CanExecute(this.CommandParameter);
}
private void OnCommandCanExecuteChanged(object sender, EventArgs e)
{
EnableDisableElement();
}
#endregion
#region Protected Methods
/// <param name="parameter">The EventArgs of the fired event.</param>
protected void Invoke(object clickedItem, MouseButtonEventArgs parameter)
{
if (IsAssociatedElementDisabled())
{
return;
}
ICommand command = Command;
if (command != null && command.CanExecute(CommandParameter))
{
command.Execute(new EventToCommandArgs(clickedItem, command,
CommandParameter, (EventArgs)parameter));
}
}
#endregion
#region DPs
#region CommandParameter
/// <summary>
/// Identifies the <see cref="CommandParameter" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(ControlMouseLeftButtonDownCommandBehavior),
new PropertyMetadata(null,
(s, e) =>
{
ControlMouseLeftButtonDownCommandBehavior sender = s as ControlMouseLeftButtonDownCommandBehavior;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This is a DependencyProperty.
/// </summary>
public object CommandParameter
{
get { return (Object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
#endregion
#region Command
/// <summary
/// >
/// Identifies the <see cref="Command" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(ControlMouseLeftButtonDownCommandBehavior),
new PropertyMetadata(null,
(s, e) => OnCommandChanged(s as ControlMouseLeftButtonDownCommandBehavior, e)));
/// <summary>
/// Gets or sets the ICommand that this trigger is bound to. This
/// is a DependencyProperty.
/// </summary>
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#endregion
#endregion
}
}

View File

@@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;
namespace AIStudio.Wpf.DiagramDesigner
{
public class CopyHelper
{
public static T DeepCopyByReflect<T>(T obj)
{
//如果是字符串或值类型则直接返回
if (obj == null || obj is string || obj.GetType().IsValueType) return obj;
object retval = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (FieldInfo field in fields)
{
try { field.SetValue(retval, DeepCopyByReflect(field.GetValue(obj))); }
catch { }
}
return (T)retval;
}
public static TChild AutoCopy<TParent, TChild>(TParent parent) where TChild : TParent, new()
{
TChild child = new TChild();
var ParentType = typeof(TParent);
var Properties = ParentType.GetProperties();
foreach (var Propertie in Properties)
{
//循环遍历属性
if (Propertie.CanRead && Propertie.CanWrite)
{
//进行属性拷贝
Propertie.SetValue(child, Propertie.GetValue(parent, null), null);
}
}
return child;
}
public static T AutoCopy<T>(T source) where T : new()
{
//如果是字符串或值类型则直接返回
if (source == null || source is string || source.GetType().IsValueType) return source;
T target = new T();
var Properties = typeof(T).GetProperties();
foreach (var Propertie in Properties)
{
//循环遍历属性
if (Propertie.CanRead && Propertie.CanWrite)
{
//进行属性拷贝
Propertie.SetValue(target, Propertie.GetValue(source, null), null);
}
}
return target;
}
public static T DeepCopy<T>(T obj)
{
//如果是字符串或值类型则直接返回
if (obj == null || obj is string || obj.GetType().IsValueType) return obj;
object retval;
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer xml = new XmlSerializer(typeof(T));
xml.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
retval = xml.Deserialize(ms);
ms.Close();
}
return (T)retval;
}
/// <summary>
/// 反射实现两个类的对象之间相同属性的值的复制
/// 适用于初始化新实体
/// </summary>
/// <typeparam name="D">返回的实体</typeparam>
/// <typeparam name="S">数据源实体</typeparam>
/// <param name="s">数据源实体</param>
/// <returns>返回的新实体</returns>
public static D Mapper<D, S>(S s)
{
D d = Activator.CreateInstance<D>(); //构造新实例
try
{
var Types = s.GetType();//获得类型
var Typed = typeof(D);
foreach (PropertyInfo sp in Types.GetProperties().Where(p => p.CanRead))//获得类型的属性字段
{
foreach (PropertyInfo dp in Typed.GetProperties().Where(p => p.CanWrite))
{
if (dp.Name == sp.Name && dp.PropertyType == sp.PropertyType)//判断属性名是否相同
{
dp.SetValue(d, sp.GetValue(s, null), null);//获得s对象属性的值复制给d对象的属性
}
}
}
}
catch (Exception ex)
{
throw ex;
}
return d;
}
public static void CopyPropertyValue(object s, object d, string propertyName = null)
{
try
{
var Types = s.GetType();//获得类型
var Typed = d.GetType();
var sps = Types.GetProperties().Where(p => p.CanRead && (string.IsNullOrEmpty(propertyName) || p.Name == propertyName));//获得类型的属性字段
var dps = Typed.GetProperties().Where(p => p.CanWrite && (string.IsNullOrEmpty(propertyName) || p.Name == propertyName));
foreach (PropertyInfo sp in sps)//获得类型的属性字段
{
foreach (PropertyInfo dp in dps)
{
if (dp.Name == sp.Name && dp.PropertyType == sp.PropertyType)//判断属性名是否相同
{
dp.SetValue(d, sp.GetValue(s, null), null);//获得s对象属性的值复制给d对象的属性
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public static ColorViewModel Mapper(IColorViewModel s)
{
var d = CopyHelper.Mapper<ColorViewModel, IColorViewModel>(s);
d.LineColor = CopyHelper.Mapper<ColorObject, IColorObject>(s.LineColor);
d.FillColor = CopyHelper.Mapper<ColorObject, IColorObject>(s.FillColor);
d.LineColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.LineColor.GradientStop);
d.FillColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.FillColor.GradientStop);
return d;
}
public static T Mapper<T>(IColorViewModel s) where T: IColorViewModel
{
var d = CopyHelper.Mapper<T, IColorViewModel>(s);
d.LineColor = CopyHelper.Mapper<ColorObjectItem, IColorObject>(s.LineColor);
d.FillColor = CopyHelper.Mapper<ColorObjectItem, IColorObject>(s.FillColor);
d.LineColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.LineColor.GradientStop);
d.FillColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.FillColor.GradientStop);
return d;
}
public static void CopyPropertyValue(IColorViewModel s, IColorViewModel d, string propertyName = null)
{
if (propertyName == "LineColor")
{
CopyPropertyValue(s.LineColor, d.LineColor);
d.LineColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.LineColor.GradientStop);
}
else if (propertyName == "FillColor")
{
CopyPropertyValue(s.FillColor, d.FillColor);
d.FillColor.GradientStop = CopyHelper.DeepCopy<ObservableCollection<GradientStop>>(s.FillColor.GradientStop);
}
else
{
CopyPropertyValue((object)s, (object)d, propertyName);
}
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace AIStudio.Wpf.DiagramDesigner
{
/// <summary>
/// win8下wpf程序测试成功
/// </summary>
public class CursorPointManager
{
#region
[DllImport("user32")]
private static extern bool GetCursorPos(out Point lpPoint);
/// <summary>
/// 获取光标相对于显示器的位置
/// </summary>
/// <returns></returns>
public static Point GetCursorPosition()
{
Point showPoint = new Point();
GetCursorPos(out showPoint);
return showPoint;
}
#endregion
}
}

View File

@@ -0,0 +1,45 @@
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace AIStudio.Wpf.DiagramDesigner
{
public class DesignerHelper
{
private static bool? _isInDesignMode;
public static bool IsInDesignMode
{
get
{
if (!_isInDesignMode.HasValue)
{
_isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}
#region IsInMainThread
/// <summary>
/// 是否是在主线程中处理
/// </summary>
public static bool IsInMainThread
{
get
{
if (Thread.CurrentThread.IsBackground || Thread.CurrentThread.IsThreadPoolThread) return false;
if (Thread.CurrentThread.Name == "主线程") return true;
if (Application.Current == null)
return true;
return Thread.CurrentThread == Application.Current.Dispatcher.Thread;
}
}
#endregion
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AIStudio.Wpf.DiagramDesigner
{
class DoCommandManager
{
#region Command定义
public class Command
{
string name;
Action action;
Action unDoAction;
Action clearAction;
internal Command(string name, Action action, Action unDoAction, Action clearAction)
{
this.name = name;
this.action = action;
this.unDoAction = unDoAction;
this.clearAction = clearAction;
}
internal void Do() { action(); }
internal void UnDo() { unDoAction(); }
internal void Clear() { if (clearAction != null) clearAction(); }
public override string ToString()
{
return name.ToString();
}
}
#endregion
public Stack<Command> ReDoActionStack { get; private set; }
public Stack<Command> UnDoActionStack { get; private set; }
public int Capacity { get; set; } = 10;
public DoCommandManager()
{
ReDoActionStack = new Stack<Command>();
UnDoActionStack = new Stack<Command>();
}
public void DoNewCommand(string name, Action action, Action unDoAction, Action clearAction = null, bool doit = true)
{
if (UnDoActionStack.Count >= Capacity)
{
//清理
var clear = UnDoActionStack.LastOrDefault();
clear.Clear();
UnDoActionStack = new Stack<Command>(UnDoActionStack.Take(Capacity - 1).Reverse());
}
var cmd = new Command(name, action, unDoAction, clearAction);
UnDoActionStack.Push(cmd);
ReDoActionStack.Clear();
if (doit)
{
cmd.Do();
}
}
public void UnDo()
{
if (!CanUnDo)
return;
var cmd = UnDoActionStack.Pop();
ReDoActionStack.Push(cmd);
cmd.UnDo();
}
public void ReDo()
{
if (!CanReDo)
return;
var cmd = ReDoActionStack.Pop();
UnDoActionStack.Push(cmd);
cmd.Do();
}
public bool CanUnDo { get { return UnDoActionStack.Count != 0; } }
public bool CanReDo { get { return ReDoActionStack.Count != 0; } }
//public IEnumerable<Command> Actions { get { return ReDoActionStack.Reverse().Concat(UnDoActionStack); } }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace AIStudio.Wpf.DiagramDesigner
{
// Wraps info of the dragged object into a class
public class DragObject
{
public Size? DesiredSize { get; set; }
public Type ContentType { get; set; }
public string Icon { get; set; }
public IColorViewModel ColorViewModel { get; set; }
public DesignerItemBase DesignerItem { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.ComponentModel;
using System.Reflection;
namespace AIStudio.Wpf.DiagramDesigner
{
public static class EnumExtension
{
public static string GetDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
return attribute == null ? value.ToString() : attribute.Description;
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace AIStudio.Wpf.DiagramDesigner
{
public class EventToCommandArgs
{
public Object Sender { get; private set; }
public ICommand CommandRan { get; private set; }
public Object CommandParameter { get; private set; }
public EventArgs EventArgs { get; private set; }
public EventToCommandArgs(Object sender, ICommand commandRan, Object commandParameter, EventArgs eventArgs)
{
this.Sender = sender;
this.CommandRan = commandRan;
this.CommandParameter = commandParameter;
this.EventArgs = eventArgs;
}
}
}

View File

@@ -0,0 +1,264 @@
using Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace AIStudio.Wpf.DiagramDesigner
{
public static partial class Extention
{
/// <summary>
/// 构造函数
/// </summary>
static Extention()
{
JsonSerializerSettings setting = new JsonSerializerSettings();
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
{
//日期类型默认格式化处理
setting.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
return setting;
});
}
private static BindingFlags _bindingFlags { get; }
= BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static;
/// <summary>
/// 将一个object对象序列化返回一个byte[]
/// </summary>
/// <param name="obj">能序列化的对象</param>
/// <returns></returns>
public static byte[] ToBytes(this object obj)
{
using (MemoryStream ms = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
return ms.GetBuffer();
}
}
/// <summary>
/// 判断是否为Null或者空
/// </summary>
/// <param name="obj">对象</param>
/// <returns></returns>
public static bool IsNullOrEmpty(this object obj)
{
if (obj == null)
return true;
else
{
string objStr = obj.ToString();
return string.IsNullOrEmpty(objStr);
}
}
/// <summary>
/// 将对象序列化成Json字符串
/// </summary>
/// <param name="obj">需要序列化的对象</param>
/// <returns></returns>
public static string ToJson(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
public static JsonSerializerSettings Settings { get; set; } = new JsonSerializerSettings
{
DateFormatString = "yyyy-MM-dd HH:mm:ss.fff",
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
};
public static string ToStandardTimeFormatJson(this object obj)
{
return JsonConvert.SerializeObject(obj, Settings);
}
/// <summary>
/// 实体类转json数据速度快
/// </summary>
/// <param name="t">实体类</param>
/// <returns></returns>
public static string EntityToJson(this object t)
{
if (t == null)
return null;
string jsonStr = "";
jsonStr += "{";
PropertyInfo[] infos = t.GetType().GetProperties();
for (int i = 0; i < infos.Length; i++)
{
jsonStr = jsonStr + "\"" + infos[i].Name + "\":\"" + infos[i].GetValue(t).ToString() + "\"";
if (i != infos.Length - 1)
jsonStr += ",";
}
jsonStr += "}";
return jsonStr;
}
///// <summary>
///// 深复制
///// </summary>
///// <typeparam name="T">类型</typeparam>
///// <param name="obj">对象</param>
///// <returns></returns>
//public static T DeepClone<T>(this T obj) where T : class
//{
// if (obj == null)
// return null;
// return obj.ToJson().ToObject<T>();
//}
/// <summary>
/// 将对象序列化为XML字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="obj">对象</param>
/// <returns></returns>
public static string ToXmlStr<T>(this T obj)
{
var jsonStr = obj.ToJson();
var xmlDoc = JsonConvert.DeserializeXmlNode(jsonStr);
string xmlDocStr = xmlDoc.InnerXml;
return xmlDocStr;
}
/// <summary>
/// 将对象序列化为XML字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="obj">对象</param>
/// <param name="rootNodeName">根节点名(建议设为xml)</param>
/// <returns></returns>
public static string ToXmlStr<T>(this T obj, string rootNodeName)
{
var jsonStr = obj.ToJson();
var xmlDoc = JsonConvert.DeserializeXmlNode(jsonStr, rootNodeName);
string xmlDocStr = xmlDoc.InnerXml;
return xmlDocStr;
}
/// <summary>
/// 是否拥有某属性
/// </summary>
/// <param name="obj">对象</param>
/// <param name="propertyName">属性名</param>
/// <returns></returns>
public static bool ContainsProperty(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName, _bindingFlags) != null;
}
/// <summary>
/// 获取某属性值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="propertyName">属性名</param>
/// <returns></returns>
public static object GetPropertyValue(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName, _bindingFlags).GetValue(obj);
}
/// <summary>
/// 设置某属性值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="propertyName">属性名</param>
/// <param name="value">值</param>
/// <returns></returns>
public static void SetPropertyValue(this object obj, string propertyName, object value)
{
obj.GetType().GetProperty(propertyName, _bindingFlags).SetValue(obj, value);
}
/// <summary>
/// 是否拥有某字段
/// </summary>
/// <param name="obj">对象</param>
/// <param name="fieldName">字段名</param>
/// <returns></returns>
public static bool ContainsField(this object obj, string fieldName)
{
return obj.GetType().GetField(fieldName, _bindingFlags) != null;
}
/// <summary>
/// 获取某字段值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="fieldName">字段名</param>
/// <returns></returns>
public static object GetGetFieldValue(this object obj, string fieldName)
{
return obj.GetType().GetField(fieldName, _bindingFlags).GetValue(obj);
}
/// <summary>
/// 设置某字段值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="fieldName">字段名</param>
/// <param name="value">值</param>
/// <returns></returns>
public static void SetFieldValue(this object obj, string fieldName, object value)
{
obj.GetType().GetField(fieldName, _bindingFlags).SetValue(obj, value);
}
///// <summary>
///// 改变实体类型
///// </summary>
///// <param name="obj">对象</param>
///// <param name="targetType">目标类型</param>
///// <returns></returns>
//public static object ChangeType(this object obj, Type targetType)
//{
// return obj.ToJson().ToObject(targetType);
//}
///// <summary>
///// 改变实体类型
///// </summary>
///// <typeparam name="T">目标泛型</typeparam>
///// <param name="obj">对象</param>
///// <returns></returns>
//public static T ChangeType<T>(this object obj)
//{
// return obj.ToJson().ToObject<T>();
//}
/// <summary>
/// 改变类型
/// </summary>
/// <param name="obj">原对象</param>
/// <param name="targetType">目标类型</param>
/// <returns></returns>
public static object ChangeType_ByConvert(this object obj, Type targetType)
{
object resObj;
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
NullableConverter newNullableConverter = new NullableConverter(targetType);
resObj = newNullableConverter.ConvertFrom(obj);
}
else
{
resObj = Convert.ChangeType(obj, targetType);
}
return resObj;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace AIStudio.Wpf.DiagramDesigner
{
public interface IPathFinder
{
List<Point> GetConnectionLine(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false);
List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false);
}
}

View File

@@ -0,0 +1,697 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace AIStudio.Wpf.DiagramDesigner
{
// Note: I couldn't find a useful open source library that does
// orthogonal routing so started to write something on my own.
// Categorize this as a quick and dirty short term solution.
// I will keep on searching.
// Helper class to provide an orthogonal connection path
public class OrthogonalPathFinder : IPathFinder
{
private const int const_margin = 20;
public List<Point> GetConnectionLine(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, bool sourceInnerPoint = false)
{
List<Point> linePoints = new List<Point>();
int margin1 = sourceInnerPoint ? 0 : const_margin;
int margin2 = const_margin;
Rect rectSource = GetRectWithMargin(source, margin1);
Rect rectSink = GetRectWithMargin(sink, margin2);
Point startPoint = GetOffsetPoint(source, rectSource, sourceInnerPoint);
Point endPoint = GetOffsetPoint(sink, rectSink);
linePoints.Add(startPoint);
Point currentPoint = startPoint;
if (!rectSink.Contains(currentPoint) && !rectSource.Contains(endPoint))
{
while (true)
{
#region source node
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource, rectSink }))
{
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
Point neighbour = GetNearestVisibleNeighborSink(currentPoint, endPoint, sink, rectSource, rectSink);
if (!double.IsNaN(neighbour.X))
{
linePoints.Add(neighbour);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
if (currentPoint == startPoint)
{
bool flag;
Point n = GetNearestNeighborSource(source, endPoint, rectSource, rectSink, out flag, sourceInnerPoint);
if (linePoints.Contains(n))
{
break;
}
linePoints.Add(n);
currentPoint = n;
if (!IsRectVisible(currentPoint, rectSink, new Rect[] { rectSource }))
{
Point n1, n2;
GetOppositeCorners(source.Orientation, rectSource, out n1, out n2, sourceInnerPoint);
if (flag)
{
linePoints.Add(n1);
currentPoint = n1;
}
else
{
linePoints.Add(n2);
currentPoint = n2;
}
if (!IsRectVisible(currentPoint, rectSink, new Rect[] { rectSource }))
{
if (flag)
{
linePoints.Add(n2);
currentPoint = n2;
}
else
{
linePoints.Add(n1);
currentPoint = n1;
}
}
}
}
#endregion
#region sink node
else // from here on we jump to the sink node
{
Point n1, n2; // neighbour corner
Point s1, s2; // opposite corner
GetNeighborCorners(sink.Orientation, rectSink, out s1, out s2);
GetOppositeCorners(sink.Orientation, rectSink, out n1, out n2);
bool n1Visible = IsPointVisible(currentPoint, n1, new Rect[] { rectSource, rectSink });
bool n2Visible = IsPointVisible(currentPoint, n2, new Rect[] { rectSource, rectSink });
if (n1Visible && n2Visible)
{
if (rectSource.Contains(n1))
{
linePoints.Add(n2);
if (rectSource.Contains(s2))
{
linePoints.Add(n1);
linePoints.Add(s1);
}
else
linePoints.Add(s2);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
if (rectSource.Contains(n2))
{
linePoints.Add(n1);
if (rectSource.Contains(s1))
{
linePoints.Add(n2);
linePoints.Add(s2);
}
else
linePoints.Add(s1);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
{
linePoints.Add(n1);
if (rectSource.Contains(s1))
{
linePoints.Add(n2);
linePoints.Add(s2);
}
else
linePoints.Add(s1);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
else
{
linePoints.Add(n2);
if (rectSource.Contains(s2))
{
linePoints.Add(n1);
linePoints.Add(s1);
}
else
linePoints.Add(s2);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
}
else if (n1Visible)
{
linePoints.Add(n1);
if (rectSource.Contains(s1))
{
linePoints.Add(n2);
linePoints.Add(s2);
}
else
linePoints.Add(s1);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
else
{
linePoints.Add(n2);
if (rectSource.Contains(s2))
{
linePoints.Add(n1);
linePoints.Add(s1);
}
else
linePoints.Add(s2);
linePoints.Add(endPoint);
currentPoint = endPoint;
break;
}
}
#endregion
}
}
else
{
linePoints.Add(endPoint);
}
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource, rectSink }, source.Orientation, sink.Orientation);
CheckPathEnd(source, sink, showLastLine, linePoints, sourceInnerPoint);
return linePoints;
}
public List<Point> GetConnectionLine(ConnectorInfo source, Point sinkPoint, ConnectorOrientation preferredOrientation, bool showLastLine, bool isInnerPoint = false)
{
List<Point> linePoints = new List<Point>();
int margin = isInnerPoint ? 0 : const_margin;
Rect rectSource = GetRectWithMargin(source, margin);
Point startPoint = GetOffsetPoint(source, rectSource, isInnerPoint);
Point endPoint = sinkPoint;
linePoints.Add(startPoint);
Point currentPoint = startPoint;
if (!rectSource.Contains(endPoint))
{
while (true)
{
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource }))
{
linePoints.Add(endPoint);
break;
}
bool sideFlag;
Point n = GetNearestNeighborSource(source, endPoint, rectSource, out sideFlag, isInnerPoint);
linePoints.Add(n);
currentPoint = n;
if (IsPointVisible(currentPoint, endPoint, new Rect[] { rectSource }))
{
linePoints.Add(endPoint);
break;
}
else
{
Point n1, n2;
GetOppositeCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
if (sideFlag)
linePoints.Add(n1);
else
linePoints.Add(n2);
linePoints.Add(endPoint);
break;
}
}
}
else
{
linePoints.Add(endPoint);
}
if (preferredOrientation != ConnectorOrientation.None)
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource }, source.Orientation, preferredOrientation);
else
linePoints = OptimizeLinePoints(linePoints, new Rect[] { rectSource }, source.Orientation, GetOpositeOrientation(source.Orientation));
if (!showLastLine)
{
linePoints.Insert(0, source.Position);
}
return linePoints;
}
private static List<Point> OptimizeLinePoints(List<Point> linePoints, Rect[] rectangles, ConnectorOrientation sourceOrientation, ConnectorOrientation sinkOrientation)
{
List<Point> points = new List<Point>();
int cut = 0;
for (int i = 0; i < linePoints.Count; i++)
{
if (i >= cut)
{
for (int k = linePoints.Count - 1; k > i; k--)
{
if (IsPointVisible(linePoints[i], linePoints[k], rectangles))
{
cut = k;
break;
}
}
points.Add(linePoints[i]);
}
}
#region Line
for (int j = 0; j < points.Count - 1; j++)
{
if (points[j].X != points[j + 1].X && points[j].Y != points[j + 1].Y)
{
ConnectorOrientation orientationFrom;
ConnectorOrientation orientationTo;
// orientation from point
if (j == 0)
orientationFrom = sourceOrientation;
else
orientationFrom = GetOrientation(points[j], points[j - 1]);
// orientation to pint
if (j == points.Count - 2)
orientationTo = sinkOrientation;
else
orientationTo = GetOrientation(points[j + 1], points[j + 2]);
if ((orientationFrom == ConnectorOrientation.Left || orientationFrom == ConnectorOrientation.Right) &&
(orientationTo == ConnectorOrientation.Left || orientationTo == ConnectorOrientation.Right))
{
double centerX = Math.Min(points[j].X, points[j + 1].X) + Math.Abs(points[j].X - points[j + 1].X) / 2;
points.Insert(j + 1, new Point(centerX, points[j].Y));
points.Insert(j + 2, new Point(centerX, points[j + 2].Y));
if (points.Count - 1 > j + 3)
points.RemoveAt(j + 3);
return points;
}
if ((orientationFrom == ConnectorOrientation.Top || orientationFrom == ConnectorOrientation.Bottom) &&
(orientationTo == ConnectorOrientation.Top || orientationTo == ConnectorOrientation.Bottom))
{
double centerY = Math.Min(points[j].Y, points[j + 1].Y) + Math.Abs(points[j].Y - points[j + 1].Y) / 2;
points.Insert(j + 1, new Point(points[j].X, centerY));
points.Insert(j + 2, new Point(points[j + 2].X, centerY));
if (points.Count - 1 > j + 3)
points.RemoveAt(j + 3);
return points;
}
if ((orientationFrom == ConnectorOrientation.Left || orientationFrom == ConnectorOrientation.Right) &&
(orientationTo == ConnectorOrientation.Top || orientationTo == ConnectorOrientation.Bottom))
{
points.Insert(j + 1, new Point(points[j + 1].X, points[j].Y));
return points;
}
if ((orientationFrom == ConnectorOrientation.Top || orientationFrom == ConnectorOrientation.Bottom) &&
(orientationTo == ConnectorOrientation.Left || orientationTo == ConnectorOrientation.Right))
{
points.Insert(j + 1, new Point(points[j].X, points[j + 1].Y));
return points;
}
}
}
#endregion
return points;
}
private static ConnectorOrientation GetOrientation(Point p1, Point p2)
{
if (p1.X == p2.X)
{
if (p1.Y >= p2.Y)
return ConnectorOrientation.Bottom;
else
return ConnectorOrientation.Top;
}
else if (p1.Y == p2.Y)
{
if (p1.X >= p2.X)
return ConnectorOrientation.Right;
else
return ConnectorOrientation.Left;
}
throw new Exception("Failed to retrieve orientation");
}
private static Orientation GetOrientation(ConnectorOrientation sourceOrientation, bool isInnerPoint)
{
if (isInnerPoint)
{
return Orientation.Vertical;
}
switch (sourceOrientation)
{
case ConnectorOrientation.Left:
return Orientation.Horizontal;
case ConnectorOrientation.Top:
return Orientation.Vertical;
case ConnectorOrientation.Right:
return Orientation.Horizontal;
case ConnectorOrientation.Bottom:
return Orientation.Vertical;
default:
throw new Exception("Unknown ConnectorOrientation");
}
}
private static Point GetNearestNeighborSource(ConnectorInfo source, Point endPoint, Rect rectSource, Rect rectSink, out bool flag, bool isInnerPoint)
{
Point n1, n2; // neighbors
GetNeighborCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
if (rectSink.Contains(n1))
{
flag = false;
return n2;
}
if (rectSink.Contains(n2))
{
flag = true;
return n1;
}
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
{
flag = true;
return n1;
}
else
{
flag = false;
return n2;
}
}
private static Point GetNearestNeighborSource(ConnectorInfo source, Point endPoint, Rect rectSource, out bool flag, bool isInnerPoint)
{
Point n1, n2; // neighbors
GetNeighborCorners(source.Orientation, rectSource, out n1, out n2, isInnerPoint);
if ((Distance(n1, endPoint) <= Distance(n2, endPoint)))
{
flag = true;
return n1;
}
else
{
flag = false;
return n2;
}
}
private static Point GetNearestVisibleNeighborSink(Point currentPoint, Point endPoint, ConnectorInfo sink, Rect rectSource, Rect rectSink)
{
Point s1, s2; // neighbors on sink side
GetNeighborCorners(sink.Orientation, rectSink, out s1, out s2);
bool flag1 = IsPointVisible(currentPoint, s1, new Rect[] { rectSource, rectSink });
bool flag2 = IsPointVisible(currentPoint, s2, new Rect[] { rectSource, rectSink });
if (flag1) // s1 visible
{
if (flag2) // s1 and s2 visible
{
if (rectSink.Contains(s1))
return s2;
if (rectSink.Contains(s2))
return s1;
if ((Distance(s1, endPoint) <= Distance(s2, endPoint)))
return s1;
else
return s2;
}
else
{
return s1;
}
}
else // s1 not visible
{
if (flag2) // only s2 visible
{
return s2;
}
else // s1 and s2 not visible
{
return new Point(double.NaN, double.NaN);
}
}
}
private static bool IsPointVisible(Point fromPoint, Point targetPoint, Rect[] rectangles)
{
foreach (Rect rect in rectangles)
{
if (RectangleIntersectsLine(rect, fromPoint, targetPoint))
return false;
}
return true;
}
private static bool IsRectVisible(Point fromPoint, Rect targetRect, Rect[] rectangles)
{
if (IsPointVisible(fromPoint, targetRect.TopLeft, rectangles))
return true;
if (IsPointVisible(fromPoint, targetRect.TopRight, rectangles))
return true;
if (IsPointVisible(fromPoint, targetRect.BottomLeft, rectangles))
return true;
if (IsPointVisible(fromPoint, targetRect.BottomRight, rectangles))
return true;
return false;
}
private static bool RectangleIntersectsLine(Rect rect, Point startPoint, Point endPoint)
{
rect.Inflate(-1, -1);
return rect.IntersectsWith(new Rect(startPoint, endPoint));
}
private static void GetOppositeCorners(ConnectorOrientation orientation, Rect rect, out Point n1, out Point n2, bool isInnerPoint = false)
{
if (isInnerPoint)
{
n1 = rect.Location; n2 = rect.Location;
return;
}
switch (orientation)
{
case ConnectorOrientation.Left:
n1 = rect.TopRight; n2 = rect.BottomRight;
break;
case ConnectorOrientation.Top:
n1 = rect.BottomLeft; n2 = rect.BottomRight;
break;
case ConnectorOrientation.Right:
n1 = rect.TopLeft; n2 = rect.BottomLeft;
break;
case ConnectorOrientation.Bottom:
n1 = rect.TopLeft; n2 = rect.TopRight;
break;
default:
throw new Exception("No opposite corners found!");
}
}
private static void GetNeighborCorners(ConnectorOrientation orientation, Rect rect, out Point n1, out Point n2, bool isInnerPoint = false)
{
if (isInnerPoint)
{
n1 = rect.Location; n2 = rect.Location;
return;
}
switch (orientation)
{
case ConnectorOrientation.Left:
n1 = rect.TopLeft; n2 = rect.BottomLeft;
break;
case ConnectorOrientation.Top:
n1 = rect.TopLeft; n2 = rect.TopRight;
break;
case ConnectorOrientation.Right:
n1 = rect.TopRight; n2 = rect.BottomRight;
break;
case ConnectorOrientation.Bottom:
n1 = rect.BottomLeft; n2 = rect.BottomRight;
break;
default:
throw new Exception("No neighour corners found!");
}
}
private static double Distance(Point p1, Point p2)
{
return Point.Subtract(p1, p2).Length;
}
private static Rect GetRectWithMargin(ConnectorInfo connectorThumb, double margin)
{
Rect rect = new Rect(connectorThumb.DesignerItemLeft,
connectorThumb.DesignerItemTop,
0,
0);
rect.Inflate(margin, margin);
return rect;
}
private static Point GetOffsetPoint(ConnectorInfo connector, Rect rect, bool isInnerPoint = false)
{
Point offsetPoint = new Point();
if (isInnerPoint)
{
offsetPoint = new Point(connector.Position.X, connector.Position.Y);
return offsetPoint;
}
switch (connector.Orientation)
{
case ConnectorOrientation.Left:
offsetPoint = new Point(rect.Left, connector.Position.Y);
break;
case ConnectorOrientation.Top:
offsetPoint = new Point(connector.Position.X, rect.Top);
break;
case ConnectorOrientation.Right:
offsetPoint = new Point(rect.Right, connector.Position.Y);
break;
case ConnectorOrientation.Bottom:
offsetPoint = new Point(connector.Position.X, rect.Bottom);
break;
default:
break;
}
return offsetPoint;
}
private static void CheckPathEnd(ConnectorInfo source, ConnectorInfo sink, bool showLastLine, List<Point> linePoints, bool sourceInnerPoint)
{
if (showLastLine)
{
Point startPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
double marginPath = 15;
switch (source.Orientation)
{
case ConnectorOrientation.Left:
startPoint = new Point(source.Position.X - marginPath, source.Position.Y);
break;
case ConnectorOrientation.Top:
startPoint = new Point(source.Position.X, source.Position.Y - marginPath);
break;
case ConnectorOrientation.Right:
startPoint = new Point(source.Position.X + marginPath, source.Position.Y);
break;
case ConnectorOrientation.Bottom:
startPoint = new Point(source.Position.X, source.Position.Y + marginPath);
break;
default:
break;
}
if (sourceInnerPoint)
{
startPoint = new Point(source.Position.X, source.Position.Y);
}
switch (sink.Orientation)
{
case ConnectorOrientation.Left:
endPoint = new Point(sink.Position.X - marginPath, sink.Position.Y);
break;
case ConnectorOrientation.Top:
endPoint = new Point(sink.Position.X, sink.Position.Y - marginPath);
break;
case ConnectorOrientation.Right:
endPoint = new Point(sink.Position.X + marginPath, sink.Position.Y);
break;
case ConnectorOrientation.Bottom:
endPoint = new Point(sink.Position.X, sink.Position.Y + marginPath);
break;
default:
break;
}
linePoints.Insert(0, startPoint);
linePoints.Add(endPoint);
}
else
{
linePoints.Insert(0, source.Position);
linePoints.Add(sink.Position);
}
}
private static ConnectorOrientation GetOpositeOrientation(ConnectorOrientation connectorOrientation)
{
switch (connectorOrientation)
{
case ConnectorOrientation.Left:
return ConnectorOrientation.Right;
case ConnectorOrientation.Top:
return ConnectorOrientation.Bottom;
case ConnectorOrientation.Right:
return ConnectorOrientation.Left;
case ConnectorOrientation.Bottom:
return ConnectorOrientation.Top;
default:
return ConnectorOrientation.Top;
}
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace AIStudio.Wpf.DiagramDesigner
{
public class PointHelper
{
public static Point GetPointForConnector(FullyCreatedConnectorInfo connector)
{
Point point = new Point();
if (connector.IsInnerPoint)
{
point = new Point(connector.DataItem.Left + connector.DataItem.ItemWidth * connector.XRatio,
connector.DataItem.Top + connector.DataItem.ItemHeight * connector.YRatio);
}
else
{
switch (connector.Orientation)
{
case ConnectorOrientation.Top:
point = new Point(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), connector.DataItem.Top);
break;
case ConnectorOrientation.Bottom:
point = new Point(connector.DataItem.Left + (connector.DataItem.ItemWidth / 2), (connector.DataItem.Top + connector.DataItem.ItemHeight));
break;
case ConnectorOrientation.Right:
point = new Point(connector.DataItem.Left + connector.DataItem.ItemWidth, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
break;
case ConnectorOrientation.Left:
point = new Point(connector.DataItem.Left, connector.DataItem.Top + (connector.DataItem.ItemHeight / 2));
break;
}
}
return point;
}
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
namespace AIStudio.Wpf.DiagramDesigner
{
public class SegmentHelper
{
/// <summary>
/// 获得贝塞尔曲线
/// </summary>
/// <param name="currentPt">当前点</param>
/// <param name="lastPt">上一个点</param>
/// <param name="nextPt1">下一个点1</param>
/// <param name="nextPt2">下一个点2</param>
/// <returns></returns>
public static BezierSegment GetBezierSegment(Point currentPt, Point lastPt, Point nextPt1, Point nextPt2)
{
//计算中点
var lastC = GetCenterPoint(lastPt, currentPt);
var nextC1 = GetCenterPoint(currentPt, nextPt1); //贝塞尔控制点
var nextC2 = GetCenterPoint(nextPt1, nextPt2);
//计算相邻中点连线跟目的点的垂足
//效果并不算太好,因为可能点在两个线上或者线的延长线上,计算会有误差
//所以就直接使用中点平移方法。
//var C1 = GetFootPoint(lastC, nextC1, currentPt);
//var C2 = GetFootPoint(nextC1, nextC2, nextPt1);
//计算“相邻中点”的中点
var c1 = GetCenterPoint(lastC, nextC1);
var c2 = GetCenterPoint(nextC1, nextC2);
//计算【"中点"的中点】需要的点位移
var controlPtOffset1 = currentPt - c1;
var controlPtOffset2 = nextPt1 - c2;
//移动控制点
var controlPt1 = nextC1 + controlPtOffset1;
var controlPt2 = nextC1 + controlPtOffset2;
//如果觉得曲线幅度太大,可以将控制点向当前点靠近一定的系数。
controlPt1 = controlPt1 + 0 * (currentPt - controlPt1);
controlPt2 = controlPt2 + 0 * (nextPt1 - controlPt2);
var bzs = new BezierSegment(controlPt1, controlPt2, nextPt1, true);
return bzs;
}
/// <summary>
/// 过c点做A和B连线的垂足
/// </summary>
/// <param name="aPoint"></param>
/// <param name="bPoint"></param>
/// <param name="cPoint"></param>
/// <returns></returns>
private static Point GetFootPoint(Point aPoint, Point bPoint, Point cPoint)
{
//设三点坐标是ABCAB构成直线C是线外的点
//三点对边距离是a,b,c,垂足为D
//根据距离推导公式得AD距离是b平方-a平方+c平方/2c
//本人数学不好可能没考虑点c在线ab上的情况
var offsetADist = (Math.Pow(cPoint.X - aPoint.X, 2) + Math.Pow(cPoint.Y - aPoint.Y, 2) - Math.Pow(bPoint.X - cPoint.X, 2) - Math.Pow(bPoint.Y - cPoint.Y, 2) + Math.Pow(aPoint.X - bPoint.X, 2) + Math.Pow(aPoint.Y - bPoint.Y, 2)) / (2 * GetDistance(aPoint, bPoint));
var v = bPoint - aPoint;
var distab = GetDistance(aPoint, bPoint);
var offsetVector = v * offsetADist / distab;
return aPoint + offsetVector;
}
private static Point GetCenterPoint(Point pt1, Point pt2)
{
return new Point((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2);
}
private static double GetDistance(Point pt1, Point pt2)
{
return Math.Sqrt(Math.Pow(pt1.X - pt2.X, 2) + Math.Pow(pt1.Y - pt2.Y, 2));
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AIStudio.Wpf.DiagramDesigner.Helpers
{
public class ToolBoxData
{
public string Text { get; protected set; }
public string Icon { get; protected set; }
public Type Type { get; protected set; }
public IColorViewModel ColorViewModel { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public object Addition { get; set; }
public ToolBoxData(string text, string icon, Type type, double width, double height)
{
this.Text = text;
this.Icon = icon;
this.Type = type;
this.Width = width;
this.Height = height;
this.ColorViewModel = new ColorViewModel();
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
namespace AIStudio.Wpf.DiagramDesigner.Helpers
{
//[DebuggerNonUserCode]
public sealed class WeakINPCEventHandler
{
private readonly WeakReference _targetReference;
private readonly MethodInfo _method;
public WeakINPCEventHandler(PropertyChangedEventHandler callback)
{
_method = callback.Method;
_targetReference = new WeakReference(callback.Target, true);
}
//[DebuggerNonUserCode]
public void Handler(object sender, PropertyChangedEventArgs e)
{
var target = _targetReference.Target;
if (target != null)
{
var callback = (Action<object, PropertyChangedEventArgs>)Delegate.CreateDelegate(typeof(Action<object, PropertyChangedEventArgs>), target, _method, true);
if (callback != null)
{
callback(sender, e);
}
}
}
}
}