mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-19 16:06:33 +08:00
示例工程版本提升至net462,项目添加了部分空引用检测逻辑。累了,消不完的空引用警告(T.T)
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Serein.Library.Framework</RootNamespace>
|
||||
<AssemblyName>Serein.Library.Framework</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -7,196 +7,193 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程中断管理
|
||||
/// </summary>
|
||||
public class ChannelFlowInterrupt
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 中断取消类型
|
||||
/// </summary>
|
||||
public enum CancelType
|
||||
{
|
||||
Manual,
|
||||
Error,
|
||||
Overtime
|
||||
}
|
||||
|
||||
// 使用并发字典管理每个信号对应的 Channel
|
||||
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="outTime">超时时间</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
|
||||
{
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
// 异步任务:超时后自动触发信号
|
||||
_ = Task.Run(async () =>
|
||||
/// <summary>
|
||||
/// 流程中断管理
|
||||
/// </summary>
|
||||
public class ChannelFlowInterrupt
|
||||
{
|
||||
try
|
||||
/// <summary>
|
||||
/// 中断取消类型
|
||||
/// </summary>
|
||||
public enum CancelType
|
||||
{
|
||||
await Task.Delay(outTime, cts.Token);
|
||||
if (!cts.Token.IsCancellationRequested)
|
||||
Manual,
|
||||
Error,
|
||||
Overtime
|
||||
}
|
||||
|
||||
// 使用并发字典管理每个信号对应的 Channel
|
||||
private readonly ConcurrentDictionary<string, Channel<CancelType>> _channels = new ConcurrentDictionary<string, Channel<CancelType>>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建信号并指定超时时间,到期后自动触发(异步方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="outTime">超时时间</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<CancelType> GetCreateChannelWithTimeoutAsync(string signal, TimeSpan outTime)
|
||||
{
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
// 异步任务:超时后自动触发信号
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await channel.Writer.WriteAsync(CancelType.Overtime);
|
||||
try
|
||||
{
|
||||
await Task.Delay(outTime, cts.Token);
|
||||
if (!cts.Token.IsCancellationRequested)
|
||||
{
|
||||
await channel.Writer.WriteAsync(CancelType.Overtime);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 超时任务被取消
|
||||
}
|
||||
finally
|
||||
{
|
||||
cts?.Dispose();
|
||||
}
|
||||
}, cts.Token);
|
||||
|
||||
// 等待信号传入(超时或手动触发)
|
||||
try
|
||||
{
|
||||
var result = await channel.Reader.ReadAsync();
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return CancelType.Error;
|
||||
}
|
||||
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 超时任务被取消
|
||||
}
|
||||
finally
|
||||
{
|
||||
cts?.Dispose();
|
||||
}
|
||||
}, cts.Token);
|
||||
|
||||
// 等待信号传入(超时或手动触发)
|
||||
try
|
||||
{
|
||||
var result = await channel.Reader.ReadAsync();
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return CancelType.Error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 创建信号,直到手动触发(异步方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="outTime">超时时间</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<CancelType> GetOrCreateChannelAsync(string signal)
|
||||
{
|
||||
try
|
||||
{
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
// 等待信号传入(超时或手动触发)
|
||||
var result = await channel.Reader.ReadAsync();
|
||||
return result;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return CancelType.Manual;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="timeout">超时时间</param>
|
||||
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
|
||||
{
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
var cts = new CancellationTokenSource();
|
||||
CancellationToken token = cts.Token;
|
||||
|
||||
// 异步任务:超时后自动触发信号
|
||||
_ = Task.Run(async () =>
|
||||
/// <summary>
|
||||
/// 创建信号,直到手动触发(异步方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="outTime">超时时间</param>
|
||||
/// <returns>等待任务</returns>
|
||||
public async Task<CancelType> GetOrCreateChannelAsync(string signal)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(timeout, token);
|
||||
await channel.Writer.WriteAsync(CancelType.Overtime);
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
// 等待信号传入(超时或手动触发)
|
||||
var result = await channel.Reader.ReadAsync();
|
||||
return result;
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
catch
|
||||
{
|
||||
// 任务被取消
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
return CancelType.Manual;
|
||||
}
|
||||
});
|
||||
|
||||
// 同步阻塞直到信号触发或超时
|
||||
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发信号
|
||||
/// </summary>
|
||||
/// <param name="signal">信号字符串</param>
|
||||
/// <returns>是否成功触发</returns>
|
||||
public bool TriggerSignal(string signal)
|
||||
{
|
||||
//if (_channels.TryGetValue(signal, out var channel))
|
||||
//{
|
||||
// // 手动触发信号
|
||||
// channel.Writer.TryWrite(CancelType.Manual);
|
||||
// return true;
|
||||
//}
|
||||
//return false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (_channels.TryGetValue(signal, out var channel))
|
||||
{
|
||||
// 手动触发信号
|
||||
channel.Writer.TryWrite(CancelType.Manual);
|
||||
|
||||
// 完成写入,标记该信号通道不再接受新写入
|
||||
channel.Writer.Complete();
|
||||
|
||||
// 触发后移除信号
|
||||
_channels.TryRemove(signal, out _);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消所有任务
|
||||
/// </summary>
|
||||
public void CancelAllTasks()
|
||||
{
|
||||
foreach (var channel in _channels.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel.Writer.Complete();
|
||||
}
|
||||
finally
|
||||
/// <summary>
|
||||
/// 创建信号并指定超时时间,到期后自动触发(同步阻塞方法)
|
||||
/// </summary>
|
||||
/// <param name="signal">信号标识符</param>
|
||||
/// <param name="timeout">超时时间</param>
|
||||
public CancelType CreateChannelWithTimeoutSync(string signal, TimeSpan timeout)
|
||||
{
|
||||
var channel = GetOrCreateChannel(signal);
|
||||
var cts = new CancellationTokenSource();
|
||||
CancellationToken token = cts.Token;
|
||||
|
||||
// 异步任务:超时后自动触发信号
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(timeout, token);
|
||||
await channel.Writer.WriteAsync(CancelType.Overtime);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// 任务被取消
|
||||
await Console.Out.WriteLineAsync(ex.Message);
|
||||
}
|
||||
});
|
||||
|
||||
// 同步阻塞直到信号触发或超时
|
||||
var result = channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();
|
||||
return result;
|
||||
|
||||
}
|
||||
}
|
||||
_channels.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或创建指定信号的 Channel
|
||||
/// </summary>
|
||||
/// <param name="signal">信号字符串</param>
|
||||
/// <returns>对应的 Channel</returns>
|
||||
private Channel<CancelType> GetOrCreateChannel(string signal)
|
||||
{
|
||||
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>());
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 触发信号
|
||||
/// </summary>
|
||||
/// <param name="signal">信号字符串</param>
|
||||
/// <returns>是否成功触发</returns>
|
||||
public bool TriggerSignal(string signal)
|
||||
{
|
||||
//if (_channels.TryGetValue(signal, out var channel))
|
||||
//{
|
||||
// // 手动触发信号
|
||||
// channel.Writer.TryWrite(CancelType.Manual);
|
||||
// return true;
|
||||
//}
|
||||
//return false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (_channels.TryGetValue(signal, out var channel))
|
||||
{
|
||||
// 手动触发信号
|
||||
channel.Writer.TryWrite(CancelType.Manual);
|
||||
|
||||
// 完成写入,标记该信号通道不再接受新写入
|
||||
channel.Writer.Complete();
|
||||
|
||||
// 触发后移除信号
|
||||
_channels.TryRemove(signal, out _);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消所有任务
|
||||
/// </summary>
|
||||
public void CancelAllTasks()
|
||||
{
|
||||
foreach (var channel in _channels.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
channel.Writer.Complete();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
_channels.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或创建指定信号的 Channel
|
||||
/// </summary>
|
||||
/// <param name="signal">信号字符串</param>
|
||||
/// <returns>对应的 Channel</returns>
|
||||
private Channel<CancelType> GetOrCreateChannel(string signal)
|
||||
{
|
||||
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<CancelType>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -345,6 +345,7 @@ namespace Serein.Library.Web
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return value;
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Net461DllTest.LogicControl;
|
||||
using Net462DllTest.LogicControl;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.NodeFlow.Tool;
|
||||
using System;
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.Device
|
||||
namespace Net462DllTest.Device
|
||||
{
|
||||
[AutoRegister]
|
||||
public class PrakingDevice : ChannelFlowTrigger<ParkingCommand>
|
||||
@@ -1,13 +1,13 @@
|
||||
using IoTClient;
|
||||
using IoTClient.Clients.PLC;
|
||||
using IoTClient.Enums;
|
||||
using Net461DllTest.Enums;
|
||||
using Net461DllTest.Signal;
|
||||
using Net462DllTest.Enums;
|
||||
using Net462DllTest.Signal;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.NodeFlow.Tool;
|
||||
using System;
|
||||
|
||||
namespace Net461DllTest.Device
|
||||
namespace Net462DllTest.Device
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -1,4 +1,4 @@
|
||||
using Net461DllTest.View;
|
||||
using Net462DllTest.View;
|
||||
using Serein.Library.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.Signal
|
||||
namespace Net462DllTest.Signal
|
||||
{
|
||||
public enum FromValue
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.Enums
|
||||
namespace Net462DllTest.Enums
|
||||
{
|
||||
public enum PlcState
|
||||
{
|
||||
@@ -1,12 +1,12 @@
|
||||
using Net461DllTest.Signal;
|
||||
using Net462DllTest.Signal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static Net461DllTest.Signal.PlcValueAttribute;
|
||||
using static Net462DllTest.Signal.PlcValueAttribute;
|
||||
|
||||
namespace Net461DllTest.Enums
|
||||
namespace Net462DllTest.Enums
|
||||
{
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Signal;
|
||||
using Net461DllTest.ViewModel;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Signal;
|
||||
using Net462DllTest.ViewModel;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Enums;
|
||||
@@ -13,7 +13,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.LogicControl
|
||||
namespace Net462DllTest.LogicControl
|
||||
{
|
||||
|
||||
public enum ParkingCommand
|
||||
@@ -1,9 +1,9 @@
|
||||
using IoTClient.Clients.PLC;
|
||||
using IoTClient.Common.Enums;
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Enums;
|
||||
using Net461DllTest.Signal;
|
||||
using Net461DllTest.Web;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Enums;
|
||||
using Net462DllTest.Signal;
|
||||
using Net462DllTest.Web;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Enums;
|
||||
@@ -18,7 +18,7 @@ using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.LogicControl
|
||||
namespace Net462DllTest.LogicControl
|
||||
{
|
||||
[AutoRegister]
|
||||
[DynamicFlow]
|
||||
@@ -1,6 +1,6 @@
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Signal;
|
||||
using Net461DllTest.ViewModel;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Signal;
|
||||
using Net462DllTest.ViewModel;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Enums;
|
||||
@@ -10,7 +10,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Net461DllTest.LogicControl
|
||||
namespace Net462DllTest.LogicControl
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -6,9 +6,9 @@
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E40EE629-1A38-4011-88E3-9AD036869987}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Net461DllTest</RootNamespace>
|
||||
<AssemblyName>Net461DllTest</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<RootNamespace>Net462DllTest</RootNamespace>
|
||||
<AssemblyName>Net462DllTest</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Net461DllTest.Signal
|
||||
namespace Net462DllTest.Signal
|
||||
{
|
||||
public enum CommandSignal
|
||||
{
|
||||
@@ -1,8 +1,8 @@
|
||||
using Serein.Library.Attributes;
|
||||
using System;
|
||||
using static Net461DllTest.Signal.PlcValueAttribute;
|
||||
using static Net462DllTest.Signal.PlcValueAttribute;
|
||||
|
||||
namespace Net461DllTest.Signal
|
||||
namespace Net462DllTest.Signal
|
||||
{
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
@@ -1,7 +1,7 @@
|
||||
using IoTClient;
|
||||
using IoTClient.Clients.PLC;
|
||||
using IoTClient.Enums;
|
||||
using Net461DllTest.Signal;
|
||||
using Net462DllTest.Signal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.Utils
|
||||
namespace Net462DllTest.Utils
|
||||
{
|
||||
internal static class MyPlcExtension
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Net461DllTest
|
||||
namespace Net462DllTest
|
||||
{
|
||||
partial class FromWorkBenchView
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Signal;
|
||||
using Net461DllTest.ViewModel;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Signal;
|
||||
using Net462DllTest.ViewModel;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Attributes;
|
||||
using System;
|
||||
@@ -13,7 +13,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Net461DllTest
|
||||
namespace Net462DllTest
|
||||
{
|
||||
public partial class FromWorkBenchView : Form
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Net461DllTest.View
|
||||
namespace Net462DllTest.View
|
||||
{
|
||||
partial class TestFormView
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Net461DllTest.View
|
||||
namespace Net462DllTest.View
|
||||
{
|
||||
public partial class TestFormView : Form
|
||||
{
|
||||
@@ -1,5 +1,5 @@
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Signal;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Signal;
|
||||
using Serein.Library.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.ViewModel
|
||||
namespace Net462DllTest.ViewModel
|
||||
{
|
||||
public class FromWorkBenchViewModel
|
||||
{
|
||||
@@ -1,5 +1,5 @@
|
||||
using Net461DllTest.Device;
|
||||
using Net461DllTest.Signal;
|
||||
using Net462DllTest.Device;
|
||||
using Net462DllTest.Signal;
|
||||
using Serein.Library.Attributes;
|
||||
using Serein.Library.Web;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Net461DllTest.Web
|
||||
namespace Net462DllTest.Web
|
||||
{
|
||||
[AutoHosting]
|
||||
public class CommandController : ControllerBase
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
<assemblyIdentity name="System.IO.Ports" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
|
||||
@@ -41,12 +41,12 @@ namespace Serein.NodeFlow.Base
|
||||
/// <summary>
|
||||
/// 节点guid
|
||||
/// </summary>
|
||||
public string Guid { get; set; }
|
||||
public string Guid { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 显示名称
|
||||
/// </summary>
|
||||
public string DisplayName { get; set; }
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为起点控件
|
||||
@@ -76,7 +76,7 @@ namespace Serein.NodeFlow.Base
|
||||
/// <summary>
|
||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
/// </summary>
|
||||
public Exception RuningException { get; set; } = null;
|
||||
public Exception? RuningException { get; set; } = null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -74,17 +74,17 @@ namespace Serein.NodeFlow.Base
|
||||
|
||||
internal virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||
{
|
||||
var node = this;
|
||||
if (node != null)
|
||||
this.Guid = nodeInfo.Guid;
|
||||
if(this.MethodDetails is not null)
|
||||
{
|
||||
node.Guid = nodeInfo.Guid;
|
||||
for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
|
||||
{
|
||||
Parameterdata? pd = nodeInfo.ParameterData[i];
|
||||
node.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
|
||||
node.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
|
||||
this.MethodDetails.ExplicitDatas[i].IsExplicitData = pd.State;
|
||||
this.MethodDetails.ExplicitDatas[i].DataValue = pd.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Serein.NodeFlow.Base
|
||||
if (upstreamNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
|
||||
{
|
||||
var cancelType = await upstreamNodes[i].DebugSetting.GetInterruptTask();
|
||||
await Console.Out.WriteLineAsync($"[{upstreamNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
await Console.Out.WriteLineAsync($"[{upstreamNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
upstreamNodes[i].PreviousNode = currentNode;
|
||||
await upstreamNodes[i].StartExecute(context); // 执行流程节点的上游分支
|
||||
@@ -165,7 +165,7 @@ namespace Serein.NodeFlow.Base
|
||||
if (nextNodes[i].DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
|
||||
{
|
||||
var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask();
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i]?.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
nextNodes[i].PreviousNode = currentNode;
|
||||
stack.Push(nextNodes[i]);
|
||||
@@ -194,18 +194,22 @@ namespace Serein.NodeFlow.Base
|
||||
// this.NextOrientation = ConnectionType.None;
|
||||
// return null;
|
||||
//}
|
||||
await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
await Console.Out.WriteLineAsync($"[{this.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
MethodDetails md = MethodDetails;
|
||||
MethodDetails? md = MethodDetails;
|
||||
//var del = md.MethodDelegate.Clone();
|
||||
if (md is null)
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
}
|
||||
if (!context.Env.TryGetDelegate(md.MethodName, out var del))
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
md.ActingInstance ??= context.Env.IOC.Get(MethodDetails.ActingInstanceType);
|
||||
md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||
object instance = md.ActingInstance;
|
||||
|
||||
var haveParameter = md.ExplicitDatas.Length > 0;
|
||||
@@ -281,7 +285,7 @@ namespace Serein.NodeFlow.Base
|
||||
|
||||
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
|
||||
{
|
||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
|
||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && flowData is not null)
|
||||
{
|
||||
// 执行表达式从上一节点获取对象
|
||||
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
|
||||
@@ -315,8 +319,6 @@ namespace Serein.NodeFlow.Base
|
||||
}
|
||||
}
|
||||
|
||||
//var attribute = ed.DataType.GetCustomAttribute<EnumTypeConvertorAttribute>();
|
||||
//if (attribute is not null && attribute.EnumType.IsEnum) // 获取枚举转换器中记录的枚举
|
||||
if ( ed.DataType != ed.ExplicitType) // 获取枚举转换器中记录的枚举
|
||||
{
|
||||
if (ed.ExplicitType.IsEnum && Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum)) // 获取对应的枚举项
|
||||
@@ -339,41 +341,43 @@ namespace Serein.NodeFlow.Base
|
||||
|
||||
try
|
||||
{
|
||||
string? valueStr = inputParameter?.ToString();
|
||||
parameters[i] = ed.DataType switch
|
||||
{
|
||||
//Type t when t == previousDataType => inputParameter, // 上下文
|
||||
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
|
||||
Type t when t == typeof(IDynamicContext) => context, // 上下文
|
||||
Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举
|
||||
Type t when t == typeof(string) => inputParameter?.ToString(),
|
||||
Type t when t == typeof(char) && !string.IsNullOrEmpty(valueStr) => char.Parse(valueStr),
|
||||
Type t when t == typeof(bool) && !string.IsNullOrEmpty(valueStr) => inputParameter is not null && bool.Parse(valueStr),
|
||||
Type t when t == typeof(float) && !string.IsNullOrEmpty(valueStr) => float.Parse(valueStr),
|
||||
Type t when t == typeof(decimal) && !string.IsNullOrEmpty(valueStr) => decimal.Parse(valueStr),
|
||||
Type t when t == typeof(double) && !string.IsNullOrEmpty(valueStr) => double.Parse(valueStr),
|
||||
Type t when t == typeof(sbyte) && !string.IsNullOrEmpty(valueStr) => sbyte.Parse(valueStr),
|
||||
Type t when t == typeof(byte) && !string.IsNullOrEmpty(valueStr) => byte.Parse(valueStr),
|
||||
Type t when t == typeof(short) && !string.IsNullOrEmpty(valueStr) => short.Parse(valueStr),
|
||||
Type t when t == typeof(ushort) && !string.IsNullOrEmpty(valueStr) => ushort.Parse(valueStr),
|
||||
Type t when t == typeof(int) && !string.IsNullOrEmpty(valueStr) => int.Parse(valueStr),
|
||||
Type t when t == typeof(uint) && !string.IsNullOrEmpty(valueStr) => uint.Parse(valueStr),
|
||||
Type t when t == typeof(long) && !string.IsNullOrEmpty(valueStr) => long.Parse(valueStr),
|
||||
Type t when t == typeof(ulong) && !string.IsNullOrEmpty(valueStr) => ulong.Parse(valueStr),
|
||||
Type t when t == typeof(nint) && !string.IsNullOrEmpty(valueStr) => nint.Parse(valueStr),
|
||||
Type t when t == typeof(nuint) && !string.IsNullOrEmpty(valueStr) => nuint.Parse(valueStr),
|
||||
//Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? 0 : DateTime.Parse(valueStr),
|
||||
|
||||
Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
||||
Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
||||
Type t when t == typeof(Guid) => new Guid(inputParameter?.ToString()),
|
||||
Type t when t == typeof(DateTime) => DateTime.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(string) => inputParameter?.ToString(),
|
||||
Type t when t == typeof(char) => char.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(bool) => inputParameter is null ? false : bool.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(float) => inputParameter is null ? 0F : float.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(decimal) => inputParameter is null ? 0 : decimal.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(double) => inputParameter is null ? 0 : double.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(sbyte) => inputParameter is null ? 0 : sbyte.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(byte) => inputParameter is null ? 0 : byte.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(short) => inputParameter is null ? 0 : short.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(ushort) => inputParameter is null ? 0U : ushort.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(int) => inputParameter is null ? 0 : int.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(uint) => inputParameter is null ? 0U : uint.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(long) => inputParameter is null ? 0L : long.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(ulong) => inputParameter is null ? 0UL : ulong.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(nint) => inputParameter is null ? 0 : nint.Parse(inputParameter?.ToString()),
|
||||
Type t when t == typeof(nuint) => inputParameter is null ? 0 : nuint.Parse(inputParameter?.ToString()),
|
||||
|
||||
Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
||||
Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
||||
Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
||||
_ => inputParameter,
|
||||
// Type t when Nullable.GetUnderlyingType(t) != null => inputParameter is null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)),
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex) // 节点参数类型转换异常
|
||||
{
|
||||
parameters[i] = null;
|
||||
parameters[i] = new object();
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
@@ -395,21 +399,25 @@ namespace Serein.NodeFlow.Base
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type)
|
||||
private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? data, int monitorType)
|
||||
{
|
||||
MonitorObjectEventArgs.ObjSourceType sourceType;
|
||||
string key;
|
||||
if(type == 0)
|
||||
string? key;
|
||||
if(monitorType == 0)
|
||||
{
|
||||
key = data.GetType().FullName;
|
||||
key = data?.GetType()?.FullName;
|
||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = nodeModel.Guid;
|
||||
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
|
||||
|
||||
}
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Env.CheckObjMonitorState(key, out List<string> exps)) // 如果新的数据处于查看状态,通知UI进行更新?交给运行环境判断?
|
||||
{
|
||||
context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态,通知UI更新数据显示
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Serein.NodeFlow
|
||||
public FlowEnvironment()
|
||||
{
|
||||
sereinIOC = new SereinIOC();
|
||||
//ChannelFlowInterrupt = new ChannelFlowInterrupt();
|
||||
ChannelFlowInterrupt = new ChannelFlowInterrupt();
|
||||
//LoadedAssemblyPaths = new List<string>();
|
||||
//LoadedAssemblies = new List<Assembly>();
|
||||
//MethodDetailss = new List<MethodDetails>();
|
||||
@@ -69,67 +69,67 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 加载Dll
|
||||
/// </summary>
|
||||
public event LoadDllHandler OnDllLoad;
|
||||
public event LoadDllHandler? OnDllLoad;
|
||||
|
||||
/// <summary>
|
||||
/// 移除DLL
|
||||
/// </summary>
|
||||
public event RemoteDllHandler OnDllRemote;
|
||||
public event RemoteDllHandler? OnDllRemote;
|
||||
|
||||
/// <summary>
|
||||
/// 项目加载完成
|
||||
/// </summary>
|
||||
public event ProjectLoadedHandler OnProjectLoaded;
|
||||
public event ProjectLoadedHandler? OnProjectLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// 节点连接属性改变事件
|
||||
/// </summary>
|
||||
public event NodeConnectChangeHandler OnNodeConnectChange;
|
||||
public event NodeConnectChangeHandler? OnNodeConnectChange;
|
||||
|
||||
/// <summary>
|
||||
/// 节点创建事件
|
||||
/// </summary>
|
||||
public event NodeCreateHandler OnNodeCreate;
|
||||
public event NodeCreateHandler? OnNodeCreate;
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点事件
|
||||
/// </summary>
|
||||
public event NodeRemoteHandler OnNodeRemote;
|
||||
public event NodeRemoteHandler? OnNodeRemote;
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点变化事件
|
||||
/// </summary>
|
||||
public event StartNodeChangeHandler OnStartNodeChange;
|
||||
public event StartNodeChangeHandler? OnStartNodeChange;
|
||||
|
||||
/// <summary>
|
||||
/// 流程运行完成事件
|
||||
/// </summary>
|
||||
public event FlowRunCompleteHandler OnFlowRunComplete;
|
||||
public event FlowRunCompleteHandler? OnFlowRunComplete;
|
||||
|
||||
/// <summary>
|
||||
/// 被监视的对象改变事件
|
||||
/// </summary>
|
||||
public event MonitorObjectChangeHandler OnMonitorObjectChange;
|
||||
public event MonitorObjectChangeHandler? OnMonitorObjectChange;
|
||||
|
||||
/// <summary>
|
||||
/// 节点中断状态改变事件
|
||||
/// </summary>
|
||||
public event NodeInterruptStateChangeHandler OnNodeInterruptStateChange;
|
||||
public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange;
|
||||
|
||||
/// <summary>
|
||||
/// 节点触发了中断
|
||||
/// </summary>
|
||||
public event ExpInterruptTriggerHandler OnInterruptTrigger;
|
||||
public event ExpInterruptTriggerHandler? OnInterruptTrigger;
|
||||
|
||||
/// <summary>
|
||||
/// 容器改变
|
||||
/// </summary>
|
||||
public event IOCMembersChangedHandler OnIOCMembersChanged;
|
||||
public event IOCMembersChangedHandler? OnIOCMembersChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 节点需要定位
|
||||
/// </summary>
|
||||
public event NodeLocatedHandler OnNodeLocate;
|
||||
public event NodeLocatedHandler? OnNodeLocate;
|
||||
#endregion
|
||||
|
||||
#region 属性
|
||||
@@ -196,12 +196,12 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 起始节点私有属性
|
||||
/// </summary>
|
||||
private NodeModelBase _startNode;
|
||||
private NodeModelBase? _startNode = null;
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
private NodeModelBase StartNode
|
||||
private NodeModelBase? StartNode
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -209,6 +209,10 @@ namespace Serein.NodeFlow
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_startNode is not null)
|
||||
{
|
||||
_startNode.IsStart = false;
|
||||
@@ -481,17 +485,19 @@ namespace Serein.NodeFlow
|
||||
|
||||
public bool RemoteDll(string assemblyFullName)
|
||||
{
|
||||
var library = NodeLibrarys.FirstOrDefault(nl => nl.Assembly.FullName.Equals(assemblyFullName));
|
||||
var library = NodeLibrarys.FirstOrDefault(nl => assemblyFullName.Equals(nl.Assembly.FullName));
|
||||
if(library is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var nodes = Nodes.Values.ToDictionary(
|
||||
key => key.MethodDetails.MethodName,
|
||||
value => value
|
||||
);
|
||||
if(nodes.Count == 0)
|
||||
var groupedNodes = Nodes.Values
|
||||
.Where(node => node.MethodDetails is not null)
|
||||
.ToArray()
|
||||
.GroupBy(node => node.MethodDetails!.MethodName)
|
||||
.ToDictionary(
|
||||
key => key.Key,
|
||||
group => group.Count());
|
||||
if(Nodes.Count == 0)
|
||||
{
|
||||
return true; // 当前无节点,可以直接删除
|
||||
}
|
||||
@@ -500,9 +506,12 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
foreach(var md in mds)
|
||||
{
|
||||
if (nodes.ContainsKey(md.MethodName))
|
||||
if(groupedNodes.TryGetValue(md.MethodName,out int count))
|
||||
{
|
||||
return false; // 创建过相关的节点
|
||||
if (count > 0)
|
||||
{
|
||||
return false; // 创建过相关的节点
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodDetailss.Remove(library);
|
||||
@@ -510,7 +519,7 @@ namespace Serein.NodeFlow
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,9 +694,10 @@ namespace Serein.NodeFlow
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetDelegate(string methodName, out Delegate del)
|
||||
public bool TryGetDelegate(string methodName, out Delegate? del)
|
||||
{
|
||||
if (MethodDelegates.TryGetValue(methodName, out del))
|
||||
|
||||
if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out del))
|
||||
{
|
||||
return del != null;
|
||||
}
|
||||
@@ -742,7 +752,7 @@ namespace Serein.NodeFlow
|
||||
return false;
|
||||
}
|
||||
nodeModel.DebugSetting.InterruptClass = interruptClass;
|
||||
OnNodeInterruptStateChange.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
|
||||
OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, interruptClass));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -958,11 +968,19 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
||||
var assemblyName = type.Assembly.GetName().Name;
|
||||
if (string.IsNullOrEmpty(assemblyName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var methods = MethodDetailsHelperTmp.GetMethodsToProcess(type);
|
||||
foreach(var method in methods)
|
||||
{
|
||||
(var md, var del) = MethodDetailsHelperTmp.CreateMethodDetails(type, method, assemblyName);
|
||||
|
||||
if(md is null || del is null)
|
||||
{
|
||||
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}");
|
||||
continue;
|
||||
}
|
||||
if (MethodDelegates.TryAdd(md.MethodName, del))
|
||||
{
|
||||
methodDetails.Add(md);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
return [];
|
||||
}
|
||||
internal override NodeInfo ToInfo()
|
||||
internal override NodeInfo? ToInfo()
|
||||
{
|
||||
if (MethodDetails is null) return null;
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public static class MethodDetailsHelperTmp
|
||||
/// 创建方法信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static (MethodDetails,Delegate) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
||||
public static (MethodDetails?,Delegate?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
||||
{
|
||||
|
||||
var methodName = method.Name;
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
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;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
|
||||
@@ -78,7 +69,7 @@ namespace Serein.NodeFlow.Tool
|
||||
// 如果类型已经缓存,直接返回缓存的类型
|
||||
if (typeCache.ContainsKey(typeName))
|
||||
{
|
||||
return Activator.CreateInstance(typeCache[typeName]);
|
||||
return Activator.CreateInstance(typeCache[typeName])!;
|
||||
}
|
||||
|
||||
// 定义动态程序集和模块
|
||||
@@ -98,7 +89,7 @@ namespace Serein.NodeFlow.Tool
|
||||
|
||||
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
|
||||
{
|
||||
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
|
||||
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)![0];
|
||||
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
|
||||
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
|
||||
}
|
||||
@@ -152,7 +143,7 @@ namespace Serein.NodeFlow.Tool
|
||||
typeCache[typeName] = dynamicType;
|
||||
|
||||
// 创建对象实例
|
||||
return Activator.CreateInstance(dynamicType);
|
||||
return Activator.CreateInstance(dynamicType)!;
|
||||
}
|
||||
|
||||
// 方法 2: 递归设置对象的属性值
|
||||
@@ -169,10 +160,10 @@ namespace Serein.NodeFlow.Tool
|
||||
if (value is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
// 创建嵌套对象
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
var nestedObj = Activator.CreateInstance(propInfo!.PropertyType);
|
||||
|
||||
// 递归设置嵌套对象的值
|
||||
SetPropertyValues(nestedObj, nestedProperties);
|
||||
SetPropertyValues(nestedObj!, nestedProperties);
|
||||
|
||||
// 将嵌套对象赋值给属性
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
@@ -180,7 +171,7 @@ namespace Serein.NodeFlow.Tool
|
||||
else
|
||||
{
|
||||
// 直接赋值给属性
|
||||
propInfo.SetValue(obj, value);
|
||||
propInfo!.SetValue(obj, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +213,7 @@ namespace Serein.NodeFlow.Tool
|
||||
if (propValue is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
if (SetPropertyValuesWithValidation(nestedObj, nestedProperties))
|
||||
if (nestedObj is not null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
|
||||
{
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
}
|
||||
@@ -235,22 +226,24 @@ namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
// 获取目标类型的数组元素类型
|
||||
var elementType = propInfo.PropertyType.GetElementType();
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
if (elementType is not null)
|
||||
{
|
||||
var item = Activator.CreateInstance(elementType);
|
||||
if (SetPropertyValuesWithValidation(item, list[i]))
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 赋值失败
|
||||
}
|
||||
}
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
|
||||
propInfo.SetValue(obj, array);
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var item = Activator.CreateInstance(elementType);
|
||||
if (item is not null && SetPropertyValuesWithValidation(item, list[i]))
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 赋值失败
|
||||
}
|
||||
}
|
||||
propInfo.SetValue(obj, array);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||
return false;
|
||||
}
|
||||
|
||||
private object GetMemberValue(object? obj, string memberPath)
|
||||
private object? GetMemberValue(object? obj, string memberPath)
|
||||
{
|
||||
string[] members = memberPath[1..].Split('.');
|
||||
foreach (var member in members)
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
}
|
||||
}
|
||||
|
||||
public static SereinConditionResolver ConditionParse(object data, string expression)
|
||||
public static SereinConditionResolver ConditionParse(object? data, string expression)
|
||||
{
|
||||
if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||
{
|
||||
@@ -91,7 +91,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <summary>
|
||||
/// 解析对象表达式
|
||||
/// </summary>
|
||||
private static SereinConditionResolver ParseObjectExpression(object data, string expression)
|
||||
private static SereinConditionResolver ParseObjectExpression(object? data, string expression)
|
||||
{
|
||||
var parts = expression.Split(' ');
|
||||
string operatorStr = parts[0]; // 获取操作类型
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||||
public static object? Evaluate(string expression, object targetObJ, out bool isChange)
|
||||
{
|
||||
var parts = expression.Split([' '], 2);
|
||||
if (parts.Length != 2)
|
||||
@@ -84,7 +84,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <param name="methodCall">方法名称</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private static object InvokeMethod(object target, string methodCall)
|
||||
private static object? InvokeMethod(object target, string methodCall)
|
||||
{
|
||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (methodParts.Length != 2)
|
||||
@@ -98,12 +98,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
.Select(p => p.Trim())
|
||||
.ToArray();
|
||||
|
||||
var method = target.GetType().GetMethod(methodName);
|
||||
if (method is null)
|
||||
{
|
||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
||||
}
|
||||
|
||||
var method = target.GetType().GetMethod(methodName) ?? throw new ArgumentException($"Method {methodName} not found on target.");
|
||||
var parameterValues = method.GetParameters()
|
||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
||||
.ToArray();
|
||||
@@ -119,15 +114,14 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <param name="memberPath">属性路径</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private static object GetMember(object target, string memberPath)
|
||||
private static object? GetMember(object? target, string memberPath)
|
||||
{
|
||||
if (target is null) return null;
|
||||
// 分割成员路径,按 '.' 处理多级访问
|
||||
var members = memberPath.Split('.');
|
||||
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (target == null) return null;
|
||||
|
||||
// 检查成员是否包含数组索引,例如 "cars[0]"
|
||||
var arrayIndexStart = member.IndexOf('[');
|
||||
if (arrayIndexStart != -1)
|
||||
@@ -148,22 +142,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
}
|
||||
|
||||
// 获取数组或集合对象
|
||||
var arrayProperty = target.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty != null)
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
var arrayField = target.GetType().GetField(arrayName);
|
||||
if (arrayField != null)
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
else
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
|
||||
// 访问数组或集合中的指定索引
|
||||
@@ -191,22 +185,22 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
else
|
||||
{
|
||||
// 处理非数组情况的属性或字段
|
||||
var property = target.GetType().GetProperty(member);
|
||||
if (property != null)
|
||||
var property = target?.GetType().GetProperty(member);
|
||||
if (property is null)
|
||||
{
|
||||
target = property.GetValue(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
var field = target.GetType().GetField(member);
|
||||
if (field != null)
|
||||
{
|
||||
target = field.GetValue(target);
|
||||
}
|
||||
else
|
||||
var field = target?.GetType().GetField(member);
|
||||
if (field is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {member} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = field.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = property.GetValue(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,7 +214,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
/// <param name="assignment">属性路径 </param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private static object SetMember(object target, string assignment)
|
||||
private static object? SetMember(object? target, string assignment)
|
||||
{
|
||||
var parts = assignment.Split(new[] { '=' }, 2);
|
||||
if (parts.Length != 2)
|
||||
@@ -238,7 +232,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
|
||||
// 检查是否包含数组索引
|
||||
var arrayIndexStart = member.IndexOf('[');
|
||||
if (arrayIndexStart != -1)
|
||||
if (arrayIndexStart != -1)
|
||||
{
|
||||
// 解析数组名和索引
|
||||
var arrayName = member.Substring(0, arrayIndexStart);
|
||||
@@ -255,24 +249,24 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
}
|
||||
|
||||
// 获取数组或集合
|
||||
var arrayProperty = target.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty != null)
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
var arrayField = target.GetType().GetField(arrayName);
|
||||
if (arrayField != null)
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
else
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
|
||||
// 获取目标数组或集合中的指定元素
|
||||
if (target is Array array)
|
||||
@@ -299,46 +293,47 @@ namespace Serein.NodeFlow.Tool.SereinExpression
|
||||
else
|
||||
{
|
||||
// 处理非数组情况的属性或字段
|
||||
var property = target.GetType().GetProperty(member);
|
||||
if (property != null)
|
||||
var property = target?.GetType().GetProperty(member);
|
||||
if (property is null)
|
||||
{
|
||||
target = property.GetValue(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
var field = target.GetType().GetField(member);
|
||||
if (field != null)
|
||||
{
|
||||
target = field.GetValue(target);
|
||||
}
|
||||
else
|
||||
var field = target?.GetType().GetField(member);
|
||||
if (field is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {member} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = field.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = property.GetValue(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置值
|
||||
var lastMember = members.Last();
|
||||
|
||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
||||
if (lastProperty != null)
|
||||
var lastProperty = target?.GetType().GetProperty(lastMember);
|
||||
if (lastProperty is null)
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||
lastProperty.SetValue(target, convertedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastField = target.GetType().GetField(lastMember);
|
||||
if (lastField != null)
|
||||
var lastField = target?.GetType().GetField(lastMember);
|
||||
if (lastField is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
||||
lastField.SetValue(target, convertedValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
||||
lastProperty.SetValue(target, convertedValue);
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
@@ -18,7 +18,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.Framework",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\Serein.Library.csproj", "{5E19D0F2-913A-4D1C-A6F8-1E1227BAA0E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net461DllTest", "Net461DllTest\Net461DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
||||
@@ -12,24 +12,24 @@ namespace Serein.WorkBench
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public class TestObject
|
||||
{
|
||||
//public class TestObject
|
||||
//{
|
||||
|
||||
public NestedObject Data { get; set; }
|
||||
// public NestedObject Data { get; set; }
|
||||
|
||||
public class NestedObject
|
||||
{
|
||||
public int Code { get; set; }
|
||||
public int Code2 { get; set; }
|
||||
// public class NestedObject
|
||||
// {
|
||||
// public int Code { get; set; }
|
||||
// public int Code2 { get; set; }
|
||||
|
||||
public string Tips { get; set; }
|
||||
// public string Tips { get; set; }
|
||||
|
||||
}
|
||||
public string ToUpper(string input)
|
||||
{
|
||||
return input.ToUpper();
|
||||
}
|
||||
}
|
||||
// }
|
||||
// public string ToUpper(string input)
|
||||
// {
|
||||
// return input.ToUpper();
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Serein.WorkBench
|
||||
/// 成功加载的工程文件
|
||||
/// </summary>
|
||||
public static SereinProjectData? FlowProjectData { get; set; }
|
||||
public static string FileDataPath = "";
|
||||
public static string FileDataPath { get; set; } = "";
|
||||
private void Application_Startup(object sender, StartupEventArgs e)
|
||||
{
|
||||
// 检查是否传入了参数
|
||||
@@ -163,7 +163,7 @@ namespace Serein.WorkBench
|
||||
string filePath;
|
||||
//filePath = @"F:\临时\project\tmp\project.dnf";
|
||||
//filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf";
|
||||
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net461DllTest\bin\Debug\project.dnf";
|
||||
filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf";
|
||||
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||
|
||||
@@ -139,15 +139,15 @@ namespace Serein.WorkBench
|
||||
/// <summary>
|
||||
/// 组合变换容器
|
||||
/// </summary>
|
||||
private TransformGroup canvasTransformGroup;
|
||||
private readonly TransformGroup canvasTransformGroup;
|
||||
/// <summary>
|
||||
/// 缩放画布
|
||||
/// </summary>
|
||||
private ScaleTransform scaleTransform;
|
||||
private readonly ScaleTransform scaleTransform;
|
||||
/// <summary>
|
||||
/// 平移画布
|
||||
/// </summary>
|
||||
private TranslateTransform translateTransform;
|
||||
private readonly TranslateTransform translateTransform;
|
||||
#endregion
|
||||
|
||||
public MainWindow()
|
||||
@@ -162,10 +162,17 @@ namespace Serein.WorkBench
|
||||
InitFlowEnvironmentEvent(); // 配置环境事件
|
||||
logWindow = InitConsoleOut(); // 重定向 Console 输出
|
||||
|
||||
InitCanvasUI(); // 配置画布
|
||||
canvasTransformGroup = new TransformGroup();
|
||||
scaleTransform = new ScaleTransform();
|
||||
translateTransform = new TranslateTransform();
|
||||
|
||||
canvasTransformGroup.Children.Add(scaleTransform);
|
||||
canvasTransformGroup.Children.Add(translateTransform);
|
||||
|
||||
FlowChartCanvas.RenderTransform = canvasTransformGroup;
|
||||
|
||||
|
||||
if(App.FlowProjectData is not null)
|
||||
if (App.FlowProjectData is not null)
|
||||
{
|
||||
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
|
||||
}
|
||||
@@ -198,17 +205,7 @@ namespace Serein.WorkBench
|
||||
|
||||
|
||||
|
||||
private void InitCanvasUI()
|
||||
{
|
||||
canvasTransformGroup = new TransformGroup();
|
||||
scaleTransform = new ScaleTransform();
|
||||
translateTransform = new TranslateTransform();
|
||||
|
||||
canvasTransformGroup.Children.Add(scaleTransform);
|
||||
canvasTransformGroup.Children.Add(translateTransform);
|
||||
|
||||
FlowChartCanvas.RenderTransform = canvasTransformGroup;
|
||||
}
|
||||
|
||||
private LogWindow InitConsoleOut()
|
||||
{
|
||||
@@ -243,7 +240,7 @@ namespace Serein.WorkBench
|
||||
}
|
||||
|
||||
var canvasData = project.Basic.Canvas;
|
||||
if (canvasData != null)
|
||||
if (canvasData is not null)
|
||||
{
|
||||
scaleTransform.ScaleX = 1;
|
||||
scaleTransform.ScaleY = 1;
|
||||
@@ -355,9 +352,9 @@ namespace Serein.WorkBench
|
||||
End = toNode,
|
||||
Type = connectionType
|
||||
};
|
||||
if (toNode is FlipflopNodeControl flipflopControl) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||
if (toNode is FlipflopNodeControl flipflopControl
|
||||
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||
{
|
||||
var nodeModel = flipflopControl?.ViewModel?.Node;
|
||||
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||
}
|
||||
|
||||
@@ -556,7 +553,7 @@ namespace Serein.WorkBench
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ViewObjectViewer.MonitorKey.Equals(monitorKey)) // 相同对象
|
||||
if (monitorKey.Equals(ViewObjectViewer.MonitorKey)) // 相同对象
|
||||
{
|
||||
ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新
|
||||
}
|
||||
@@ -704,7 +701,7 @@ namespace Serein.WorkBench
|
||||
/// <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)
|
||||
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)
|
||||
@@ -883,10 +880,10 @@ namespace Serein.WorkBench
|
||||
/// <param name="nodeControl"><para> 任何情景下都尽量避免直接操作 ViewModel 中的 NodeModel 节点,而是应该调用 FlowEnvironment 提供接口进行操作。</para> 因为 Workbench 应该更加关注UI视觉效果,而非直接干扰流程环境运行的逻辑。<para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para> </param>
|
||||
private void ConfigureContextMenu(NodeControlBase nodeControl)
|
||||
{
|
||||
|
||||
var contextMenu = new ContextMenu();
|
||||
|
||||
// var nodeModel = nodeControl.ViewModel.Node;
|
||||
|
||||
|
||||
if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
|
||||
{
|
||||
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
|
||||
@@ -894,7 +891,7 @@ namespace Serein.WorkBench
|
||||
DisplayReturnTypeTreeViewer(returnType);
|
||||
}));
|
||||
}
|
||||
var nodeGuid = nodeControl?.ViewModel?.Node?.Guid;
|
||||
var nodeGuid = nodeControl.ViewModel?.Node?.Guid;
|
||||
|
||||
#region 右键菜单功能 - 中断
|
||||
|
||||
@@ -923,10 +920,10 @@ namespace Serein.WorkBench
|
||||
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
|
||||
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
|
||||
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsSucceed)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsFail)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.IsError)));
|
||||
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection((NodeControlBase)s, ConnectionType.Upstream)));
|
||||
|
||||
|
||||
|
||||
@@ -976,6 +973,10 @@ namespace Serein.WorkBench
|
||||
{
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
||||
if (connection.ArrowPath is null || connection.BezierPath is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
connection.ArrowPath.ContextMenu = contextMenu;
|
||||
connection.BezierPath.ContextMenu = contextMenu;
|
||||
}
|
||||
@@ -1198,8 +1199,8 @@ namespace Serein.WorkBench
|
||||
// 准备放置条件表达式控件
|
||||
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
|
||||
{
|
||||
ConditionRegionControl conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
|
||||
if (conditionRegion != null)
|
||||
ConditionRegionControl? conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement);
|
||||
if (conditionRegion is not null)
|
||||
{
|
||||
TryPlaceNodeInRegion(conditionRegion, nodeControl);
|
||||
//// 如果存在条件区域容器
|
||||
@@ -1222,8 +1223,8 @@ namespace Serein.WorkBench
|
||||
// 准备放置条件表达式控件
|
||||
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
|
||||
{
|
||||
ConditionRegionControl conditionRegion = regionControl as ConditionRegionControl;
|
||||
if (conditionRegion != null)
|
||||
ConditionRegionControl? conditionRegion = regionControl as ConditionRegionControl;
|
||||
if (conditionRegion is not null)
|
||||
{
|
||||
// 如果存在条件区域容器
|
||||
conditionRegion.AddCondition(nodeControl);
|
||||
@@ -1365,14 +1366,17 @@ namespace Serein.WorkBench
|
||||
}
|
||||
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
|
||||
{
|
||||
var node = nodeControl?.ViewModel?.Node;
|
||||
var node = nodeControl.ViewModel.Node;
|
||||
//if (node is not null && (node.MethodDetails is null || node.MethodDetails.ReturnType != typeof(void))
|
||||
if (node is not null && node?.MethodDetails?.ReturnType != typeof(void))
|
||||
if (node is not null && node.MethodDetails?.ReturnType != typeof(void))
|
||||
{
|
||||
var key = node.Guid;
|
||||
var instance = node.GetFlowData();
|
||||
ViewObjectViewer.LoadObjectInformation(key, instance);
|
||||
ChangeViewerObj(key, instance);
|
||||
if(instance is not null)
|
||||
{
|
||||
ViewObjectViewer.LoadObjectInformation(key, instance);
|
||||
ChangeViewerObj(key, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void ChangeViewerObj(string key, object instance)
|
||||
@@ -1768,64 +1772,6 @@ namespace Serein.WorkBench
|
||||
}
|
||||
|
||||
e.Handled = true; // 防止事件传播影响其他控件
|
||||
return;
|
||||
// 如果正在选取状态,再次点击画布时自动确定选取范围,否则进入选取状态
|
||||
if (IsSelectControl)
|
||||
{
|
||||
IsSelectControl = false;
|
||||
// 释放鼠标捕获
|
||||
FlowChartCanvas.ReleaseMouseCapture();
|
||||
|
||||
// 隐藏选取矩形(如果需要保持选取状态显示,可以删除此行)
|
||||
SelectionRectangle.Visibility = Visibility.Collapsed;
|
||||
|
||||
// 处理选取区域内的元素(例如,获取选取范围内的控件)
|
||||
Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
|
||||
Canvas.GetTop(SelectionRectangle),
|
||||
SelectionRectangle.Width,
|
||||
SelectionRectangle.Height);
|
||||
|
||||
|
||||
// 在此处处理选取的逻辑
|
||||
foreach (UIElement element in FlowChartCanvas.Children)
|
||||
{
|
||||
Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element),
|
||||
element.RenderSize.Width, element.RenderSize.Height);
|
||||
|
||||
if (selectionArea.Contains(elementBounds))
|
||||
{
|
||||
// 选中元素,执行相应操作
|
||||
if (element is NodeControlBase control)
|
||||
{
|
||||
selectNodeControls.Add(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
SelectedNode();// 选择之后需要执行的操作
|
||||
}
|
||||
else
|
||||
{
|
||||
// 进入选取状态
|
||||
IsSelectControl = true;
|
||||
|
||||
// 开始选取时,记录鼠标起始点
|
||||
startSelectControolPoint = e.GetPosition(FlowChartCanvas);
|
||||
|
||||
// 初始化选取矩形的位置和大小
|
||||
Canvas.SetLeft(SelectionRectangle, startSelectControolPoint.X);
|
||||
Canvas.SetTop(SelectionRectangle, startSelectControolPoint.Y);
|
||||
SelectionRectangle.Width = 0;
|
||||
SelectionRectangle.Height = 0;
|
||||
|
||||
// 显示选取矩形
|
||||
SelectionRectangle.Visibility = Visibility.Visible;
|
||||
SelectionRectangle.ContextMenu ??= ConfiguerSelectionRectangle();
|
||||
|
||||
// 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件
|
||||
FlowChartCanvas.CaptureMouse();
|
||||
|
||||
}
|
||||
e.Handled = true; // 防止事件传播影响其他控件
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2323,13 +2269,13 @@ namespace Serein.WorkBench
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="element"></param>
|
||||
/// <returns></returns>
|
||||
private static T GetParentOfType<T>(DependencyObject element) where T : DependencyObject
|
||||
private static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
|
||||
{
|
||||
while (element != null)
|
||||
{
|
||||
if (element is T)
|
||||
if (element is T e)
|
||||
{
|
||||
return element as T;
|
||||
return e;
|
||||
}
|
||||
element = VisualTreeHelper.GetParent(element);
|
||||
}
|
||||
@@ -2342,9 +2288,9 @@ namespace Serein.WorkBench
|
||||
|
||||
private void JudgmentFlipFlopNode(NodeControlBase nodeControl)
|
||||
{
|
||||
if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器
|
||||
if (nodeControl is FlipflopNodeControl flipflopControl
|
||||
&& flipflopControl?.ViewModel?.Node is NodeModelBase nodeModel) // 判断是否为触发器
|
||||
{
|
||||
var nodeModel = flipflopControl?.ViewModel?.Node;
|
||||
int count = 0;
|
||||
foreach (var ct in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
@@ -2473,13 +2419,18 @@ namespace Serein.WorkBench
|
||||
node.Position = new Position(positionRelativeToParent.X, positionRelativeToParent.Y);
|
||||
}
|
||||
}
|
||||
var isPass = SaveContentToFile(out string savePath, out Action<string,string>? savaProjectFile);
|
||||
if(!isPass)
|
||||
if (!SaveContentToFile(out string savePath, out Action<string, string>? savaProjectFile))
|
||||
{
|
||||
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
|
||||
return;
|
||||
}
|
||||
|
||||
string librarySavePath = System.IO.Path.GetDirectoryName(savePath);
|
||||
string? librarySavePath = System.IO.Path.GetDirectoryName(savePath);
|
||||
if (string.IsNullOrEmpty(librarySavePath))
|
||||
{
|
||||
Console.WriteLine("保存项目DLL时返回了意外的文件保存路径");
|
||||
return;
|
||||
}
|
||||
Console.WriteLine(savePath);
|
||||
for (int index = 0; index < projectData.Librarys.Length; index++)
|
||||
{
|
||||
@@ -2578,17 +2529,17 @@ namespace Serein.WorkBench
|
||||
/// <param name="e"></param>
|
||||
private void ButtonTestExpObj_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string jsonString =
|
||||
"""
|
||||
{
|
||||
"Name": "张三",
|
||||
"Age": 24,
|
||||
"Address": {
|
||||
"City": "北京",
|
||||
"PostalCode": "10000"
|
||||
}
|
||||
}
|
||||
""";
|
||||
//string jsonString =
|
||||
//"""
|
||||
//{
|
||||
// "Name": "张三",
|
||||
// "Age": 24,
|
||||
// "Address": {
|
||||
// "City": "北京",
|
||||
// "PostalCode": "10000"
|
||||
// }
|
||||
//}
|
||||
//""";
|
||||
|
||||
var externalData = new Dictionary<string, object>
|
||||
{
|
||||
@@ -2615,12 +2566,12 @@ namespace Serein.WorkBench
|
||||
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
|
||||
}
|
||||
ObjDynamicCreateHelper.PrintObjectProperties(result);
|
||||
ObjDynamicCreateHelper.PrintObjectProperties(result!);
|
||||
Console.WriteLine( );
|
||||
var exp = "@set .Addresses[1].Street = qwq";
|
||||
var data = SerinExpressionEvaluator.Evaluate(exp, result, out bool isChange);
|
||||
var data = SerinExpressionEvaluator.Evaluate(exp, result!, out bool isChange);
|
||||
exp = "@get .Addresses[1].Street";
|
||||
data = SerinExpressionEvaluator.Evaluate(exp,result, out isChange);
|
||||
data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
|
||||
Console.WriteLine($"{exp} => {data}");
|
||||
}
|
||||
|
||||
@@ -2681,22 +2632,29 @@ namespace Serein.WorkBench
|
||||
|
||||
canvas.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
if (connection is not null && connection.BezierPath is null)
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection.BezierPath is null)
|
||||
{
|
||||
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||
//Canvas.SetZIndex(connection.BezierPath, -1);
|
||||
canvas.Children.Add(connection.BezierPath);
|
||||
}
|
||||
|
||||
if (connection is not null && connection.ArrowPath is null)
|
||||
if (connection.ArrowPath is null)
|
||||
{
|
||||
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
||||
//Canvas.SetZIndex(connection.ArrowPath, -1);
|
||||
canvas.Children.Add(connection.ArrowPath);
|
||||
}
|
||||
|
||||
|
||||
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
||||
isUpdating = false;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2746,10 +2704,10 @@ namespace Serein.WorkBench
|
||||
public class Connection
|
||||
{
|
||||
public ConnectionType Type { get; set; }
|
||||
public Canvas Canvas { get; set; }// 贝塞尔曲线所在画布
|
||||
public Canvas? Canvas { get; set; }// 贝塞尔曲线所在画布
|
||||
|
||||
public System.Windows.Shapes.Path BezierPath { get; set; }// 贝塞尔曲线路径
|
||||
public System.Windows.Shapes.Path ArrowPath { get; set; } // 箭头路径
|
||||
public System.Windows.Shapes.Path? BezierPath { get; set; }// 贝塞尔曲线路径
|
||||
public System.Windows.Shapes.Path? ArrowPath { get; set; } // 箭头路径
|
||||
|
||||
public required NodeControlBase Start { get; set; } // 起始
|
||||
public required NodeControlBase End { get; set; } // 结束
|
||||
@@ -2757,8 +2715,11 @@ namespace Serein.WorkBench
|
||||
|
||||
public void RemoveFromCanvas()
|
||||
{
|
||||
Canvas.Children.Remove(BezierPath); // 移除线
|
||||
Canvas.Children.Remove(ArrowPath); // 移除线
|
||||
if(Canvas != null)
|
||||
{
|
||||
Canvas.Children.Remove(BezierPath); // 移除线
|
||||
Canvas.Children.Remove(ArrowPath); // 移除线
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2766,6 +2727,10 @@ namespace Serein.WorkBench
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
if(Canvas is null || BezierPath is null || ArrowPath is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ namespace Serein.WorkBench.Node.View
|
||||
/// </summary>
|
||||
public partial class ConditionRegionControl : NodeControlBase
|
||||
{
|
||||
private Point _dragStartPoint;
|
||||
|
||||
public ConditionRegionControl() : base()
|
||||
{
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Serein.WorkBench.Themes
|
||||
{ConnectionType.IsError, []},
|
||||
}
|
||||
};
|
||||
string itemName = rootNodeModel?.MethodDetails?.MethodTips;
|
||||
string? itemName = rootNodeModel.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = rootNodeModel.ControlType.ToString();
|
||||
@@ -141,10 +141,10 @@ namespace Serein.WorkBench.Themes
|
||||
RootNode = child,
|
||||
ChildNodes = child.SuccessorNodes,
|
||||
};
|
||||
string itemName = child?.MethodDetails?.MethodTips;
|
||||
string? itemName = child?.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = child.ControlType.ToString();
|
||||
itemName = child?.ControlType.ToString();
|
||||
}
|
||||
TreeViewItem treeViewItem = new TreeViewItem
|
||||
{
|
||||
@@ -204,10 +204,10 @@ namespace Serein.WorkBench.Themes
|
||||
ChildNodes = childNodeModel.SuccessorNodes,
|
||||
};
|
||||
|
||||
string itemName = childNodeModel?.MethodDetails?.MethodTips;
|
||||
string? itemName = childNodeModel?.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = childNodeModel.ControlType.ToString();
|
||||
itemName = childNodeModel?.ControlType.ToString();
|
||||
}
|
||||
TreeViewItem treeViewItem = new TreeViewItem
|
||||
{
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
public partial class NodeTreeViewControl : UserControl
|
||||
{
|
||||
private IFlowEnvironment FlowEnvironment { get; set; }
|
||||
public NodeTreeViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <summary>
|
||||
/// 属性名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
/// <summary>
|
||||
/// 属性类型
|
||||
/// </summary>
|
||||
@@ -40,15 +40,15 @@ namespace Serein.WorkBench.Themes
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
public Type DataType { get; set; }
|
||||
public Type? DataType { get; set; }
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
public object DataValue { get; set; }
|
||||
public object? DataValue { get; set; }
|
||||
/// <summary>
|
||||
/// 数据路径
|
||||
/// </summary>
|
||||
public string DataPath { get; set; }
|
||||
public string DataPath { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,24 +80,24 @@ namespace Serein.WorkBench.Themes
|
||||
/// <summary>
|
||||
/// 运行环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment FlowEnvironment { get; set; }
|
||||
public IFlowEnvironment? FlowEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视对象的键
|
||||
/// </summary>
|
||||
public string MonitorKey { get => monitorKey; }
|
||||
public string? MonitorKey { get => monitorKey; }
|
||||
/// <summary>
|
||||
/// 正在监视的对象
|
||||
/// </summary>
|
||||
public object MonitorObj { get => monitorObj; }
|
||||
public object? MonitorObj { get => monitorObj; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视表达式
|
||||
/// </summary>
|
||||
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
public string? MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
|
||||
private string monitorKey;
|
||||
private object monitorObj;
|
||||
private string? monitorKey;
|
||||
private object? monitorObj;
|
||||
|
||||
// 用于存储当前展开的节点路径
|
||||
private HashSet<string> expandedNodePaths = new HashSet<string>();
|
||||
@@ -133,7 +133,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="e"></param>
|
||||
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
|
||||
if (FlowEnvironment is not null && FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
|
||||
{
|
||||
if (string.IsNullOrEmpty(MonitorExpression))
|
||||
{
|
||||
@@ -146,7 +146,7 @@ namespace Serein.WorkBench.Themes
|
||||
}
|
||||
}
|
||||
|
||||
private TreeViewItem? LoadTree(object obj)
|
||||
private TreeViewItem? LoadTree(object? obj)
|
||||
{
|
||||
if (obj is null) return null;
|
||||
var objectType = obj.GetType();
|
||||
@@ -178,7 +178,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <summary>
|
||||
/// 刷新对象属性树
|
||||
/// </summary>
|
||||
public void RefreshObjectTree(object obj)
|
||||
public void RefreshObjectTree(object? obj)
|
||||
{
|
||||
monitorObj = obj;
|
||||
var rootNode = LoadTree(obj);
|
||||
@@ -222,14 +222,20 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
|
||||
{
|
||||
if (flowDataDetails.ItemType == TreeItemType.Item || item.Items.Count == 0)
|
||||
if (flowDataDetails.ItemType != TreeItemType.Item && item.Items.Count != 0)
|
||||
{
|
||||
// 记录当前节点的路径
|
||||
var path = flowDataDetails.DataPath;
|
||||
expandedNodePaths.Add(path);
|
||||
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
||||
return;
|
||||
}
|
||||
|
||||
if(flowDataDetails.DataValue is null || flowDataDetails.DataType is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录当前节点的路径
|
||||
var path = flowDataDetails.DataPath;
|
||||
expandedNodePaths.Add(path);
|
||||
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,8 +282,8 @@ namespace Serein.WorkBench.Themes
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
|
||||
if (memberNode != null)
|
||||
TreeViewItem? memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
|
||||
if (memberNode is not null)
|
||||
{
|
||||
treeViewNode.Items.Add(memberNode); // 添加到当前节点
|
||||
|
||||
@@ -305,7 +311,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
private TreeViewItem? ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
@@ -353,7 +359,7 @@ namespace Serein.WorkBench.Themes
|
||||
else
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
string propertyValue = GetPropertyValue(obj, property, out object value);
|
||||
string propertyValue = GetPropertyValue(obj, property, out object? value);
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
@@ -417,7 +423,7 @@ namespace Serein.WorkBench.Themes
|
||||
else
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
string fieldValue = GetFieldValue(obj, field, out object value);
|
||||
string fieldValue = GetFieldValue(obj, field, out object? value);
|
||||
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
@@ -454,7 +460,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
|
||||
private string GetPropertyValue(object obj, PropertyInfo property,out object? value)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -482,7 +488,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="field"></param>
|
||||
/// <returns></returns>
|
||||
private string GetFieldValue(object obj, FieldInfo field, out object value)
|
||||
private string GetFieldValue(object obj, FieldInfo field, out object? value)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -227,10 +227,10 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
private TreeViewItem? GetParentTreeViewItem(TreeViewItem node)
|
||||
{
|
||||
DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
while (parent != null && !(parent is TreeViewItem))
|
||||
while (parent != null && parent is not TreeViewItem)
|
||||
{
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Serein.WorkBench.Tool.Converters
|
||||
return boolValue ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType,
|
||||
public object? ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user