mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
对象预览器支持了值类型集合成员的简单预览
This commit is contained in:
@@ -11,6 +11,13 @@
|
||||
<ResourceDictionary Source="/Themes/MethodDetailsControl.xaml" />
|
||||
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="{x:Type TextBlock }">
|
||||
<!--<Setter Property="FontFamily" Value="Comic Sans MS"/>-->
|
||||
<Setter Property="Foreground" Value="#071042"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
</Application.Resources>
|
||||
|
||||
</Application>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench"
|
||||
xmlns:custom="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:nodeView="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||
Title="Dynamic Node Flow" Height="900" Width="1400"
|
||||
AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver"
|
||||
@@ -21,51 +21,49 @@
|
||||
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="{Binding CancelConnectionCommand}"/>
|
||||
|
||||
|
||||
</Window.InputBindings>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<DockPanel Grid.Column="0" Background="#F5F5F5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="40"></RowDefinition>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="2*"></RowDefinition>
|
||||
<RowDefinition Height="3"></RowDefinition>
|
||||
<RowDefinition Height="3*"></RowDefinition>
|
||||
<!--<RowDefinition Height="3"></RowDefinition>-->
|
||||
<!--<RowDefinition Height="3*"></RowDefinition>-->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Margin="2,2,2,5" Grid.Row="0" Grid.ColumnSpan="2" >
|
||||
<Grid Margin="2,2,1,5" Grid.Row="0" >
|
||||
<Button Grid.Row="0" Content="保存项目" Click="ButtonSaveFile_Click" HorizontalAlignment="Left" Margin="5,5,5,5"/>
|
||||
<!--<Button Grid.Row="0" Content="卸载清空" Click="UnloadAllButton_Click" HorizontalAlignment="Right" Margin="5,5,5,5"/>-->
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" Grid.ColumnSpan="2">
|
||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<custom:ExpOpNodeControl x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<custom:ConditionNodeControl x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<custom:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<nodeView:ExpOpNodeControl x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<nodeView:ConditionNodeControl x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<nodeView:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" MaxHeight="400" Grid.ColumnSpan="2" Margin="0,72,0,5" Grid.RowSpan="3">
|
||||
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto" MaxHeight="400" Grid.RowSpan="2">
|
||||
<StackPanel x:Name="DllStackPanel" Margin="5"/>
|
||||
</ScrollViewer>
|
||||
<GridSplitter Grid.Row="3" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" ResizeBehavior="PreviousAndNext" Background="Gray" Grid.ColumnSpan="2"/>
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
|
||||
<themes:ObjectViewerControl x:Name="ObjectViewer"/>
|
||||
</Grid>
|
||||
<!--<GridSplitter Grid.Row="3" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" ResizeBehavior="PreviousAndNext" Background="Gray"/>-->
|
||||
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
|
||||
<!--<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" DragDelta="GridSplitter_DragDelta"/>-->
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
|
||||
|
||||
<Grid Grid.Column="2" >
|
||||
@@ -79,18 +77,23 @@
|
||||
<StackPanel Grid.Row="0" Background="#F5F5F5" Orientation="Horizontal" >
|
||||
<Button x:Name="ButtonDebugRun" Content="运行" Width="100" Margin="10" Click="ButtonDebugRun_Click"></Button>
|
||||
<Button x:Name="ButtonDebugFlipflopNode" Content="结束" Width="100" Margin="10" Click="ButtonDebugFlipflopNode_Click"></Button>
|
||||
<Button x:Name="ButtonStartFlowInSelectNode" Content="从选定节点开始" Width="100" Margin="10" Click="ButtonStartFlowInSelectNode_Click"></Button>
|
||||
<!--<Button x:Name="ButtonReflushCanvasConfig" Content="重置画布设置" Width="100" Margin="10" Click="ButtonReflushCanvasConfig_Click"></Button>-->
|
||||
<!--<Button x:Name="ButtonLoadCanvasConfig" Content="加载画布设置" Width="100" Margin="10" Click="ButtonLoadCanvasConfig_Click"></Button>-->
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<!--HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"-->
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
x:Name="FlowChartStackPanel"
|
||||
|
||||
ClipToBounds="True">
|
||||
<Canvas
|
||||
x:Name="FlowChartCanvas"
|
||||
Background="#D9FFEA"
|
||||
Background="#E1FBEA"
|
||||
AllowDrop="True"
|
||||
Width="1000"
|
||||
Width="700"
|
||||
Height="700"
|
||||
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
|
||||
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
|
||||
@@ -132,14 +135,6 @@
|
||||
Canvas.Left="0"
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource BottomThumbPositionConverter}}"/>-->
|
||||
|
||||
<!-- Bottom-Right Thumb -->
|
||||
<Thumb x:Name="BottomRightThumb"
|
||||
Width="40" Height="40"
|
||||
DragDelta="Thumb_DragDelta_BottomRight"
|
||||
Cursor="SizeNWSE"
|
||||
Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource RightThumbPositionConverter}}"
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource BottomThumbPositionConverter}}"/>
|
||||
|
||||
<!-- Left Thumb -->
|
||||
<!--<Thumb x:Name="LeftThumb"
|
||||
Width="10" Height="10"
|
||||
@@ -149,15 +144,6 @@
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource VerticalCenterThumbPositionConverter}}"/>-->
|
||||
|
||||
<!-- Right Thumb -->
|
||||
<!--Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource RightThumbPositionConverter}}"
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource VerticalCenterThumbPositionConverter}}"-->
|
||||
<Thumb x:Name="RightThumb" Width="5" Cursor="SizeWE" Canvas.Top="0" Canvas.Right="0" DragDelta="Thumb_DragDelta_Right">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
<Border Background="#B1B9F8" Width="5" Height="{Binding RelativeSource={RelativeSource AncestorType=Canvas}, Path=ActualHeight}" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
<!-- Top Thumb -->
|
||||
<!--<Thumb x:Name="TopThumb"
|
||||
@@ -168,24 +154,59 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con
|
||||
Canvas.Top="0"/>-->
|
||||
|
||||
<!-- Bottom Thumb -->
|
||||
<!-- Bottom-Right Thumb -->
|
||||
<Thumb x:Name="BottomRightThumb"
|
||||
Width="15" Height="15"
|
||||
DragDelta="Thumb_DragDelta_BottomRight"
|
||||
Cursor="SizeNWSE"
|
||||
Canvas.Left="{Binding ActualWidth, Converter={StaticResource RightThumbPositionConverter}, ElementName=FlowChartCanvas, Mode=OneWay}"
|
||||
Canvas.Top="{Binding ActualHeight, Converter={StaticResource BottomThumbPositionConverter}, ElementName=FlowChartCanvas, Mode=OneWay}"/>
|
||||
|
||||
<!--Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource RightThumbPositionConverter}}"
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource VerticalCenterThumbPositionConverter}}"-->
|
||||
<Thumb x:Name="RightThumb" Width="5" Cursor="SizeWE" Canvas.Top="0" Canvas.Right="0" DragDelta="Thumb_DragDelta_Right">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
<Border Background="#B1B9F8" Width="5" Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType={x:Type Canvas}}}" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
<!--Canvas.Left="{Binding ActualWidth, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource HorizontalCenterThumbPositionConverter}}"
|
||||
Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Converter={StaticResource BottomThumbPositionConverter}}"-->
|
||||
<Thumb x:Name="BottomThumb" Height="5" Cursor="SizeNS" Canvas.Bottom="0" Canvas.Left="0" DragDelta="Thumb_DragDelta_Bottom">
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
<Border Background="#B1B9F8" Height="5" Width="{Binding RelativeSource={RelativeSource AncestorType=Canvas}, Path=ActualWidth}" />
|
||||
<Border Background="#B1B9F8" Height="5" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type Canvas}}}" />
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
</Canvas>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
</Grid>
|
||||
<GridSplitter Grid.Column="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
|
||||
<!--IOC容器属性-->
|
||||
<Grid Grid.Column="4" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" >
|
||||
<!--<themes:LazyTreeView x:Name="lazyTreeView" />-->
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" >
|
||||
<themes:IOCObjectViewControl x:Name="IOCObjectViewer">
|
||||
<!--<x:Arguments>
|
||||
<x:String>Apple</x:String>
|
||||
</x:Arguments>-->
|
||||
</themes:IOCObjectViewControl>
|
||||
</Grid>
|
||||
<Grid Grid.Row="3" Margin="0,3,0,0" Grid.RowSpan="2">
|
||||
<themes:ObjectViewerControl x:Name="ViewObjectViewer"></themes:ObjectViewerControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -148,25 +148,15 @@ namespace Serein.WorkBench
|
||||
|
||||
ViewModel = new MainWindowViewModel(this);
|
||||
FlowEnvironment = ViewModel.FlowEnvironment;
|
||||
ObjectViewer.FlowEnvironment = FlowEnvironment;
|
||||
ViewObjectViewer.FlowEnvironment = FlowEnvironment;
|
||||
|
||||
InitFlowEnvironmentEvent(); // 配置环境事件
|
||||
|
||||
logWindow = new LogWindow();
|
||||
logWindow.Show();
|
||||
// 重定向 Console 输出
|
||||
var logTextWriter = new LogTextWriter(msg => logWindow.AppendText(msg), () => logWindow.Clear());;
|
||||
Console.SetOut(logTextWriter);
|
||||
logWindow = InitConsoleOut(); // 重定向 Console 输出
|
||||
|
||||
InitCanvasUI();
|
||||
InitCanvasUI(); // 配置画布
|
||||
|
||||
var project = App.FlowProjectData;
|
||||
if (project == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小
|
||||
FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目
|
||||
|
||||
FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目
|
||||
}
|
||||
|
||||
private void InitFlowEnvironmentEvent()
|
||||
@@ -186,9 +176,6 @@ namespace Serein.WorkBench
|
||||
FlowEnvironment.OnInterruptTrigger += FlowEnvironment_OnInterruptTrigger;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void InitCanvasUI()
|
||||
{
|
||||
canvasTransformGroup = new TransformGroup();
|
||||
@@ -202,7 +189,15 @@ namespace Serein.WorkBench
|
||||
//FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
|
||||
}
|
||||
|
||||
|
||||
private LogWindow InitConsoleOut()
|
||||
{
|
||||
var logWindow = new LogWindow();
|
||||
logWindow.Show();
|
||||
// 重定向 Console 输出
|
||||
var logTextWriter = new LogTextWriter(msg => logWindow.AppendText(msg), () => logWindow.Clear()); ;
|
||||
Console.SetOut(logTextWriter);
|
||||
return logWindow;
|
||||
}
|
||||
|
||||
#region 窗体加载方法
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
@@ -215,15 +210,20 @@ namespace Serein.WorkBench
|
||||
}
|
||||
private void Window_ContentRendered(object sender, EventArgs e)
|
||||
{
|
||||
var project = App.FlowProjectData;
|
||||
if (project == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小
|
||||
foreach (var connection in Connections)
|
||||
{
|
||||
connection.Refresh();
|
||||
}
|
||||
|
||||
var canvasData = App.FlowProjectData?.Basic.Canvas;
|
||||
var canvasData = project.Basic.Canvas;
|
||||
if (canvasData != null)
|
||||
{
|
||||
|
||||
scaleTransform.ScaleX = 1;
|
||||
scaleTransform.ScaleY = 1;
|
||||
translateTransform.X = 0;
|
||||
@@ -453,35 +453,36 @@ namespace Serein.WorkBench
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 被监视的对象发生改变(节点执行了一次)
|
||||
/// 被监视的对象发生改变
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void FlowEnvironment_OnMonitorObjectChange(MonitorObjectEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
NodeControlBase nodeControl = GuidToControl(nodeGuid);
|
||||
ObjectViewer.Dispatcher.BeginInvoke(() => {
|
||||
if (string.IsNullOrEmpty(ObjectViewer.NodeGuid)) // 如果没有加载过
|
||||
|
||||
object monitorKey = MonitorObjectEventArgs.ObjSourceType.NodeFlowData switch
|
||||
{
|
||||
MonitorObjectEventArgs.ObjSourceType.NodeFlowData => nodeGuid,
|
||||
_ => eventArgs.NewData,
|
||||
};
|
||||
|
||||
//NodeControlBase nodeControl = GuidToControl(nodeGuid);
|
||||
ViewObjectViewer.Dispatcher.BeginInvoke(() => {
|
||||
if (ViewObjectViewer.MonitorObj is null) // 如果没有加载过对象
|
||||
{
|
||||
ObjectViewer.NodeGuid = nodeGuid;
|
||||
ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
//ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
}
|
||||
ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象 ViewObjectViewerControl.MonitorType.Obj
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加载过,如果显示的对象来源并非同一个节点,则停止监听之前的节点
|
||||
if (!ObjectViewer.NodeGuid.Equals(nodeGuid))
|
||||
if (ViewObjectViewer.MonitorKey.Equals(monitorKey)) // 相同对象
|
||||
{
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false);
|
||||
ObjectViewer.NodeGuid = nodeGuid;
|
||||
//ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
ViewObjectViewer.RefreshObjectTree(eventArgs.NewData); // 刷新
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectViewer.RefreshObjectTree(eventArgs.NewData);
|
||||
ViewObjectViewer.LoadObjectInformation(monitorKey, eventArgs.NewData); // 加载对象
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -733,18 +734,6 @@ namespace Serein.WorkBench
|
||||
|
||||
#endregion
|
||||
|
||||
contextMenu.Items.Add(CreateMenuItem("查看数据", (s, e) =>
|
||||
{
|
||||
var node = nodeControl?.ViewModel?.Node;
|
||||
if(node is not null)
|
||||
{
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false); // 通知环境,该节点的数据更新后需要传到UI
|
||||
ObjectViewer.NodeGuid = node.Guid;
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
|
||||
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
|
||||
|
||||
@@ -847,7 +836,7 @@ namespace Serein.WorkBench
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// var typeViewerWindow = new ObjectViewerWindow();
|
||||
// var typeViewerWindow = new ViewObjectViewerWindow();
|
||||
// typeViewerWindow.LoadObjectInformation(@object);
|
||||
// typeViewerWindow.Show();
|
||||
// }
|
||||
@@ -1076,16 +1065,59 @@ namespace Serein.WorkBench
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件的鼠标左键按下事件,启动拖动操作。
|
||||
/// 控件的鼠标左键按下事件,启动拖动操作。同时显示当前正在传递的数据。
|
||||
/// </summary>
|
||||
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
IsControlDragging = true;
|
||||
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
|
||||
((UIElement)sender).CaptureMouse(); // 捕获鼠标
|
||||
e.Handled = true; // 防止事件传播影响其他控件
|
||||
if(sender is NodeControlBase nodeControl)
|
||||
{
|
||||
ChangeViewerObjOfNode(nodeControl);
|
||||
if (nodeControl.ViewModel.Node.MethodDetails.IsProtectionParameter) return;
|
||||
IsControlDragging = true;
|
||||
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
|
||||
((UIElement)sender).CaptureMouse(); // 捕获鼠标
|
||||
e.Handled = true; // 防止事件传播影响其他控件
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
|
||||
{
|
||||
// int i = false;
|
||||
var node = nodeControl?.ViewModel?.Node;
|
||||
if (node is not null && node.MethodDetails.ReturnType != typeof(void))
|
||||
{
|
||||
if (ViewObjectViewer.MonitorObj is null)
|
||||
{
|
||||
FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
// FlowEnvironment.SetMonitorObjState(nodeObj, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
return;
|
||||
}
|
||||
var nodeObj = node.GetFlowData();
|
||||
if (nodeObj is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//if (nodeObj.Equals(ViewObjectViewer.MonitorObj) == true)
|
||||
//{
|
||||
// // 选择同一个控件,不再监视
|
||||
// ViewObjectViewer.RefreshObjectTree(nodeObj);
|
||||
// return;
|
||||
//}
|
||||
if (node.Guid.Equals(ViewObjectViewer.MonitorKey) == true)
|
||||
{
|
||||
ViewObjectViewer.RefreshObjectTree(nodeObj);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlowEnvironment.SetMonitorObjState(ViewObjectViewer.MonitorKey, false); // 取消对旧节点的监视
|
||||
FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
||||
/// </summary>
|
||||
@@ -1691,12 +1723,18 @@ namespace Serein.WorkBench
|
||||
}
|
||||
private void SelectedNode()
|
||||
{
|
||||
if(selectNodeControls.Count == 0)
|
||||
|
||||
if (selectNodeControls.Count == 0)
|
||||
{
|
||||
//Console.WriteLine($"没有选择控件");
|
||||
SelectionRectangle.Visibility = Visibility.Collapsed;
|
||||
return;
|
||||
}
|
||||
if(selectNodeControls.Count == 1)
|
||||
{
|
||||
// ChangeViewerObjOfNode(selectNodeControls[0]);
|
||||
}
|
||||
|
||||
//Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件");
|
||||
foreach (var node in selectNodeControls)
|
||||
{
|
||||
@@ -2018,7 +2056,7 @@ namespace Serein.WorkBench
|
||||
|
||||
#endregion
|
||||
|
||||
#region 窗体静态方法
|
||||
#region 静态方法:创建节点,创建菜单子项,获取区域
|
||||
|
||||
|
||||
private static TControl CreateNodeControl<TControl, TViewModel>(NodeModelBase model)
|
||||
@@ -2115,6 +2153,12 @@ namespace Serein.WorkBench
|
||||
|
||||
#endregion
|
||||
|
||||
#region IOC视图管理
|
||||
void LoadIOCObjectViewer()
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -2165,6 +2209,27 @@ namespace Serein.WorkBench
|
||||
private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FlowEnvironment?.Exit(); // 在运行平台上点击了退出
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void ButtonStartFlowInSelectNode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(selectNodeControls.Count == 0)
|
||||
{
|
||||
Console.WriteLine("请至少选择一个节点");
|
||||
}
|
||||
else if (selectNodeControls.Count > 1)
|
||||
{
|
||||
Console.WriteLine("请只选择一个节点");
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.FlowEnvironment.StartFlowInSelectNodeAsync(selectNodeControls[0].ViewModel.Node.Guid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,13 @@
|
||||
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
||||
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="#8DE9FD" BorderThickness="1">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Border>
|
||||
@@ -40,9 +44,7 @@
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FFCFDF">
|
||||
<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"/>
|
||||
@@ -50,17 +52,17 @@
|
||||
<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="Black" BorderThickness="0"
|
||||
<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" >
|
||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border Grid.Column="1" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
|
||||
<Border Grid.Column="1" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -71,6 +73,7 @@
|
||||
|
||||
<!--Visibility="{Binding IsEnable, Converter={StaticResource BoolToVisConverter}, ConverterParameter=False}"-->
|
||||
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:NodeControlBase>
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
<ListBox x:Name="ConditionsListBox" Background="#A8D8EA"/>
|
||||
</GroupBox>-->
|
||||
<GroupBox Grid.Row="0" Header="动作" Margin="5">
|
||||
<ListBox x:Name="ActionsListBox" Background="#FFCFDF"/>
|
||||
<ListBox x:Name="ActionsListBox" Background="#D0F1F9"/>
|
||||
</GroupBox>
|
||||
<GroupBox Grid.Row="1" Header="触发器" Margin="5">
|
||||
<ListBox x:Name="FlipflopsListBox" Background="#FFFFD2"/>
|
||||
<ListBox x:Name="FlipflopsListBox" Background="#FACFC1"/>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -12,10 +12,13 @@
|
||||
<vm:TypeToStringConverter x:Key="TypeToStringConverter"/>
|
||||
<!--<themes:ConditionControl x:Key="ConditionControl"/>-->
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Border BorderBrush="#FCB334" BorderThickness="1">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
@@ -23,23 +26,25 @@
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Background="#FFFFD2" BorderBrush="Black" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
|
||||
<Border Background="#FCB334" >
|
||||
<TextBlock Grid.Row="0" Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||
<Grid Grid.Row="2" >
|
||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Border Grid.Column="1" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
|
||||
<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>
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:NodeControlBase>
|
||||
|
||||
25
WorkBench/Themes/IOCObjectViewControl.xaml
Normal file
25
WorkBench/Themes/IOCObjectViewControl.xaml
Normal file
@@ -0,0 +1,25 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.IOCObjectViewControl"
|
||||
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.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<GroupBox Grid.Row="0" Header="已注册待绑定的类型" Margin="5">
|
||||
<ListBox x:Name="TypeListBox" Background="#E3F6FA"/>
|
||||
</GroupBox>
|
||||
<GroupBox Grid.Row="1" Header="完成注入的实例" Margin="5">
|
||||
<ListBox x:Name="DependenciesListBox" Background="#E3FAE9"/>
|
||||
</GroupBox>
|
||||
<!--<GroupBox Grid.Row="3" Header="未完成注入的实例" Margin="5">
|
||||
<ListBox x:Name="UnfinishedDependenciesListBox" Background="#FFE9D7"/>
|
||||
</GroupBox>-->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
40
WorkBench/Themes/IOCObjectViewControl.xaml.cs
Normal file
40
WorkBench/Themes/IOCObjectViewControl.xaml.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// IOCObjectViewControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class IOCObjectViewControl : UserControl
|
||||
{
|
||||
private IOCObjectViewMoel IOCObjectViewMoel;
|
||||
private SereinIOC sereinIOC;
|
||||
public void SetIOC(SereinIOC sereinIOC)
|
||||
{
|
||||
this.sereinIOC = sereinIOC;
|
||||
}
|
||||
|
||||
public IOCObjectViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
IOCObjectViewMoel = new IOCObjectViewMoel();
|
||||
DataContext = IOCObjectViewMoel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
WorkBench/Themes/IOCObjectViewMoel.cs
Normal file
12
WorkBench/Themes/IOCObjectViewMoel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
internal class IOCObjectViewMoel
|
||||
{
|
||||
}
|
||||
}
|
||||
26
WorkBench/Themes/LazyTreeView.xaml
Normal file
26
WorkBench/Themes/LazyTreeView.xaml
Normal file
@@ -0,0 +1,26 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.LazyTreeView"
|
||||
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.Themes"
|
||||
xmlns:converters="clr-namespace:Serein.WorkBench.Tool.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
|
||||
<UserControl.Resources>
|
||||
<converters:TypeToColorConverter x:Key="TypeToColorConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<TreeView Name="treeView"
|
||||
SelectedItemChanged="treeView_SelectedItemChanged"
|
||||
ItemsSource="{Binding RootNodes}">
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate ItemsSource="{Binding SuccessorNodes}">
|
||||
<TextBlock Text="{Binding DisplayName}" Foreground="{Binding ControlType, Converter={StaticResource TypeToColorConverter}}" />
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
58
WorkBench/Themes/LazyTreeView.xaml.cs
Normal file
58
WorkBench/Themes/LazyTreeView.xaml.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// LazyTreeView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class LazyTreeView : UserControl
|
||||
{
|
||||
public ObservableCollection<NodeModelBase> RootNodes { get; set; }
|
||||
|
||||
public LazyTreeView()
|
||||
{
|
||||
InitializeComponent();
|
||||
RootNodes = new ObservableCollection<NodeModelBase>();
|
||||
treeView.DataContext = this;
|
||||
}
|
||||
|
||||
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
|
||||
{
|
||||
if (e.NewValue is NodeModelBase node)
|
||||
{
|
||||
// 在这里设置 Expanded 事件
|
||||
var treeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(node);
|
||||
treeViewItem.Expanded += (s, args) => LoadChildren(node, treeViewItem);
|
||||
}
|
||||
}
|
||||
private void LoadChildren(NodeModelBase node, TreeViewItem treeViewItem)
|
||||
{
|
||||
// 懒加载逻辑
|
||||
if (node.SuccessorNodes.Count > 0)
|
||||
{
|
||||
treeViewItem.Items.Clear();
|
||||
foreach (var child in node.SuccessorNodes[ConnectionType.IsSucceed]) // 根据类型加载子项
|
||||
{
|
||||
treeViewItem.Items.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
WorkBench/Themes/NodeTreeView.cs
Normal file
71
WorkBench/Themes/NodeTreeView.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Serein.NodeFlow.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
using Serein.Library.Enums;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
public class NodeTreeView : TreeView
|
||||
{
|
||||
public NodeTreeView()
|
||||
{
|
||||
this.ItemContainerGenerator.StatusChanged += OnStatusChanged;
|
||||
}
|
||||
|
||||
private void OnStatusChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
|
||||
{
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var treeViewItem = (TreeViewItem)this.ItemContainerGenerator.ContainerFromItem(item);
|
||||
if (treeViewItem != null)
|
||||
{
|
||||
treeViewItem.Expanded += TreeViewItem_Expanded;
|
||||
ApplyColor(treeViewItem, item as NodeModelBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is TreeViewItem item && item.DataContext is NodeModelBase node)
|
||||
{
|
||||
if (item.Items.Count == 0) // 懒加载
|
||||
{
|
||||
foreach (var childNode in node.SuccessorNodes[ConnectionType.Upstream])
|
||||
{
|
||||
item.Items.Add(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyColor(TreeViewItem item, NodeModelBase node)
|
||||
{
|
||||
// 根据 ControlType 设置颜色
|
||||
switch (node.ControlType)
|
||||
{
|
||||
case NodeControlType.Flipflop:
|
||||
item.Background = Brushes.LightGreen;
|
||||
break;
|
||||
case NodeControlType.Action:
|
||||
item.Background = Brushes.LightCoral;
|
||||
break;
|
||||
// 添加更多条件
|
||||
default:
|
||||
item.Background = Brushes.Transparent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using Serein.Library.Api;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@@ -14,6 +16,7 @@ using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Markup.Primitives;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
@@ -54,73 +57,83 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
public partial class ObjectViewerControl : UserControl
|
||||
{
|
||||
private object _objectInstance;
|
||||
public string NodeGuid { get;set; }
|
||||
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
public IFlowEnvironment FlowEnvironment { get;set; }
|
||||
public NodeModelBase NodeModel { get;set; }
|
||||
|
||||
public ObjectViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private DateTime _lastRefreshTime = DateTime.MinValue; // 上次刷新时间
|
||||
private TimeSpan _refreshInterval = TimeSpan.FromSeconds(0.1); // 刷新间隔(2秒)
|
||||
/// <summary>
|
||||
/// 监视类型
|
||||
/// </summary>
|
||||
public enum MonitorType
|
||||
{
|
||||
/// <summary>
|
||||
/// 作用于对象(对象的引用)的监视
|
||||
/// </summary>
|
||||
NodeFlowData,
|
||||
/// <summary>
|
||||
/// 作用与节点(FLowData)的监视
|
||||
/// </summary>
|
||||
IOCObj,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment FlowEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视对象的键
|
||||
/// </summary>
|
||||
public object MonitorKey { get => monitorKey; }
|
||||
/// <summary>
|
||||
/// 正在监视的对象
|
||||
/// </summary>
|
||||
public object MonitorObj { get => monitorObj; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视表达式
|
||||
/// </summary>
|
||||
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
|
||||
private object monitorKey;
|
||||
private object monitorObj;
|
||||
|
||||
// 用于存储当前展开的节点路径
|
||||
private HashSet<string> expandedNodePaths = new HashSet<string>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加载对象信息,展示其成员
|
||||
/// </summary>
|
||||
/// <param name="obj">要展示的对象</param>
|
||||
public void LoadObjectInformation(object obj)
|
||||
public void LoadObjectInformation(object key, object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return;
|
||||
|
||||
|
||||
// 当前时间
|
||||
var currentTime = DateTime.Now;
|
||||
|
||||
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
|
||||
if (currentTime - _lastRefreshTime < _refreshInterval)
|
||||
{
|
||||
// 跳过过于频繁的刷新调用
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录这次的刷新时间
|
||||
_lastRefreshTime = currentTime;
|
||||
|
||||
_objectInstance = obj;
|
||||
RefreshObjectTree(obj);
|
||||
if (obj == null) return;
|
||||
monitorKey = key;
|
||||
monitorObj = obj;
|
||||
expandedNodePaths.Clear();
|
||||
LoadTree(obj);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 添加表达式
|
||||
///// </summary>
|
||||
///// <param name="sender"></param>
|
||||
///// <param name="e"></param>
|
||||
//private void AddMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
|
||||
// OpenInputDialog((exp) =>
|
||||
// {
|
||||
// FlowEnvironment.AddInterruptExpression(NodeGuid, exp);
|
||||
// });
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 刷新对象
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//RefreshObjectTree(_objectInstance);
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(NodeGuid, true);
|
||||
RefreshObjectTree(monitorObj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新表达式
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//MonitorExpression = ExpressionTextBox.Text.ToString();
|
||||
|
||||
if(FlowEnvironment.AddInterruptExpression(NodeGuid, MonitorExpression))
|
||||
if (FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
|
||||
{
|
||||
if (string.IsNullOrEmpty(MonitorExpression))
|
||||
{
|
||||
@@ -131,98 +144,72 @@ namespace Serein.WorkBench.Themes
|
||||
UpMonitorExpressionButton.Content = "更新监视表达式";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 用于存储当前展开的节点路径
|
||||
private HashSet<string> _expandedNodePaths = new HashSet<string>();
|
||||
private TreeViewItem? LoadTree(object obj)
|
||||
{
|
||||
if (obj is null) return null;
|
||||
var objectType = obj.GetType();
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
{
|
||||
Name = objectType.Name,
|
||||
DataType = objectType,
|
||||
DataValue = obj,
|
||||
DataPath = ""
|
||||
};
|
||||
var rootNode = new TreeViewItem
|
||||
{
|
||||
Header = objectType.Name,
|
||||
Tag = flowDataDetails,
|
||||
};
|
||||
|
||||
|
||||
ObjectTreeView.Items.Clear(); // 移除对象树的所有节点
|
||||
ObjectTreeView.Items.Add(rootNode); // 添加所有节点
|
||||
rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
|
||||
rootNode.Collapsed += TreeViewItem_Collapsed; // 监听折叠事件
|
||||
// 这里创建了一个子项,并给这个子项创建了“正在加载”的子项
|
||||
// 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去
|
||||
// 绑定了展开/折叠事件后,自动展开第一层,开始反射obj的成员,并判断obj的成员生成什么样的节点
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新对象属性树
|
||||
/// </summary>
|
||||
public void RefreshObjectTree(object obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return;
|
||||
// 当前时间
|
||||
var currentTime = DateTime.Now;
|
||||
|
||||
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
|
||||
if (currentTime - _lastRefreshTime < _refreshInterval)
|
||||
monitorObj = obj;
|
||||
var rootNode = LoadTree(obj);
|
||||
if (rootNode is not null)
|
||||
{
|
||||
// 跳过过于频繁的刷新调用
|
||||
return;
|
||||
ExpandPreviouslyExpandedNodes(rootNode); // 遍历节点,展开之前记录的节点
|
||||
|
||||
}
|
||||
|
||||
// 记录这次的刷新时间
|
||||
_lastRefreshTime = currentTime;
|
||||
|
||||
var objectType = obj.GetType();
|
||||
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
{
|
||||
Name = objectType.Name,
|
||||
DataType = objectType,
|
||||
DataValue = obj
|
||||
};
|
||||
var rootNode = new TreeViewItem
|
||||
{
|
||||
Header = objectType.Name,
|
||||
Tag = flowDataDetails,
|
||||
};
|
||||
|
||||
// 添加占位符节点
|
||||
AddPlaceholderNode(rootNode);
|
||||
ObjectTreeView.Items.Clear();
|
||||
ObjectTreeView.Items.Add(rootNode);
|
||||
|
||||
// 监听展开事件
|
||||
rootNode.Expanded += TreeViewItem_Expanded;
|
||||
|
||||
// 自动展开第一层
|
||||
rootNode.IsExpanded = true; // 直接展开根节点
|
||||
|
||||
// 加载根节点的属性和字段
|
||||
if (rootNode.Items.Count == 1 && rootNode.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
|
||||
{
|
||||
rootNode.Items.Clear();
|
||||
AddMembersToTreeNode(rootNode, obj, objectType);
|
||||
}
|
||||
// 遍历节点,展开之前记录的节点
|
||||
ExpandPreviouslyExpandedNodes(rootNode);
|
||||
|
||||
}
|
||||
|
||||
// 遍历并展开之前记录的节点
|
||||
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
|
||||
/// <summary>
|
||||
/// 展开父节点,如果路径存在哈希记录,则将其自动展开,并递归展开后的子节点。
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
|
||||
{
|
||||
if (_expandedNodePaths.Contains(GetNodeFullPath(node)))
|
||||
if (node == null) return;
|
||||
if(node.Tag is FlowDataDetails flowDataDetails)
|
||||
{
|
||||
node.IsExpanded = true;
|
||||
if (expandedNodePaths.Contains(flowDataDetails.DataPath))
|
||||
{
|
||||
node.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (TreeViewItem child in node.Items)
|
||||
{
|
||||
ExpandPreviouslyExpandedNodes(child);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加父节点
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
private void AddPlaceholderNode(TreeViewItem node)
|
||||
{
|
||||
node.Items.Add(new TreeViewItem { Header = "Loading..." });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展开子项事件
|
||||
/// </summary>
|
||||
@@ -231,19 +218,45 @@ namespace Serein.WorkBench.Themes
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (TreeViewItem)sender;
|
||||
|
||||
if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
|
||||
|
||||
|
||||
// 判断有没有加载过
|
||||
//if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
|
||||
if (item.Items.Count == 0)
|
||||
{
|
||||
item.Items.Clear();
|
||||
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
|
||||
{
|
||||
// 记录当前节点的路径
|
||||
_expandedNodePaths.Add(GetNodeFullPath(item));
|
||||
var path = flowDataDetails.DataPath;
|
||||
expandedNodePaths.Add(path);
|
||||
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 折叠事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (TreeViewItem)sender;
|
||||
|
||||
if (item.Items.Count > 0)
|
||||
{
|
||||
if (item.Tag is FlowDataDetails flowDataDetails)
|
||||
{
|
||||
// 记录当前节点的路径
|
||||
var path = flowDataDetails.DataPath;
|
||||
if(path != "")
|
||||
{
|
||||
expandedNodePaths.Remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反射对象数据添加子节点
|
||||
/// </summary>
|
||||
@@ -252,81 +265,168 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="type"></param>
|
||||
private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
|
||||
{
|
||||
// 获取属性和字段
|
||||
// 获取公开的属性
|
||||
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member);
|
||||
treeViewNode.Items.Add(memberNode);
|
||||
if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
||||
if (member.Name.StartsWith(".") ||
|
||||
member.Name.StartsWith("get_") ||
|
||||
member.Name.StartsWith("set_")
|
||||
)
|
||||
{
|
||||
memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
|
||||
// 跳过构造函数、属性的get/set方法
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
|
||||
if (memberNode != null)
|
||||
{
|
||||
treeViewNode.Items.Add(memberNode); // 添加到当前节点
|
||||
|
||||
// 配置数据路径
|
||||
FlowDataDetails subFlowDataDetails = (FlowDataDetails)memberNode.Tag;
|
||||
string superPath = ((FlowDataDetails)treeViewNode.Tag).DataPath;
|
||||
string subPath = superPath + "." + subFlowDataDetails.Name;
|
||||
subFlowDataDetails.DataPath = subPath;
|
||||
|
||||
// 配置右键菜单
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
ExpressionTextBox.Text = subPath; // 获取表达式
|
||||
|
||||
}));
|
||||
memberNode.ContextMenu = contextMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置右键菜单功能
|
||||
/// 配置节点子项
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
|
||||
#region 属性
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
|
||||
string propertyValue = GetPropertyValue(obj, property,out object value);
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
|
||||
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.GetValue(obj) is IEnumerable collection && collection is not null)
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = value,
|
||||
DataPath = GetNodeFullPath(memberNode),
|
||||
};
|
||||
// 处理集合类型的属性
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.IEnumerable,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = collection,
|
||||
};
|
||||
|
||||
memberNode.Tag = flowDataDetails;
|
||||
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
||||
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
{
|
||||
AddPlaceholderNode(memberNode);
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
int index = 0;
|
||||
foreach (var item in collection)
|
||||
{
|
||||
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Item,
|
||||
DataType = item?.GetType(),
|
||||
Name = property.Name,
|
||||
DataValue = itemNode,
|
||||
};
|
||||
memberNode.Items.Add(itemNode);
|
||||
}
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} [{index}]";
|
||||
return memberNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
string propertyValue = GetPropertyValue(obj, property, out object value);
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = value,
|
||||
}; ;
|
||||
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
||||
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
#region 字段
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
|
||||
string fieldValue = GetFieldValue(obj, field, out object value);
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
if (typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.GetValue(obj) is IEnumerable collection && collection is not null)
|
||||
{
|
||||
ItemType = TreeItemType.Field,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = value,
|
||||
DataPath = GetNodeFullPath(memberNode),
|
||||
};
|
||||
|
||||
memberNode.Tag = flowDataDetails;
|
||||
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
||||
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
// 处理集合类型的字段
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.IEnumerable,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = collection,
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in collection)
|
||||
{
|
||||
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Item,
|
||||
DataType = item?.GetType(),
|
||||
Name = field.Name,
|
||||
DataValue = itemNode,
|
||||
};
|
||||
//collectionNode.Items.Add(itemNode);
|
||||
memberNode.Items.Add(itemNode);
|
||||
}
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} [{index}]";
|
||||
return memberNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPlaceholderNode(memberNode);
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
|
||||
string fieldValue = GetFieldValue(obj, field, out object value);
|
||||
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Field,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = value,
|
||||
|
||||
};
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
||||
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region 返回null
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
return memberNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -335,15 +435,12 @@ 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
|
||||
{
|
||||
|
||||
var properties = obj.GetType().GetProperties();
|
||||
|
||||
|
||||
|
||||
// 获取实例属性值
|
||||
value = property.GetValue(obj);
|
||||
return value?.ToString() ?? "null"; // 返回值或“null”
|
||||
@@ -355,14 +452,13 @@ namespace Serein.WorkBench.Themes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取字段类型的成员
|
||||
/// </summary>
|
||||
/// <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
|
||||
{
|
||||
@@ -376,165 +472,170 @@ namespace Serein.WorkBench.Themes
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据成员类别配置右键菜单
|
||||
/// </summary>
|
||||
/// <param name="memberNode"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="contextMenu"></param>
|
||||
/// <returns></returns>
|
||||
private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
||||
{
|
||||
bool isChange = false;
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = /*"@Get " + */fullPath;
|
||||
ExpressionTextBox.Text = copyValue;
|
||||
// Clipboard.SetDataObject(copyValue);
|
||||
|
||||
}));
|
||||
}
|
||||
else if (member is MethodInfo method)
|
||||
{
|
||||
//isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
}
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = /*"@Get " +*/ fullPath;
|
||||
ExpressionTextBox.Text = copyValue;
|
||||
// Clipboard.SetDataObject(copyValue);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMenu = new ContextMenu();
|
||||
}
|
||||
return isChange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的完整路径,例如 "node1.node2.node3.node4"
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>节点路径</returns>
|
||||
private string GetNodeFullPath(TreeViewItem node)
|
||||
{
|
||||
if (node == null)
|
||||
return string.Empty;
|
||||
|
||||
FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag;
|
||||
var parent = GetParentTreeViewItem(node);
|
||||
if (parent != null)
|
||||
{
|
||||
// 递归获取父节点的路径,并拼接当前节点的 Header
|
||||
return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有父节点,则说明这是根节点,直接返回 Header
|
||||
return "";
|
||||
// return typeNodeDetails.Name.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定节点的父级节点
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
{
|
||||
DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
while (parent != null && !(parent is TreeViewItem))
|
||||
{
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return parent as TreeViewItem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private InputDialog OpenInputDialog(Action<string> action)
|
||||
{
|
||||
var inputDialog = new InputDialog();
|
||||
inputDialog.Closed += (s, e) =>
|
||||
{
|
||||
if (inputDialog.DialogResult == true)
|
||||
{
|
||||
string userInput = inputDialog.InputValue;
|
||||
action?.Invoke(userInput);
|
||||
}
|
||||
};
|
||||
inputDialog.ShowDialog();
|
||||
return inputDialog;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 刷新按钮的点击事件
|
||||
///// </summary>
|
||||
//private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// RefreshObjectTree();
|
||||
//}
|
||||
|
||||
//private bool IsTimerRefres = false;
|
||||
//private void TimerRefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// IsTimerRefres = false;
|
||||
// TimerRefreshButton.Content = "定时刷新";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// IsTimerRefres = true;
|
||||
// TimerRefreshButton.Content = "取消刷新";
|
||||
|
||||
// _ = Task.Run(async () => {
|
||||
// while (true)
|
||||
// {
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// Application.Current.Dispatcher.Invoke(() =>
|
||||
// {
|
||||
// RefreshObjectTree(); // 刷新UI
|
||||
// });
|
||||
// await Task.Delay(100);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// IsTimerRefres = false;
|
||||
// });
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上次刷新事件
|
||||
/// </summary>
|
||||
//private DateTime lastRefreshTime = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// 刷新间隔
|
||||
/// </summary>
|
||||
//private readonly TimeSpan refreshInterval = TimeSpan.FromSeconds(0.1);
|
||||
// 当前时间
|
||||
//var currentTime = DateTime.Now;
|
||||
//if (currentTime - lastRefreshTime < refreshInterval)
|
||||
//{
|
||||
// return; // 跳过过于频繁的刷新调用
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// lastRefreshTime = currentTime;// 记录这次的刷新时间
|
||||
//}
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// 从当前节点获取至父节点的路径,例如 "node1.node2.node3.node4"
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>节点路径</returns>
|
||||
//private string GetNodeFullPath(TreeViewItem node)
|
||||
//{
|
||||
// if (node == null)
|
||||
// return string.Empty;
|
||||
|
||||
// FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag;
|
||||
// var parent = GetParentTreeViewItem(node);
|
||||
// if (parent != null)
|
||||
// {
|
||||
// // 递归获取父节点的路径,并拼接当前节点的 Header
|
||||
// return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 没有父节点,则说明这是根节点,直接返回 Header
|
||||
// return "";
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定节点的父级节点
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
//private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
//{
|
||||
// DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
// while (parent != null && !(parent is TreeViewItem))
|
||||
// {
|
||||
// parent = VisualTreeHelper.GetParent(parent);
|
||||
// }
|
||||
// return parent as TreeViewItem;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据成员类别配置右键菜单
|
||||
/// </summary>
|
||||
/// <param name="memberNode"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="contextMenu"></param>
|
||||
/// <returns></returns>
|
||||
//private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
||||
//{
|
||||
// if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
||||
// {
|
||||
// memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
// }
|
||||
|
||||
// bool isChange = false;
|
||||
// if (member is PropertyInfo property)
|
||||
// {
|
||||
// isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
// {
|
||||
// string fullPath = GetNodeFullPath(memberNode);
|
||||
// string copyValue = /*"@Get " + */fullPath;
|
||||
// ExpressionTextBox.Text = copyValue;
|
||||
// // Clipboard.SetDataObject(copyValue);
|
||||
|
||||
// }));
|
||||
// }
|
||||
// else if (member is MethodInfo method)
|
||||
// {
|
||||
// //isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// }
|
||||
// else if (member is FieldInfo field)
|
||||
// {
|
||||
// isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
// {
|
||||
// string fullPath = GetNodeFullPath(memberNode);
|
||||
// string copyValue = /*"@Get " +*/ fullPath;
|
||||
// ExpressionTextBox.Text = copyValue;
|
||||
// // Clipboard.SetDataObject(copyValue);
|
||||
// }));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// contextMenu = new ContextMenu();
|
||||
// }
|
||||
// return isChange;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 刷新按钮的点击事件
|
||||
///// </summary>
|
||||
//private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// RefreshObjectTree();
|
||||
//}
|
||||
|
||||
//private bool IsTimerRefres = false;
|
||||
//private void TimerRefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// IsTimerRefres = false;
|
||||
// TimerRefreshButton.Content = "定时刷新";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// IsTimerRefres = true;
|
||||
// TimerRefreshButton.Content = "取消刷新";
|
||||
|
||||
// _ = Task.Run(async () => {
|
||||
// while (true)
|
||||
// {
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// Application.Current.Dispatcher.Invoke(() =>
|
||||
// {
|
||||
// RefreshObjectTree(); // 刷新UI
|
||||
// });
|
||||
// await Task.Delay(100);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// IsTimerRefres = false;
|
||||
// });
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
@@ -267,7 +267,9 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
Property,
|
||||
Method,
|
||||
Field
|
||||
Field,
|
||||
IEnumerable,
|
||||
Item,
|
||||
}
|
||||
|
||||
|
||||
|
||||
23
WorkBench/Tool/Converters/TypeToColorConverter.cs
Normal file
23
WorkBench/Tool/Converters/TypeToColorConverter.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Serein.Library.Enums;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Serein.WorkBench.Tool.Converters
|
||||
{
|
||||
public class TypeToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// 根据 ControlType 返回颜色
|
||||
return value switch
|
||||
{
|
||||
NodeControlType.Action => Brushes.Blue,
|
||||
NodeControlType.Flipflop => Brushes.Green,
|
||||
_ => Brushes.Black,
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user