mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-04-21 00:47:25 +08:00
整理一下项目文件
This commit is contained in:
626
AIStudio.Wpf.DiagramDesigner/Helpers/ColorHelper.cs
Normal file
626
AIStudio.Wpf.DiagramDesigner/Helpers/ColorHelper.cs
Normal 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
|
||||
}
|
||||
93
AIStudio.Wpf.DiagramDesigner/Helpers/ColorPickerManager.cs
Normal file
93
AIStudio.Wpf.DiagramDesigner/Helpers/ColorPickerManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
17
AIStudio.Wpf.DiagramDesigner/Helpers/ConnectorInfo.cs
Normal file
17
AIStudio.Wpf.DiagramDesigner/Helpers/ConnectorInfo.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
406
AIStudio.Wpf.DiagramDesigner/Helpers/ControlExtession.cs
Normal file
406
AIStudio.Wpf.DiagramDesigner/Helpers/ControlExtession.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
182
AIStudio.Wpf.DiagramDesigner/Helpers/CopyHelper.cs
Normal file
182
AIStudio.Wpf.DiagramDesigner/Helpers/CopyHelper.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
29
AIStudio.Wpf.DiagramDesigner/Helpers/CursorPointManager.cs
Normal file
29
AIStudio.Wpf.DiagramDesigner/Helpers/CursorPointManager.cs
Normal 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
|
||||
|
||||
}
|
||||
}
|
||||
45
AIStudio.Wpf.DiagramDesigner/Helpers/DesignerHelper.cs
Normal file
45
AIStudio.Wpf.DiagramDesigner/Helpers/DesignerHelper.cs
Normal 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
|
||||
}
|
||||
}
|
||||
94
AIStudio.Wpf.DiagramDesigner/Helpers/DoCommandManager.cs
Normal file
94
AIStudio.Wpf.DiagramDesigner/Helpers/DoCommandManager.cs
Normal 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); } }
|
||||
}
|
||||
}
|
||||
18
AIStudio.Wpf.DiagramDesigner/Helpers/DragObject.cs
Normal file
18
AIStudio.Wpf.DiagramDesigner/Helpers/DragObject.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
18
AIStudio.Wpf.DiagramDesigner/Helpers/EnumExtension.cs
Normal file
18
AIStudio.Wpf.DiagramDesigner/Helpers/EnumExtension.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
25
AIStudio.Wpf.DiagramDesigner/Helpers/EventToCommandArgs.cs
Normal file
25
AIStudio.Wpf.DiagramDesigner/Helpers/EventToCommandArgs.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
264
AIStudio.Wpf.DiagramDesigner/Helpers/Extention.Object.cs
Normal file
264
AIStudio.Wpf.DiagramDesigner/Helpers/Extention.Object.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
AIStudio.Wpf.DiagramDesigner/Helpers/IPathFinder.cs
Normal file
14
AIStudio.Wpf.DiagramDesigner/Helpers/IPathFinder.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
697
AIStudio.Wpf.DiagramDesigner/Helpers/OrthogonalPathFinder.cs
Normal file
697
AIStudio.Wpf.DiagramDesigner/Helpers/OrthogonalPathFinder.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
AIStudio.Wpf.DiagramDesigner/Helpers/PointHelper.cs
Normal file
47
AIStudio.Wpf.DiagramDesigner/Helpers/PointHelper.cs
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
85
AIStudio.Wpf.DiagramDesigner/Helpers/SegmentHelper.cs
Normal file
85
AIStudio.Wpf.DiagramDesigner/Helpers/SegmentHelper.cs
Normal 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)
|
||||
{
|
||||
//设三点坐标是A,B,C,AB构成直线,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
AIStudio.Wpf.DiagramDesigner/Helpers/ToolBoxData.cs
Normal file
29
AIStudio.Wpf.DiagramDesigner/Helpers/ToolBoxData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
AIStudio.Wpf.DiagramDesigner/Helpers/WeakINPCEventHandler.cs
Normal file
37
AIStudio.Wpf.DiagramDesigner/Helpers/WeakINPCEventHandler.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user