修改了logwindows输出,避免高频输出时卡死。修改了流程运行上下文,使节点具备终止分支运行的能力。

This commit is contained in:
fengjiayi
2024-10-14 17:29:28 +08:00
parent f76f09da94
commit 4338554384
93 changed files with 4640 additions and 541 deletions

View File

@@ -0,0 +1,127 @@
using Serein.Library.Entity;
using Serein.NodeFlow.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Serein.WorkBench.Node.ViewModel
{
public abstract class NodeControlViewModelBase : INotifyPropertyChanged
{
public NodeControlViewModelBase(NodeModelBase node)
{
Node = node;
MethodDetails = Node.MethodDetails;
}
/// <summary>
/// 对应的节点实体类
/// </summary>
internal NodeModelBase Node { get; }
private bool isSelect;
/// <summary>
/// 表示节点控件是否被选中
/// </summary>
internal bool IsSelect
{
get => isSelect;
set
{
isSelect = value;
OnPropertyChanged();
}
}
public NodeDebugSetting DebugSetting
{
get => Node.DebugSetting;
set
{
if (value != null)
{
Node.DebugSetting = value;
OnPropertyChanged(/*nameof(DebugSetting)*/);
}
}
}
public MethodDetails MethodDetails
{
get => Node.MethodDetails;
set
{
if(value != null)
{
Node.MethodDetails = value;
OnPropertyChanged(/*nameof(MethodDetails)*/);
}
}
}
private bool isInterrupt;
public bool IsInterrupt
{
get => isInterrupt;
set
{
isInterrupt = value;
OnPropertyChanged(/*nameof(IsInterrupt)*/);
}
}
//public bool IsInterrupt
//{
// get => Node.DebugSetting.IsInterrupt;
// set
// {
// if (value)
// {
// Node.Interrupt();
// }
// else
// {
// Node.CancelInterrupt();
// }
// OnPropertyChanged(nameof(IsInterrupt));
// }
//}
//public bool IsProtectionParameter
//{
// get => MethodDetails.IsProtectionParameter;
// set
// {
// MethodDetails.IsProtectionParameter = value;
// OnPropertyChanged(nameof(IsInterrupt));
// }
//}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void Selected()
{
IsSelect = true;
}
public void CancelSelect()
{
IsSelect = false;
}
}
}

View File

@@ -0,0 +1,79 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ActionNodeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
xmlns:Converters="clr-namespace:Serein.WorkBench.Tool.Converters"
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
MaxWidth="300">
<UserControl.Resources>
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
</UserControl.Resources>
<Border BorderBrush="#8DE9FD" BorderThickness="1">
<Grid>
<Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
</Grid.ToolTip>
<Border>
<Border.Style>
<Style TargetType="Border">
<!-- 默认无边框 -->
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsInterrupt}" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="Background" Value="#80000000" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#8DE9FD">
<CheckBox IsChecked="{Binding DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
<CheckBox IsChecked="{Binding MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
<TextBlock Text="{Binding MethodDetails.MethodTips}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}"/>
<!-- ParameterProtectionMask 参数保护 -->
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
Visibility="{Binding MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
<Grid Grid.Row="2" Background="#D5F0FC" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderThickness="1">
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<Border Grid.Column="1" BorderThickness="1">
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Grid>
</Grid>
</Border>
<!--Visibility="{Binding IsEnable, Converter={StaticResource BoolToVisConverter}, ConverterParameter=False}"-->
</Grid>
</Border>
</local:NodeControlBase>

View File

@@ -0,0 +1,19 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.ViewModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// ActionNode.xaml 的交互逻辑
/// </summary>
public partial class ActionNodeControl : NodeControlBase
{
public ActionNodeControl(ActionNodeControlViewModel viewModel):base(viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,21 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ActionRegionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
MaxWidth="300">
<Grid>
<!--<Border BorderBrush="Black" BorderThickness="1" Padding="10">
<StackPanel>
<Grid Margin="2,2,2,5">
<TextBlock Text="动作区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
</Grid>
<ListBox x:Name="ActionsListBox" AllowDrop="True" Drop="ActionsListBox_Drop" />
</StackPanel>
</Border>-->
</Grid>
</local:NodeControlBase>

View File

@@ -0,0 +1,130 @@
using Serein.NodeFlow;
using Serein.NodeFlow.Model;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// ActionRegion.xaml 的交互逻辑
/// </summary>
public partial class ActionRegionControl : NodeControlBase
{
private Point _dragStartPoint;
//private new readonly CompositeActionNode Node;
//public override NodeControlViewModel ViewModel { get ; set ; }
public ActionRegionControl() : base(null)
{
InitializeComponent();
}
//public ActionRegionControl(CompositeActionNode node)
//{
// InitializeComponent();
// //ViewModel = new NodeControlViewModel(node);
// DataContext = ViewModel;
// base.Name = "动作组合节点";
//}
public void AddAction(NodeControlBase node, bool isTask = false)
{
/*TextBlock actionText = new TextBlock
{
Text = node.MethodDetails.MethodName + (isTask ? " (Task)" : ""),
Margin = new Thickness(10, 2, 0, 0),
Tag = node.MethodDetails,
};*/
/// Node?.AddNode((SingleActionNode)node.ViewModel.Node);
// ActionsListBox.Items.Add(node);
}
/* public async Task ExecuteActions(DynamicContext context)
{
foreach (TextBlock item in ActionsListBox.Items)
{
dynamic tag = item.Tag;
IAction action = tag.Action;
bool isTask = tag.IsTask;
if (isTask)
{
await Task.Run(() => action.Execute(Node.MethodDetails, context));
}
else
{
action.Execute(Node.MethodDetails, context);
}
}
}*/
private void ActionsListBox_Drop(object sender, DragEventArgs e)
{
/*if (e.Data.GetDataPresent("Type"))
{
Type droppedType = e.Data.GetData("Type") as Type;
if (droppedType != null && typeof(ICondition).IsAssignableFrom(droppedType) && droppedType.IsClass)
{
// 创建一个新的 TextBlock 并设置其属性
TextBlock conditionText = new TextBlock
{
Text = droppedType.Name,
Margin = new Thickness(10, 2, 0, 0),
Tag = droppedType
};
// 为 TextBlock 添加鼠标左键按下事件处理程序
// conditionText.MouseLeftButtonDown += TypeText_MouseLeftButtonDown;
// 为 TextBlock 添加鼠标移动事件处理程序
// conditionText.MouseMove += TypeText_MouseMove;
// 将 TextBlock 添加到 ActionsListBox 中
ActionsListBox.Items.Add(conditionText);
}
}*/
e.Handled = true;
}
// 用于拖动的鼠标事件处理程序
private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_dragStartPoint = e.GetPosition(null);
}
private void TypeText_MouseMove(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(null);
Vector diff = _dragStartPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
if (sender is TextBlock typeText)
{
MoveNodeData moveNodeData = new MoveNodeData
{
NodeControlType = Library.Enums.NodeControlType.ConditionRegion
};
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData);
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
//var dragData = new DataObject(MouseNodeType.CreateNodeInCanvas, typeText.Tag);
//DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ConditionNodeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
MaxWidth="300">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
</UserControl.Resources>
<Grid>
<Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" />
</Grid.ToolTip>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="#A8D8EA" BorderBrush="#A8D8EA" BorderThickness="1" HorizontalAlignment="Stretch">
<TextBlock Text="条件节点" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Grid Grid.Row="1" Background="#F1FFDF" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" IsChecked="{Binding IsCustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <!--Converter={StaticResource BoolToVis}-->
<TextBox Grid.Column="1" MinWidth="50" Text="{Binding CustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsCustomData}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock Grid.Column="1" MinWidth="50" Text="上一节点数据" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsCustomData}" Value="False">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<TextBox Grid.Row="2" Background="#f1F66F" MinWidth="100" Text="{Binding Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
<!--<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
<Border Grid.Row="2" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
<TextBlock Text="{Binding MethodDetails.MethodTips, Converter={StaticResource TypeToStringConverter}, StringFormat=return:{0}, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>-->
</Grid>
</local:NodeControlBase>

View File

@@ -0,0 +1,26 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.ViewModel;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// ConditionNode.xaml 的交互逻辑
/// </summary>
public partial class ConditionNodeControl : NodeControlBase
{
public ConditionNodeControl() : base()
{
// 窗体初始化需要
ViewModel = new ConditionNodeControlViewModel (new SingleConditionNode());
DataContext = ViewModel;
InitializeComponent();
}
public ConditionNodeControl(ConditionNodeControlViewModel viewModel):base(viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,19 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ConditionRegionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
MaxWidth="300">
<Grid>
<Border BorderBrush="Black" BorderThickness="1" Padding="10">
<StackPanel>
<DockPanel Margin="2,2,2,5">
<TextBlock Text="条件区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
</DockPanel>
<ListBox x:Name="ConditionsListBox" AllowDrop="True" Drop="ConditionsListBox_Drop"/>
</StackPanel>
</Border>
</Grid>
</local:NodeControlBase>

View File

@@ -0,0 +1,95 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.ViewModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// ConditionRegion.xaml 的交互逻辑
/// </summary>
public partial class ConditionRegionControl : NodeControlBase
{
public ConditionRegionControl() : base()
{
InitializeComponent();
}
public ConditionRegionControl(ConditionRegionNodeControlViewModel viewModel) : base(viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
/// <summary>
/// 添加条件控件
/// </summary>
/// <param name="condition"></param>
public void AddCondition(NodeControlBase node)
{
((CompositeConditionNode)ViewModel.Node).AddNode((SingleConditionNode)node.ViewModel.Node);
this.Width += node.Width;
this.Height += node.Height;
ConditionsListBox.Items.Add(node);
}
private void ConditionsListBox_Drop(object sender, DragEventArgs e)
{
e.Handled = true;
}
// Mouse event handlers for dragging
//private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
//{
// _dragStartPoint = e.GetPosition(null);
//}
//private void TypeText_MouseMove(object sender, MouseEventArgs e)
//{
// Point mousePos = e.GetPosition(null);
// Vector diff = _dragStartPoint - mousePos;
// if (e.LeftButton == MouseButtonState.Pressed &&
// (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
// Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
// {
// if (sender is TextBlock typeText)
// {
// var dragData = new DataObject(MouseNodeType.RegionType, typeText.Tag);
// DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
// }
// }
//}
/*private void TypeText_MouseMove(object sender, MouseEventArgs e)
{
Point mousePos = e.GetPosition(null);
Vector diff = _dragStartPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
TextBlock typeText = sender as TextBlock;
if (typeText != null)
{
DataObject dragData = new DataObject("Type", typeText.Tag);
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
}
}
}*/
}
}

View File

@@ -0,0 +1,40 @@
<UserControl x:Class="Serein.WorkBench.Node.View.DllControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
MaxWidth="300"
>
<DockPanel>
<StackPanel DockPanel.Dock="Top" >
<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType=UserControl}}"
FontWeight="Bold" FontSize="14" Margin="5" Background="#dbe2ef"/>
</StackPanel>
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<!--<RowDefinition Height="*"/>-->
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<!--<ColumnDefinition Width="*" />-->
</Grid.ColumnDefinitions>
<!--<GroupBox Grid.Row="0" Header="条件" Margin="5">
<ListBox x:Name="ConditionsListBox" Background="#A8D8EA"/>
</GroupBox>-->
<GroupBox Grid.Row="0" Header="动作" Margin="5">
<ListBox x:Name="ActionsListBox" Background="#D0F1F9"/>
</GroupBox>
<GroupBox Grid.Row="1" Header="触发器" Margin="5">
<ListBox x:Name="FlipflopsListBox" Background="#FACFC1"/>
</GroupBox>
</Grid>
</DockPanel>
</DockPanel>
</UserControl>

View File

@@ -0,0 +1,160 @@
using Serein.Library.Api;
using Serein.Library.Entity;
using Serein.Library.Enums;
using Serein.NodeFlow;
using System.Reflection;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Input;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// UserControl1.xaml 的交互逻辑
/// </summary>
public partial class DllControl : UserControl
{
private readonly NodeLibrary nodeLibrary;
public DllControl()
{
Header = "DLL文件"; // 设置初始值
InitializeComponent();
}
public DllControl(NodeLibrary nodeLibrary)
{
this.nodeLibrary = nodeLibrary;
Header = "DLL name : " + nodeLibrary.Assembly.GetName().Name;
InitializeComponent();
}
/// <summary>
/// Header 依赖属性,用于绑定标题
/// </summary>
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(DllControl), new PropertyMetadata(string.Empty));
/// <summary>
/// 向动作面板添加类型的文本块
/// </summary>
/// <param name="type">要添加的类型</param>
public void AddAction(MethodDetails md)
{
AddTypeToListBox(md, ActionsListBox);
}
/// <summary>
/// 向触发器面板添加类型的文本块
/// </summary>
/// <param name="type">要添加的类型</param>
public void AddFlipflop(MethodDetails md)
{
AddTypeToListBox(md, FlipflopsListBox);
}
/// <summary>
/// 向指定面板添加类型的文本块
/// </summary>
/// <param name="type">要添加的类型</param>
/// <param name="panel">要添加到的面板</param>
private void AddTypeToListBox(MethodDetails md, ListBox listBox)
{
// 创建一个新的 TextBlock 并设置其属性
TextBlock typeText = new TextBlock
{
Text = $"{md.MethodTips}",
Margin = new Thickness(10, 2, 0, 0),
Tag = md
};
// 为 TextBlock 添加鼠标左键按下事件处理程序
typeText.MouseLeftButtonDown += TypeText_MouseLeftButtonDown;
// 为 TextBlock 添加鼠标移动事件处理程序
typeText.MouseMove += TypeText_MouseMove;
// 将 TextBlock 添加到指定的面板
listBox.Items.Add(typeText);
}
/// <summary>
/// 存储拖拽开始时的鼠标位置
/// </summary>
private Point _dragStartPoint;
/// <summary>
/// 处理 TextBlock 的鼠标左键按下事件
/// </summary>
/// <param name="sender">事件源</param>
/// <param name="e">事件参数</param>
private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 记录鼠标按下时的位置
_dragStartPoint = e.GetPosition(null);
}
/// <summary>
/// 处理 TextBlock 的鼠标移动事件
/// </summary>
/// <param name="sender">事件源</param>
/// <param name="e">事件参数</param>
private void TypeText_MouseMove(object sender, MouseEventArgs e)
{
// 获取当前鼠标位置
Point mousePos = e.GetPosition(null);
// 计算鼠标移动的距离
Vector diff = _dragStartPoint - mousePos;
// 判断是否符合拖拽的最小距离要求
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
// 获取触发事件的 TextBlock
if (sender is TextBlock typeText && typeText.Tag is MethodDetails md)
{
MoveNodeData moveNodeData = new MoveNodeData
{
NodeControlType = md.MethodDynamicType switch
{
NodeType.Action => NodeControlType.Action,
NodeType.Flipflop => NodeControlType.Flipflop,
_ => NodeControlType.None,
},
MethodDetails = md,
};
if(moveNodeData.NodeControlType == NodeControlType.None)
{
return;
}
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData);
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ExpOpNodeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
MaxWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<TextBlock Grid.Row="0" Text=""></TextBlock>-->
<StackPanel Grid.Row="0" Orientation="Vertical" Background="LightSteelBlue">
<TextBlock Grid.Row="2" Text="表达式"></TextBlock>
<TextBox Text="{Binding Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch"></TextBox>
</StackPanel>
</Grid>
</local:NodeControlBase>

View File

@@ -0,0 +1,24 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.ViewModel;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// ExprOpNodeControl.xaml 的交互逻辑
/// </summary>
public partial class ExpOpNodeControl : NodeControlBase
{
public ExpOpNodeControl() : base()
{
// 窗体初始化需要
ViewModel = new ExpOpNodeViewModel(new SingleExpOpNode());
DataContext = ViewModel;
InitializeComponent();
}
public ExpOpNodeControl(ExpOpNodeViewModel viewModel) :base(viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,62 @@
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.FlipflopNodeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Converters="clr-namespace:Serein.WorkBench.Tool.Converters"
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
MaxWidth="300">
<UserControl.Resources>
<vm:TypeToStringConverter x:Key="TypeToStringConverter"/>
<!--<themes:ConditionControl x:Key="ConditionControl"/>-->
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
</UserControl.Resources>
<Border BorderBrush="#FCB334" BorderThickness="1">
<Grid>
<Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
</Grid.ToolTip>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FCB334">
<CheckBox IsChecked="{Binding DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
<CheckBox IsChecked="{Binding MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
Visibility="{Binding MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
<!--<Border Grid.Row="0" Background="#FCB334" >
</Border>-->
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
<Grid Grid.Row="2" Background="#D5F0FC" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderThickness="1">
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Grid.Column="1" BorderThickness="1">
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</Border>
</Grid>
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
</Grid>
</Border>
</local:NodeControlBase>

View File

@@ -0,0 +1,17 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.ViewModel;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// StateNode.xaml 的交互逻辑
/// </summary>
public partial class FlipflopNodeControl : NodeControlBase
{
public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,61 @@
using Serein.Library.Api;
using Serein.Library.Entity;
using Serein.NodeFlow.Base;
using Serein.WorkBench.Node.ViewModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using System.Windows.Media;
namespace Serein.WorkBench.Node.View
{
/// <summary>
/// 节点控件基类(控件)
/// </summary>
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
{
public NodeControlViewModelBase ViewModel { get; set; }
protected NodeControlBase()
{
this.Background = Brushes.Transparent;
}
protected NodeControlBase(NodeControlViewModelBase viewModelBase)
{
ViewModel = viewModelBase;
this.Background = Brushes.Transparent;
}
}
public class FLowNodeObObservableCollection<T> : ObservableCollection<T>
{
public void AddRange(IEnumerable<T> items)
{
foreach (var item in items)
{
this.Items.Add(item);
}
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}
}
}

View File

@@ -0,0 +1,15 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
namespace Serein.WorkBench.Node.ViewModel
{
public class ActionNodeControlViewModel : NodeControlViewModelBase
{
private readonly SingleActionNode node;
public ActionNodeControlViewModel(SingleActionNode node):base(node)
{
this.node = node;
}
}
}

View File

@@ -0,0 +1,44 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
namespace Serein.WorkBench.Node.ViewModel
{
public class ConditionNodeControlViewModel : NodeControlViewModelBase
{
private readonly SingleConditionNode singleConditionNode;
/// <summary>
/// 是否为自定义参数
/// </summary>
public bool IsCustomData
{
get => singleConditionNode.IsCustomData;
set { singleConditionNode.IsCustomData= value; OnPropertyChanged(); }
}
/// <summary>
/// 自定义参数值
/// </summary>
public object? CustomData
{
get => singleConditionNode.CustomData;
set { singleConditionNode.CustomData = value ; OnPropertyChanged(); }
}
/// <summary>
/// 表达式
/// </summary>
public string Expression
{
get => singleConditionNode.Expression;
set { singleConditionNode.Expression = value; OnPropertyChanged(); }
}
public ConditionNodeControlViewModel(SingleConditionNode node) : base(node)
{
this.singleConditionNode = node;
//IsCustomData = false;
//CustomData = "";
//Expression = "PASS";
}
}
}

View File

@@ -0,0 +1,18 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.WorkBench.Node.ViewModel
{
public class ConditionRegionNodeControlViewModel : NodeControlViewModelBase
{
public ConditionRegionNodeControlViewModel(CompositeConditionNode node):base(node)
{
}
}
}

View File

@@ -0,0 +1,26 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
namespace Serein.WorkBench.Node.ViewModel
{
public class ExpOpNodeViewModel: NodeControlViewModelBase
{
public readonly SingleExpOpNode node;
public string Expression
{
get => node.Expression;
set
{
node.Expression = value;
OnPropertyChanged();
}
}
public ExpOpNodeViewModel(SingleExpOpNode node) : base(node)
{
this.node = node;
}
}
}

View File

@@ -0,0 +1,14 @@
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
namespace Serein.WorkBench.Node.ViewModel
{
public class FlipflopNodeControlViewModel : NodeControlViewModelBase
{
private readonly SingleFlipflopNode node;
public FlipflopNodeControlViewModel(SingleFlipflopNode node) : base(node)
{
this.node = node;
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Serein.WorkBench.Node.ViewModel
{
public class TypeToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Type type)
{
return type.ToString();
}
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}