增加了节点树预览、节点定位,容器对象预览

This commit is contained in:
fengjiayi
2024-09-27 23:47:25 +08:00
parent 7461e32a3d
commit 51bdbab4d1
17 changed files with 793 additions and 181 deletions

View File

@@ -345,32 +345,23 @@ namespace Serein.Library.Api
}
public string Key { get; private set; }
public object Instance { get; private set; }
}
//public class IOCMembersChangedEventArgs : FlowEventArgs
//{
// //public enum EventType
// //{
// // /// <summary>
// // /// 登记了类型
// // /// </summary>
// // Registered,
// // /// <summary>
// // /// 构建了类型
// // /// </summary>
// // Completeuild,
// //}
// public IOCMembersChangedEventArgs(Type[] types, object[] dependencies, object[] unfinishedDependencies)
// {
// this.Types = types;
// this.Dependencies = dependencies;
// this.UnfinishedDependencies = unfinishedDependencies;
// }
// public Type[] Types { get; protected set; }
// public object[] Dependencies { get; private set; }
// public object[] UnfinishedDependencies { get; private set; }
//}
/// <summary>
/// 节点需要定位
/// </summary>
/// <param name="eventArgs"></param>
public delegate void NodeLocatedHandler(NodeLocatedEventArgs eventArgs);
public class NodeLocatedEventArgs : FlowEventArgs
{
public NodeLocatedEventArgs(string nodeGuid)
{
NodeGuid = nodeGuid;
}
public string NodeGuid { get; private set; }
}
public interface IFlowEnvironment
{
#region
@@ -447,6 +438,11 @@ namespace Serein.Library.Api
event IOCMembersChangedHandler OnIOCMembersChanged;
/// <summary>
/// 节点需要定位
/// </summary>
event NodeLocatedHandler OnNodeLocate;
#endregion
@@ -461,7 +457,7 @@ namespace Serein.Library.Api
//bool TryGetNodeData(string methodName, out NodeData node);
#region Workbench
#region
/// <summary>
/// 保存当前项目
@@ -501,8 +497,6 @@ namespace Serein.Library.Api
/// </summary>
void Exit();
/// <summary>
/// 设置流程起点节点
/// </summary>
@@ -549,21 +543,7 @@ namespace Serein.Library.Api
/// <param name="expression"></param>
/// <returns></returns>
bool AddInterruptExpression(string key, string expression);
/// <summary>
/// 添加作用于指定节点的中断表达式
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="expression"></param>
/// <returns></returns>
// bool AddInterruptExpression(string nodeGuid,string expression);
// <summary>
// 设置节点数据监视状态
// </summary>
// <param name="nodeGuid">需要监视的节点Guid</param>
// <param name="isMonitor">是否监视</param>
// void SetNodeFLowDataMonitorState(string nodeGuid, bool isMonitor);
/// <summary>
/// 监视指定对象
/// </summary>
@@ -591,7 +571,7 @@ namespace Serein.Library.Api
#endregion
#region Start
#region
/// <summary>
/// 流程启动器调用,监视数据更新通知
@@ -609,6 +589,17 @@ namespace Serein.Library.Api
void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type);
#endregion
#region UI视觉
/// <summary>
/// 节点定位
/// </summary>
/// <param name="nodeGuid"></param>
void NodeLocated(string nodeGuid);
#endregion
}
}

View File

@@ -17,10 +17,10 @@ using System.Threading.Tasks;
namespace Net461DllTest.Flow
{
[DynamicFlow] // 标记该类存在节点方法
[DynamicFlow]
public class LogicControl
{
[AutoInjection] // 标记该属性为依赖项,需要注入
[AutoInjection]
public PlcDevice MyPlc { get; set; }
@@ -58,7 +58,7 @@ namespace Net461DllTest.Flow
#region
[NodeAction(NodeType.Flipflop, "等待信号触发", ReturnType = typeof(int))]
public async Task<IFlipflopContext> WaitTask(OrderSignal order = OrderSignal.A)
public async Task<IFlipflopContext> WaitTask(OrderSignal order = OrderSignal.Command_1)
{
try
{

View File

@@ -19,12 +19,13 @@ namespace Net461DllTest.Flow
{
private List<Form> forms = new List<Form>();
public void OpenView(Form form)
public void OpenView(Form form, bool isTop)
{
form.FormClosing += (s, e) =>
{
// 关闭窗体时执行一些关于逻辑层的操作
};
form.TopMost = isTop;
form.Show();
forms.Add(form);
}
@@ -67,21 +68,20 @@ namespace Net461DllTest.Flow
[NodeAction(NodeType.Action, "打开窗体(指定枚举值)")]
public void OpenForm(IDynamicContext context, FromId fromId = FromId.None)
public void OpenForm(IDynamicContext context, FromId fromId = FromId.None, bool isTop = true)
{
var fromType = EnumHelper.GetBoundValue<FromId, Type>(fromId, attr => attr.Value);
if (fromType is null) return;
if (context.Env.IOC.Instantiate(fromType) is Form form)
{
ViewManagement.OpenView(form);
ViewManagement.OpenView(form, isTop);
}
}
[NodeAction(NodeType.Action, "打开窗体(使用转换器)")]
public void OpenForm2([EnumTypeConvertor(typeof(FromId))] Form form)
public void OpenForm2([EnumTypeConvertor(typeof(FromId))] Form form, bool isTop = true)
{
ViewManagement.OpenView(form);
ViewManagement.OpenView(form, isTop);
}

View File

@@ -10,12 +10,9 @@ namespace Net461DllTest.Signal
{
public enum OrderSignal
{
A,
B,
C,
D,
E,
F,
G
View_1,
View_2,
Command_1,
Command_2,
}
}

View File

@@ -122,6 +122,10 @@ namespace Serein.NodeFlow
/// </summary>
public event IOCMembersChangedHandler OnIOCMembersChanged;
/// <summary>
/// 节点需要定位
/// </summary>
public event NodeLocatedHandler OnNodeLocate;
#endregion
#region
@@ -1037,7 +1041,16 @@ namespace Serein.NodeFlow
#endregion
#region
#region
/// <summary>
/// 定位节点
/// </summary>
/// <param name="nodeGuid"></param>
public void NodeLocated(string nodeGuid)
{
OnNodeLocate?.Invoke(new NodeLocatedEventArgs(nodeGuid));
}
#endregion

View File

@@ -150,10 +150,10 @@ namespace Serein.NodeFlow
#region Ioc容器
// 清除节点使用的对象,筛选出需要初始化的方法描述
var thisRuningMds = new List<MethodDetails>();
thisRuningMds.AddRange(runNodeMd.Where(md => md is not null));
thisRuningMds.AddRange(initMethods.Where(md => md is not null));
thisRuningMds.AddRange(loadingMethods.Where(md => md is not null));
thisRuningMds.AddRange(exitMethods.Where(md => md is not null));
thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null));
thisRuningMds.AddRange(initMethods.Where(md => md?.ActingInstanceType is not null));
thisRuningMds.AddRange(loadingMethods.Where(md => md?.ActingInstanceType is not null));
thisRuningMds.AddRange(exitMethods.Where(md => md?.ActingInstanceType is not null));
// .AddRange(initMethods).AddRange(loadingMethods).a
foreach (var nodeMd in thisRuningMds)

View File

@@ -673,6 +673,7 @@ namespace Serein.NodeFlow.Tool
*/
#endregion
#region
/* /// <summary>
/// 表达式树构建多个参数,有返回值的方法

View File

@@ -0,0 +1,308 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Serein.NodeFlow.Tool
{
public class ObjDynamicCreateHelper
{// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
static Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
public static object Resolve(Dictionary<string, object> properties, string typeName)
{
var obj = CreateObjectWithProperties(properties, typeName);
//SetPropertyValues(obj, properties);
return obj;
}
public static bool TryResolve(Dictionary<string, object> properties, string typeName, out object? result)
{
result = CreateObjectWithProperties(properties, typeName);
bool success = SetPropertyValuesWithValidation(result, properties);
return success;
// 打印赋值结果
}
// 递归方法:打印对象属性及类型
public static void PrintObjectProperties(object obj, string indent = "")
{
var objType = obj.GetType();
foreach (var prop in objType.GetProperties())
{
var value = prop.GetValue(obj);
Console.WriteLine($"{indent}{prop.Name} (Type: {prop.PropertyType.Name}): {value}");
if (value != null)
{
if (prop.PropertyType.IsArray) // 处理数组类型
{
var array = (Array)value;
Console.WriteLine($"{indent}{prop.Name} is an array with {array.Length} elements:");
for (int i = 0; i < array.Length; i++)
{
var element = array.GetValue(i);
if (element != null && element.GetType().IsClass && !(element is string))
{
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element.GetType().Name}) contains a nested object:");
PrintObjectProperties(element, indent + "\t\t");
}
else
{
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element?.GetType().Name}): {element}");
}
}
}
else if (value.GetType().IsClass && !(value is string)) // 处理嵌套对象
{
Console.WriteLine($"{indent}{prop.Name} contains a nested object:");
PrintObjectProperties(value, indent + "\t");
}
}
}
}
// 方法 1: 创建动态类型及其对象实例
public static object CreateObjectWithProperties(Dictionary<string, object> properties, string typeName)
{
// 如果类型已经缓存,直接返回缓存的类型
if (typeCache.ContainsKey(typeName))
{
return Activator.CreateInstance(typeCache[typeName]);
}
// 定义动态程序集和模块
var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
// 定义动态类型
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
// 为每个属性名和值添加相应的属性到动态类型中
foreach (var kvp in properties)
{
string propName = kvp.Key;
object propValue = kvp.Value;
Type propType;
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
{
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
}
else if(propValue is Dictionary<string, object> nestedProperties)
{
// 如果值是嵌套的字典,递归创建嵌套类型
propType = CreateObjectWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
}
else
{
// 如果是普通类型,使用值的类型
propType = propValue?.GetType() ?? typeof(object);
}
// 定义私有字段和公共属性
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
// 定义 getter 方法
var getMethodBuilder = typeBuilder.DefineMethod(
"get_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propType,
Type.EmptyTypes);
var getIL = getMethodBuilder.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.Emit(OpCodes.Ret);
// 定义 setter 方法
var setMethodBuilder = typeBuilder.DefineMethod(
"set_" + propName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new Type[] { propType });
var setIL = setMethodBuilder.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, fieldBuilder);
setIL.Emit(OpCodes.Ret);
// 将 getter 和 setter 方法添加到属性
propertyBuilder.SetGetMethod(getMethodBuilder);
propertyBuilder.SetSetMethod(setMethodBuilder);
}
// 创建类型并缓存
var dynamicType = typeBuilder.CreateType();
typeCache[typeName] = dynamicType;
// 创建对象实例
return Activator.CreateInstance(dynamicType);
}
// 方法 2: 递归设置对象的属性值
public static void SetPropertyValues(object obj, Dictionary<string, object> properties)
{
var objType = obj.GetType();
foreach (var kvp in properties)
{
var propInfo = objType.GetProperty(kvp.Key);
object value = kvp.Value;
// 如果值是嵌套的字典类型,递归处理嵌套对象
if (value is Dictionary<string, object> nestedProperties)
{
// 创建嵌套对象
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
// 递归设置嵌套对象的值
SetPropertyValues(nestedObj, nestedProperties);
// 将嵌套对象赋值给属性
propInfo.SetValue(obj, nestedObj);
}
else
{
// 直接赋值给属性
propInfo.SetValue(obj, value);
}
}
}
// 方法 2: 递归设置对象的属性值(带验证)
public static bool SetPropertyValuesWithValidation(object obj, Dictionary<string, object> properties)
{
var objType = obj.GetType();
bool allSuccessful = true; // 标记是否所有属性赋值成功
foreach (var kvp in properties)
{
var propName = kvp.Key;
var propValue = kvp.Value;
var propInfo = objType.GetProperty(propName);
if (propInfo == null)
{
// 属性不存在,打印警告并标记失败
Console.WriteLine($"Warning: 属性 '{propName}' 不存在于类型 '{objType.Name}' 中,跳过赋值。");
allSuccessful = false;
continue;
}
// 检查属性类型是否与要赋的值兼容
var targetType = propInfo.PropertyType;
if (!IsCompatibleType(targetType, propValue))
{
// 如果类型不兼容,打印错误并标记失败
Console.WriteLine($"Error: 无法将类型 '{propValue?.GetType().Name}' 赋值给属性 '{propName}' (Type: {targetType.Name}),跳过赋值。");
allSuccessful = false;
continue;
}
try
{
// 如果值是一个嵌套对象,递归赋值
if (propValue is Dictionary<string, object> nestedProperties)
{
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
if (SetPropertyValuesWithValidation(nestedObj, nestedProperties))
{
propInfo.SetValue(obj, nestedObj);
}
else
{
allSuccessful = false; // 嵌套赋值失败
}
}
else if (propValue is IList<Dictionary<string, object>> list) // 处理列表
{
// 获取目标类型的数组元素类型
var elementType = propInfo.PropertyType.GetElementType();
var array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
var item = Activator.CreateInstance(elementType);
if (SetPropertyValuesWithValidation(item, list[i]))
{
array.SetValue(item, i);
}
else
{
allSuccessful = false; // 赋值失败
}
}
propInfo.SetValue(obj, array);
}
else
{
// 直接赋值
propInfo.SetValue(obj, propValue);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: 为属性 '{propName}' 赋值时发生异常:{ex.Message}");
allSuccessful = false;
}
}
return allSuccessful;
}
// 检查类型兼容性的方法(支持嵌套类型)
static bool IsCompatibleType(Type targetType, object value)
{
if (value == null)
{
// 如果值为null且目标类型是引用类型或者可空类型则兼容
return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
}
// 检查值的类型是否与目标类型相同,或是否可以转换为目标类型
if (targetType.IsAssignableFrom(value.GetType()))
{
return true;
}
// 处理数组类型
if (targetType.IsArray)
{
// 检查数组的元素类型与值的类型兼容
var elementType = targetType.GetElementType();
return value is IList<Dictionary<string, object>>; // 假设值是一个列表,具体处理逻辑在赋值时
}
// 处理嵌套类型的情况
if (value is Dictionary<string, object> && targetType.IsClass && !targetType.IsPrimitive)
{
// 如果目标类型是一个复杂对象,并且值是一个字典,可能是嵌套对象
return true; // 假设可以递归处理嵌套对象
}
return false;
}
}
}

View File

@@ -13,7 +13,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
try
{
if (string.IsNullOrEmpty(expression))
{
return false;
}
var parse = ConditionParse(data, expression);
var result = parse.Evaluate(data);
return result;
@@ -104,7 +107,8 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
// 如果不需要转为指定类型
memberPath = operatorStr;
targetObj = GetMemberValue(data, operatorStr);
targetObj = SerinExpressionEvaluator.Evaluate("@get " + operatorStr, data, out _);
//targetObj = GetMemberValue(data, operatorStr);
type = targetObj.GetType();
operatorStr = parts[1].ToLower(); //
valueStr = string.Join(' ', parts.Skip(2));
@@ -189,18 +193,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
}
//int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
//return new MemberConditionResolver<int>
//{
// TargetObj = targetObj,
// //MemberPath = memberPath,
// Op = ParseValueTypeOperator<int>(operatorStr),
// Value = value,
// ArithmeticExpression = GetArithmeticExpression(parts[0])
//};
}
#endregion
#region double

View File

@@ -121,40 +121,98 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <exception cref="ArgumentException"></exception>
private static object GetMember(object target, string memberPath)
{
// 分割成员路径,按 '.' 处理多级访问
var members = memberPath.Split('.');
foreach (var member in members)
{
if (target == null) return null;
if (target is null) return null;
var property = target.GetType().GetProperty(member);
if (property != null)
// 检查成员是否包含数组索引,例如 "cars[0]"
var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
// 解析数组/集合名与索引部分
var arrayName = member.Substring(0, arrayIndexStart);
var arrayIndexEnd = member.IndexOf(']');
if (arrayIndexEnd == -1 || arrayIndexEnd <= arrayIndexStart + 1)
{
throw new ArgumentException($"Invalid array syntax for member {member}");
}
target = field.GetValue(target);
// 提取数组索引
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
if (!int.TryParse(indexStr, out int index))
{
throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}");
}
// 获取数组或集合对象
var arrayProperty = target.GetType().GetProperty(arrayName);
if (arrayProperty != null)
{
target = arrayProperty.GetValue(target);
}
else
{
throw new ArgumentException($"Member {member} not found on target.");
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
{
throw new ArgumentException($"Member {arrayName} not found on target.");
}
}
// 访问数组或集合中的指定索引
if (target is Array array)
{
if (index < 0 || index >= array.Length)
{
throw new ArgumentException($"Index {index} out of bounds for array {arrayName}");
}
target = array.GetValue(index);
}
else if (target is IList<object> list)
{
if (index < 0 || index >= list.Count)
{
throw new ArgumentException($"Index {index} out of bounds for list {arrayName}");
}
target = list[index];
}
else
{
throw new ArgumentException($"Member {arrayName} is not an array or list.");
}
}
else
{
// 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member);
if (property != null)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
{
throw new ArgumentException($"Member {member} not found on target.");
}
}
}
}
return target;
}
/// <summary>
/// 设置目标的值
/// </summary>
@@ -178,26 +236,85 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
var member = members[i];
var property = target.GetType().GetProperty(member);
if (property != null)
// 检查是否包含数组索引
var arrayIndexStart = member.IndexOf('[');
if (arrayIndexStart != -1)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
// 解析数组名和索引
var arrayName = member.Substring(0, arrayIndexStart);
var arrayIndexEnd = member.IndexOf(']');
if (arrayIndexEnd == -1 || arrayIndexEnd <= arrayIndexStart + 1)
{
throw new ArgumentException($"Invalid array syntax for member {member}");
}
target = field.GetValue(target);
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
if (!int.TryParse(indexStr, out int index))
{
throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}");
}
// 获取数组或集合
var arrayProperty = target.GetType().GetProperty(arrayName);
if (arrayProperty != null)
{
target = arrayProperty.GetValue(target);
}
else
{
throw new ArgumentException($"Member {member} not found on target.");
var arrayField = target.GetType().GetField(arrayName);
if (arrayField != null)
{
target = arrayField.GetValue(target);
}
else
{
throw new ArgumentException($"Member {arrayName} not found on target.");
}
}
// 获取目标数组或集合中的指定元素
if (target is Array array)
{
if (index < 0 || index >= array.Length)
{
throw new ArgumentException($"Index {index} out of bounds for array {arrayName}");
}
target = array.GetValue(index);
}
else if (target is IList<object> list)
{
if (index < 0 || index >= list.Count)
{
throw new ArgumentException($"Index {index} out of bounds for list {arrayName}");
}
target = list[index];
}
else
{
throw new ArgumentException($"Member {arrayName} is not an array or list.");
}
}
else
{
// 处理非数组情况的属性或字段
var property = target.GetType().GetProperty(member);
if (property != null)
{
target = property.GetValue(target);
}
else
{
var field = target.GetType().GetField(member);
if (field != null)
{
target = field.GetValue(target);
}
else
{
throw new ArgumentException($"Member {member} not found on target.");
}
}
}
}
@@ -205,7 +322,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
var lastMember = members.Last();
var lastProperty = target.GetType().GetProperty(lastMember);
if (lastProperty != null)
{
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
@@ -227,7 +343,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
return target;
}
/// <summary>
/// 计算数学简单表达式
/// </summary>

View File

@@ -1,6 +1,7 @@
using Newtonsoft.Json;
using Serein.Library.Entity;
using Serein.NodeFlow;
using Serein.NodeFlow.Tool;
using System.Diagnostics;
using System.Windows;
@@ -35,6 +36,9 @@ namespace Serein.WorkBench
public App()
{
#if false //测试 操作表达式,条件表达式
#region
string expression = "";

View File

@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Serein.WorkBench"
mc:Ignorable="d"
Topmost="False"
Topmost="True"
Title="LogWindow" Height="600" Width="400"
Closing="Window_Closing">
<Grid>

View File

@@ -23,7 +23,11 @@
<KeyBinding Key="Escape" Command="{Binding CancelConnectionCommand}"/>
</Window.InputBindings>
<Grid>
<Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="3*"/>
@@ -31,7 +35,15 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0" Background="#F5F5F5">
<StackPanel Grid.Row="0" Grid.ColumnSpan="5" Background="#F5F5F5" Orientation="Horizontal" >
<Button x:Name="ButtonDebugRun" Content="运行" Width="100" Margin="10" Click="ButtonDebugRun_Click"></Button>
<Button x:Name="ButtonDebugFlipflopNode" Content="结束" Width="100" Margin="10" Click="ButtonDebugFlipflopNode_Click"></Button>
<Button x:Name="ButtonStartFlowInSelectNode" Content="从选定节点开始" Width="100" Margin="10" Click="ButtonStartFlowInSelectNode_Click"></Button>
<Button x:Name="ButtonResetCanvas" Content="重置画布" Width="100" Margin="10" Click="ButtonResetCanvas_Click"></Button>
<Button x:Name="ButtonTestExpObj" Content="测试对象表达式" Width="100" Margin="10" Click="ButtonTestExpObj_Click"></Button>
</StackPanel>
<DockPanel Grid.Row="1" Grid.Column="0" Background="#F5F5F5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@@ -64,31 +76,11 @@
</Grid>
</DockPanel>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<Grid Grid.Column="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background="#F5F5F5" Orientation="Horizontal" >
<Button x:Name="ButtonDebugRun" Content="运行" Width="100" Margin="10" Click="ButtonDebugRun_Click"></Button>
<Button x:Name="ButtonDebugFlipflopNode" Content="结束" Width="100" Margin="10" Click="ButtonDebugFlipflopNode_Click"></Button>
<Button x:Name="ButtonStartFlowInSelectNode" Content="从选定节点开始" Width="100" Margin="10" Click="ButtonStartFlowInSelectNode_Click"></Button>
<!--<Button x:Name="ButtonLoadStartNodeTree" Content="加载起始节点树" Width="100" Margin="10" Click="ButtonLoadStartNodeTree_Click"></Button>-->
<!--<Button x:Name="ButtonReflushCanvasConfig" Content="重置画布设置" Width="100" Margin="10" Click="ButtonReflushCanvasConfig_Click"></Button>-->
<!--<Button x:Name="ButtonLoadCanvasConfig" Content="加载画布设置" Width="100" Margin="10" Click="ButtonLoadCanvasConfig_Click"></Button>-->
</StackPanel>
<!--HorizontalAlignment="Center"
VerticalAlignment="Center"-->
<StackPanel Grid.Row="1"
x:Name="FlowChartStackPanel"
<Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid">
<StackPanel x:Name="FlowChartStackPanel"
ClipToBounds="True">
<Canvas
x:Name="FlowChartCanvas"
@@ -187,9 +179,9 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con
</StackPanel>
</Grid>
<GridSplitter Grid.Column="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<GridSplitter Grid.Row="1" Grid.Column="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<!--IOC容器属性-->
<Grid Grid.Column="4" >
<Grid Grid.Row="1" Grid.Column="4" >
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>

View File

@@ -7,6 +7,8 @@ using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Base;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool;
using Serein.NodeFlow.Tool.SereinExpression;
using Serein.WorkBench.Node;
using Serein.WorkBench.Node.View;
using Serein.WorkBench.Node.ViewModel;
@@ -14,6 +16,7 @@ using Serein.WorkBench.Themes;
using Serein.WorkBench.tool;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
@@ -189,7 +192,12 @@ namespace Serein.WorkBench
FlowEnvironment.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChanged;
FlowEnvironment.OnNodeLocate += FlowEnvironment_OnNodeLocate;
}
private void InitCanvasUI()
{
canvasTransformGroup = new TransformGroup();
@@ -200,7 +208,6 @@ namespace Serein.WorkBench
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
//FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
}
private LogWindow InitConsoleOut()
@@ -260,11 +267,6 @@ namespace Serein.WorkBench
/// <param name="eventArgs"></param>
private void FlowEnvironment_OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
{
//foreach(var connection in Connections)
//{
// connection.Refresh();
//}
//Console.WriteLine((FlowChartStackPanel.ActualWidth, FlowChartStackPanel.ActualHeight));
}
/// <summary>
@@ -319,8 +321,16 @@ namespace Serein.WorkBench
{
string fromNodeGuid = eventArgs.FromNodeGuid;
string toNodeGuid = eventArgs.ToNodeGuid;
NodeControlBase fromNode = GuidToControl(fromNodeGuid);
NodeControlBase toNode = GuidToControl(toNodeGuid);
NodeControlBase? fromNode = GuidToControl(fromNodeGuid);
NodeControlBase? toNode = GuidToControl(toNodeGuid);
if(fromNode != null && toNode != null)
{
}
else
{
return;
}
ConnectionType connectionType = eventArgs.ConnectionType;
Action? action = null;
@@ -387,7 +397,8 @@ namespace Serein.WorkBench
private void FlowEnvironment_NodeRemoteEvent(NodeRemoteEventArgs eventArgs)
{
var nodeGuid = eventArgs.NodeGuid;
NodeControlBase nodeControl = GuidToControl(nodeGuid);
NodeControlBase? nodeControl = GuidToControl(nodeGuid);
if (nodeControl is null) return;
if (selectNodeControls.Count > 0)
{
if (selectNodeControls.Contains(nodeControl))
@@ -489,11 +500,12 @@ namespace Serein.WorkBench
{
string oldNodeGuid = eventArgs.OldNodeGuid;
string newNodeGuid = eventArgs.NewNodeGuid;
NodeControlBase newStartNodeControl = GuidToControl(newNodeGuid);
NodeControlBase? newStartNodeControl = GuidToControl(newNodeGuid);
if (newStartNodeControl is null) return;
if (!string.IsNullOrEmpty(oldNodeGuid))
{
NodeControlBase oldStartNodeControl = GuidToControl(oldNodeGuid);
NodeControlBase? oldStartNodeControl = GuidToControl(oldNodeGuid);
if (oldStartNodeControl is null) return;
oldStartNodeControl.BorderBrush = Brushes.Black;
oldStartNodeControl.BorderThickness = new Thickness(0);
}
@@ -554,7 +566,8 @@ namespace Serein.WorkBench
private void FlowEnvironment_OnNodeInterruptStateChange(NodeInterruptStateChangeEventArgs eventArgs)
{
string nodeGuid = eventArgs.NodeGuid;
NodeControlBase nodeControl = GuidToControl(nodeGuid);
NodeControlBase? nodeControl = GuidToControl(nodeGuid);
if (nodeControl is null) return;
if (eventArgs.Class == InterruptClass.None)
{
nodeControl.ViewModel.IsInterrupt = false;
@@ -591,7 +604,7 @@ namespace Serein.WorkBench
private void FlowEnvironment_OnInterruptTrigger(InterruptTriggerEventArgs eventArgs)
{
string nodeGuid = eventArgs.NodeGuid;
NodeControlBase nodeControl = GuidToControl(nodeGuid);
NodeControlBase? nodeControl = GuidToControl(nodeGuid);
if (nodeControl is null) return;
if(eventArgs.Type == InterruptTriggerEventArgs.InterruptTriggerType.Exp)
{
@@ -613,24 +626,118 @@ namespace Serein.WorkBench
IOCObjectViewer.AddDependenciesInstance(eventArgs.Key, eventArgs.Instance);
}
/// <summary>
/// 节点需要定位
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_OnNodeLocate(NodeLocatedEventArgs eventArgs)
{
NodeControlBase? nodeControl = GuidToControl(eventArgs.NodeGuid);
if (nodeControl is null) return;
//scaleTransform.ScaleX = 1;
//scaleTransform.ScaleY = 1;
// 获取控件在 FlowChartCanvas 上的相对位置
Rect controlBounds = VisualTreeHelper.GetDescendantBounds(nodeControl);
Point controlPosition = nodeControl.TransformToAncestor(FlowChartCanvas).Transform(new Point(0, 0));
// 获取控件在画布上的中心点
double controlCenterX = controlPosition.X + controlBounds.Width / 2;
double controlCenterY = controlPosition.Y + controlBounds.Height / 2;
// 考虑缩放因素计算目标位置的中心点
double scaledCenterX = controlCenterX * scaleTransform.ScaleX;
double scaledCenterY = controlCenterY * scaleTransform.ScaleY;
//// 计算画布的可视区域大小
//double visibleAreaLeft = scaledCenterX;
//double visibleAreaTop = scaledCenterY;
//double visibleAreaRight = scaledCenterX + FlowChartStackGrid.ActualWidth;
//double visibleAreaBottom = scaledCenterY + FlowChartStackGrid.ActualHeight;
//// 检查控件中心点是否在可视区域内
//bool isInView = scaledCenterX >= visibleAreaLeft && scaledCenterX <= visibleAreaRight &&
// scaledCenterY >= visibleAreaTop && scaledCenterY <= visibleAreaBottom;
//Console.WriteLine($"isInView :{isInView}");
//if (!isInView)
//{
//}
// 计算平移偏移量,使得控件在可视区域的中心
double translateX = scaledCenterX - FlowChartStackGrid.ActualWidth / 2;
double translateY = scaledCenterY - FlowChartStackGrid.ActualHeight / 2;
var translate = this.translateTransform;
// 应用平移变换
translate.X = 0;
translate.Y = 0;
translate.X -= translateX;
translate.Y -= translateY;
// 设置RenderTransform以实现移动效果
TranslateTransform translateTransform = new TranslateTransform();
nodeControl.RenderTransform = translateTransform;
ElasticAnimation(nodeControl, translateTransform, 4,1,0.5);
}
/// <summary>
/// 控件抖动
/// 来源https://www.cnblogs.com/RedSky/p/17705411.html
/// 作者HotSky
/// (……太好用了)
/// </summary>
/// <param name="translate"></param>
/// <param name="power">抖动第一下偏移量</param>
/// <param name="range">减弱幅度小于等于power大于0</param>
/// <param name="speed">持续系数(大于0),越大时间越长,</param>
private static void ElasticAnimation(NodeControlBase? nodeControl, TranslateTransform translate, int power, int range = 1, double speed = 1)
{
DoubleAnimationUsingKeyFrames animation1 = new DoubleAnimationUsingKeyFrames();
for (int i = power, j = 1; i >= 0; i -= range)
{
animation1.KeyFrames.Add(new LinearDoubleKeyFrame(-i, TimeSpan.FromMilliseconds(j++ * 100 * speed)));
animation1.KeyFrames.Add(new LinearDoubleKeyFrame(i, TimeSpan.FromMilliseconds(j++ * 100 * speed)));
}
translate.BeginAnimation(TranslateTransform.YProperty, animation1);
DoubleAnimationUsingKeyFrames animation2 = new DoubleAnimationUsingKeyFrames();
for (int i = power, j = 1; i >= 0; i -= range)
{
animation2.KeyFrames.Add(new LinearDoubleKeyFrame(-i, TimeSpan.FromMilliseconds(j++ * 100 * speed)));
animation2.KeyFrames.Add(new LinearDoubleKeyFrame(i, TimeSpan.FromMilliseconds(j++ * 100 * speed)));
}
translate.BeginAnimation(TranslateTransform.XProperty, animation2);
animation2.Completed += (s, e) =>
{
nodeControl.RenderTransform = null; // 或者重新设置为默认值
};
}
/// <summary>
/// Guid 转 NodeControl
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
private NodeControlBase GuidToControl(string nodeGuid)
private NodeControlBase? GuidToControl(string nodeGuid)
{
if (string.IsNullOrEmpty(nodeGuid))
{
return null;
throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid));
}
if (!NodeControls.TryGetValue(nodeGuid, out NodeControlBase? nodeControl) || nodeControl is null)
{
return null;
throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid));
}
return nodeControl;
}
#endregion
#region
@@ -800,6 +907,7 @@ namespace Serein.WorkBench
#endregion
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
@@ -948,7 +1056,7 @@ namespace Serein.WorkBench
#endregion
#region
#region
/// <summary>
/// 鼠标在画布移动。
@@ -1138,7 +1246,7 @@ namespace Serein.WorkBench
if(sender is NodeControlBase nodeControl)
{
ChangeViewerObjOfNode(nodeControl);
if (nodeControl.ViewModel.Node.MethodDetails.IsProtectionParameter) return;
if (nodeControl?.ViewModel?.Node?.MethodDetails?.IsProtectionParameter == true) return;
IsControlDragging = true;
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
((UIElement)sender).CaptureMouse(); // 捕获鼠标
@@ -1168,7 +1276,7 @@ namespace Serein.WorkBench
// 获取element控件的旧位置
double oldLeft = Canvas.GetLeft(element);
double oldTop = Canvas.GetTop(element);
// 计算被选择控件的偏移量
double deltaX = (int)(currentPosition.X - startControlDragPoint.X);
double deltaY = (int)(currentPosition.Y - startControlDragPoint.Y);
@@ -1221,12 +1329,12 @@ namespace Serein.WorkBench
{
return;
}
double deltaX = currentPosition.X - startControlDragPoint.X; // 计算X轴方向的偏移量
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(block) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(block) + deltaY; // 新的上边距
//Console.WriteLine((Canvas.GetLeft(block), Canvas.GetTop(block)));
// 限制控件不超出FlowChartCanvas的边界
if (newLeft >= 0 && newLeft + block.ActualWidth <= FlowChartCanvas.ActualWidth)
@@ -1245,9 +1353,9 @@ namespace Serein.WorkBench
}
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
{
var node = nodeControl?.ViewModel?.Node;
if (node is not null && node.MethodDetails.ReturnType != typeof(void))
//if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void))
if (node is not null && node?.MethodDetails?.ReturnType != typeof(void))
{
var key = node.Guid;
var instance = node.GetFlowData();
@@ -1421,8 +1529,6 @@ namespace Serein.WorkBench
}
#endregion
#region
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
@@ -1446,19 +1552,19 @@ namespace Serein.WorkBench
{
// if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
if (e.Delta < 0 && scaleTransform.ScaleX < 0.2) return;
if (e.Delta < 0 && scaleTransform.ScaleX < 0.05) return;
if (e.Delta > 0 && scaleTransform.ScaleY > 1.5) return;
// 获取鼠标在 Canvas 内的相对位置
var mousePosition = e.GetPosition(FlowChartCanvas);
// 缩放因子,根据滚轮方向调整
double zoomFactor = e.Delta > 0 ? 0.1 : -0.1;
//double zoomFactor = e.Delta > 0 ? 1.1 : 0.9;
//double zoomFactor = e.Delta > 0 ? 0.1 : -0.1;
double zoomFactor = e.Delta > 0 ? 1.1 : 0.9;
// 当前缩放比例
double oldScale = scaleTransform.ScaleX;
// double newScale = oldScale * zoomFactor;
double newScale = oldScale + zoomFactor;
double newScale = oldScale * zoomFactor;
//double newScale = oldScale + zoomFactor;
// 更新缩放比例
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
@@ -1608,6 +1714,8 @@ namespace Serein.WorkBench
#endregion
#endregion
#endregion
#region
@@ -1706,7 +1814,6 @@ namespace Serein.WorkBench
}
e.Handled = true; // 防止事件传播影响其他控件
}
/// <summary>
@@ -2256,6 +2363,8 @@ namespace Serein.WorkBench
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
{
FlowEnvironment.ClearAll();
}
/// <summary>
/// 卸载DLL文件清空当前项目
@@ -2450,6 +2559,70 @@ namespace Serein.WorkBench
}
}
/// <summary>
/// 对象装箱测试
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonTestExpObj_Click(object sender, RoutedEventArgs e)
{
string jsonString =
"""
{
"Name": "张三",
"Age": 24,
"Address": {
"City": "北京",
"PostalCode": "10000"
}
}
""";
var externalData = new Dictionary<string, object>
{
{ "Name", "John" },
{ "Age", 30 },
{ "Addresses", new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "Street", "123 Main St" },
{ "City", "New York" }
},
new Dictionary<string, object>
{
{ "Street", "456 Another St" },
{ "City", "Los Angeles" }
}
}
}
};
if (!ObjDynamicCreateHelper.TryResolve(externalData, "RootType",out var result))
{
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
}
ObjDynamicCreateHelper.PrintObjectProperties(result);
Console.WriteLine( );
var exp = "@set .Addresses[1].Street = qwq";
var data = SerinExpressionEvaluator.Evaluate(exp, result, out bool isChange);
exp = "@get .Addresses[1].Street";
data = SerinExpressionEvaluator.Evaluate(exp,result, out isChange);
Console.WriteLine($"{exp} => {data}");
}
/// <summary>
/// 重置画布
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonResetCanvas_Click(object sender, RoutedEventArgs e)
{
translateTransform.X = 0;
translateTransform.Y = 0;
}
}
#region UI层面上显示为 线

View File

@@ -34,7 +34,7 @@ namespace Serein.WorkBench.Node.ViewModel
set
{
isSelect = value;
// OnPropertyChanged();
OnPropertyChanged();
}
}

View File

@@ -5,8 +5,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
d:DesignHeight="400" d:DesignWidth="200">
<UserControl.Resources>
<Style x:Key="CustomTreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
@@ -21,7 +28,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Width="1" x:Name="UpstreamTreeRectangle" Grid.Row="0" Fill="#4A82E4" Margin="4,1,4,1" IsHitTestVisible="False"/>
<TreeView Grid.Column="1" x:Name="UpstreamTreeNodes" BorderThickness="0"/>
<TreeView Grid.Column="1" x:Name="UpstreamTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
</Grid>
<Grid Grid.Row="1" x:Name="IsSucceedTreeGuid" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
@@ -29,7 +36,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Width="1" x:Name="IsSucceedRectangle" Grid.Row="0" Fill="#04FC10" Margin="4,1,4,1" IsHitTestVisible="False"/>
<TreeView Grid.Column="1" x:Name="IsSucceedTreeNodes" BorderThickness="0"/>
<TreeView Grid.Column="1" x:Name="IsSucceedTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
</Grid>
<Grid Grid.Row="2" x:Name="IsFailTreeGuid" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
@@ -38,7 +45,7 @@
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Width="1" x:Name="IsFailRectangle" Grid.Row="0" Fill="#F18905" Margin="4,1,4,1" IsHitTestVisible="False"/>
<TreeView Grid.Column="1" x:Name="IsFailTreeNodes" BorderThickness="0"/>
<TreeView Grid.Column="1" x:Name="IsFailTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
</Grid>
<Grid Grid.Row="3" x:Name="IsErrorTreeGuid" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
@@ -46,7 +53,7 @@
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Width="1" x:Name="IsErrorRectangle" Grid.Row="0" Fill="#FE1343" Margin="4,1,4,1" IsHitTestVisible="False"/>
<TreeView Grid.Column="1" x:Name="IsErrorTreeNodes" BorderThickness="0"/>
<TreeView Grid.Column="1" x:Name="IsErrorTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
</Grid>
</Grid>
</UserControl>

View File

@@ -74,9 +74,14 @@ namespace Serein.WorkBench.Themes
{ConnectionType.IsError, []},
}
};
string itemName = rootNodeModel?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = rootNodeModel.ControlType.ToString();
}
var rootNode = new TreeViewItem
{
Header = rootNodeModel.MethodDetails.MethodTips,
Header = itemName,
Tag = nodeTreeModel,
};
LoadNodeItem(this, nodeTreeModel);
@@ -136,19 +141,27 @@ namespace Serein.WorkBench.Themes
RootNode = child,
ChildNodes = child.SuccessorNodes,
};
string itemName = child?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = child.ControlType.ToString();
}
TreeViewItem treeViewItem = new TreeViewItem
{
Header = child.MethodDetails.MethodTips,
Header = itemName,
Tag = tmpNodeTreeModel
};
treeViewItem.Expanded += TreeViewItem_Expanded;
ContextMenu contextMenu = new ContextMenu();
var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", (s, e) =>
{
flowEnvironment.StartFlowInSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid);
}));
treeViewItem.ContextMenu = contextMenu;
contextMenu.Items.Add(MainWindow.CreateMenuItem("定位", (s, e) => flowEnvironment.NodeLocated(tmpNodeTreeModel.RootNode.Guid)));
treeViewItem.ContextMenu = contextMenu;
treeViewItem.Margin = new Thickness(-20, 0, 0, 0);
treeViewer.Items.Add(treeViewItem);
}
guid.Visibility = Visibility.Visible;
@@ -190,12 +203,18 @@ namespace Serein.WorkBench.Themes
RootNode = childNodeModel,
ChildNodes = childNodeModel.SuccessorNodes,
};
string itemName = childNodeModel?.MethodDetails?.MethodTips;
if (string.IsNullOrEmpty(itemName))
{
itemName = childNodeModel.ControlType.ToString();
}
TreeViewItem treeViewItem = new TreeViewItem
{
Header = childNodeModel.MethodDetails.MethodTips,
Header = itemName,
Tag = tempNodeTreeModel
};
treeViewItem.Margin = new Thickness(-15, 0, 0, 0);
treeViewItem.Margin = new Thickness(-20, 0, 0, 0);
treeViewItem.Visibility = Visibility.Visible;
treeView.Items.Add(treeViewItem);
}