mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-15 20:36:35 +08:00
添加Avalonia项目的demo
This commit is contained in:
217
Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs
Normal file
217
Serein.Workbench.Avalonia/Custom/Views/ConnectionLineShape.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Skia;
|
||||
using Avalonia.Styling;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Workbench.Avalonia.Extension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views
|
||||
{
|
||||
public class ConnectionLineShape : Control
|
||||
{
|
||||
private readonly double strokeThickness;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 确定起始坐标和目标坐标、外光样式的曲线
|
||||
/// </summary>
|
||||
/// <param name="start">起始坐标</param>
|
||||
/// <param name="end">结束坐标</param>
|
||||
/// <param name="brush">颜色</param>
|
||||
/// <param name="isDotted">是否为虚线</param>
|
||||
public ConnectionLineShape(Point start,
|
||||
Point end,
|
||||
Brush brush,
|
||||
bool isDotted = false,
|
||||
bool isTop = false)
|
||||
{
|
||||
this.brush = brush;
|
||||
startPoint = start;
|
||||
endPoint = end;
|
||||
this.strokeThickness = 4;
|
||||
InitElementPoint(isDotted, isTop);
|
||||
InvalidateVisual(); // 触发重绘
|
||||
}
|
||||
|
||||
|
||||
public void InitElementPoint(bool isDotted, bool isTop = false)
|
||||
{
|
||||
//hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
||||
//hitVisiblePen.Freeze(); // Freeze以提高性能
|
||||
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
|
||||
opacity = 1.0d;
|
||||
var dashStyle = new DashStyle();
|
||||
|
||||
if (isDotted)
|
||||
{
|
||||
opacity = 0.42d;
|
||||
visualPen.DashStyle = new DashStyle(); // DashStyles.Dash; // 选择虚线样式
|
||||
}
|
||||
//visualPen.Freeze(); // Freeze以提高性能
|
||||
|
||||
linkSize = 4; // 整线条粗细
|
||||
int zIndex = 999999;
|
||||
|
||||
this.ZIndex = zIndex;
|
||||
//Panel.SetZIndex(this, zIndex); // 置底
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新线条落点位置
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="end"></param>
|
||||
public void UpdatePoints(Point start, Point end)
|
||||
{
|
||||
startPoint = start;
|
||||
endPoint = end;
|
||||
InvalidateVisual(); // 触发重绘
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新线条落点位置
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
public void UpdateEndPoints(Point point)
|
||||
{
|
||||
endPoint = point;
|
||||
InvalidateVisual(); // 触发重绘
|
||||
}
|
||||
/// <summary>
|
||||
/// 更新线条起点位置
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
public void UpdateStartPoints(Point point)
|
||||
{
|
||||
startPoint = point;
|
||||
InvalidateVisual(); // 触发重绘
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件重绘事件
|
||||
/// </summary>
|
||||
/// <param name="drawingContext"></param>
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
// 刷新线条显示位置
|
||||
DrawBezierCurve(drawingContext, startPoint, endPoint);
|
||||
|
||||
}
|
||||
#region 重绘
|
||||
|
||||
private StreamGeometry streamGeometry = new StreamGeometry();
|
||||
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||
//private Pen hitVisiblePen; // 初始化碰撞检测线
|
||||
private Pen visualPen; // 默认可视化Pen
|
||||
private Point startPoint; // 连接线的起始节点
|
||||
private Point endPoint; // 连接线的终点
|
||||
private Brush brush; // 线条颜色
|
||||
private double opacity; // 透明度
|
||||
|
||||
double linkSize; // 根据缩放比例调整线条粗细
|
||||
|
||||
//public void UpdateLineColor()
|
||||
//{
|
||||
// visualPen = new Pen(brush, 3.0); // 默认可视化Pen
|
||||
// InvalidateVisual(); // 触发重绘
|
||||
//}
|
||||
|
||||
|
||||
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
||||
private Vector axis = new Vector(1, 0);
|
||||
private Vector startToEnd;
|
||||
private int i = 0;
|
||||
private void DrawBezierCurve(DrawingContext drawingContext,
|
||||
Point start,
|
||||
Point end)
|
||||
{
|
||||
// 控制点的计算逻辑
|
||||
double power = 140; // 控制贝塞尔曲线的“拉伸”强度
|
||||
drawingContext.PushOpacity(opacity);
|
||||
|
||||
// 计算轴向向量与起点到终点的向量
|
||||
//var axis = new Vector(1, 0);
|
||||
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
||||
|
||||
|
||||
|
||||
//var dp = axis.DotProduct(startToEnd);
|
||||
//dp = dp < 50 ? 50 : dp;
|
||||
//var pow = Math.Max(0, dp) ;
|
||||
//var k = 1 - Math.Pow(pow, 10.0);
|
||||
//
|
||||
//Debug.WriteLine("pow : " + pow);
|
||||
//Debug.WriteLine("k : " + k);
|
||||
//Debug.WriteLine("");
|
||||
|
||||
// 计算拉伸程度k,拉伸与水平夹角正相关
|
||||
var dp = axis.DotProduct(startToEnd) ;
|
||||
var pow = Math.Pow(dp, 10.0);
|
||||
pow = pow > 0 ? 0 : pow;
|
||||
var k = 1 - pow;
|
||||
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
||||
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
||||
// 控制点的实际计算
|
||||
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
||||
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
||||
|
||||
// 准备StreamGeometry以用于绘制曲线
|
||||
// why can't clearValue()?
|
||||
//streamGeometry.ClearValue(ThemeProperty);
|
||||
//var streamGeometry = new StreamGeometry();
|
||||
//if( i++ > 100 && streamGeometry is AvaloniaObject avaloniaObject)
|
||||
//{
|
||||
// var platformImplInfo = streamGeometry.GetType().GetProperty("PlatformImpl", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
// var platformImpl = platformImplInfo?.GetValue(streamGeometry);
|
||||
|
||||
// var pathCache = platformImpl?.GetType().GetField("_bounds", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
// if(pathCache is IDisposable disposable)
|
||||
// {
|
||||
// disposable.Dispose();
|
||||
// }
|
||||
// //pathCache?.Invoke(platformImpl, []);
|
||||
// Debug.WriteLine("invoke => InvalidateCaches");
|
||||
// i = 0;
|
||||
// //public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
|
||||
// //private readonly ValueStore _values;
|
||||
//}
|
||||
|
||||
// this is override "Render()" method
|
||||
// display a bezier-line on canvas and follow the mouse in real time
|
||||
// I don't want to re-instantiate StreamGeometry()
|
||||
// because I want to reduce the number of GC
|
||||
// but , it seems impossible to do so in avalonia
|
||||
streamGeometry = new StreamGeometry();
|
||||
// in wpf , this method allows display content to be cleared
|
||||
// streamGeometry.Clear(); // this is wpf method
|
||||
// in avalonia, why does this method need value of "AvaloniaProperty" data type ?
|
||||
// but I don't know use what "AvaloniaProperty" to clear the displayed content
|
||||
// if I don't clear the cache or re-instantiate it
|
||||
// the canvas will display repeated lines , because exits cache inside streamGeometry
|
||||
// streamGeometry.ClearValue("AvaloniaProperty");
|
||||
using (var context = streamGeometry.Open())
|
||||
{
|
||||
context.BeginFigure(start, true); // start point of the bezier-line
|
||||
context.CubicBezierTo(c0, c1, end, true); // drawing bezier-line
|
||||
}
|
||||
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
|
||||
xmlns:cv="using:Serein.Workbench.Avalonia.Custom.Views"
|
||||
xmlns:converter="using:Serein.Workbench.Avalonia.Converters"
|
||||
xmlns:dtp="using:Serein.Workbench.Avalonia.DataTemplates"
|
||||
>
|
||||
|
||||
<!--预览-->
|
||||
<Design.PreviewWith>
|
||||
<StackPanel Width="400" Spacing="10">
|
||||
<StackPanel Background="{DynamicResource SystemRegionBrush}">
|
||||
<cv:FlowLibraryInfoView />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<ControlTheme x:Key="{x:Type cv:FlowLibraryInfoView}" TargetType="cv:FlowLibraryInfoView">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<StackPanel>
|
||||
<!--类库名称-->
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="2">
|
||||
<TextBlock Text="library : " FontSize="18"></TextBlock>
|
||||
<TextBlock Text="{TemplateBinding LibraryName}" FontSize="18"></TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!--Action Method Info-->
|
||||
<!--动作节点方法信息-->
|
||||
<ListBox x:Name="PART_ActionMethodInfos" ItemsSource="{TemplateBinding ActionMethods}" Background="#F1FBFB">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<!--use custom DataTemplate create items -->
|
||||
<!--使用自定义模板创建子项控件-->
|
||||
<dtp:LibraryMethodInfoDataTemplate>
|
||||
</dtp:LibraryMethodInfoDataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<!--Flipflop Method Info-->
|
||||
<!--触发器节点方法信息-->
|
||||
<ListBox x:Name="PART_FlipflopMethodInfos" ItemsSource="{TemplateBinding FlipflopMethods}" Background="#FBF8F1">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<dtp:LibraryMethodInfoDataTemplate>
|
||||
</dtp:LibraryMethodInfoDataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.VisualTree;
|
||||
using Serein.Library;
|
||||
using Serein.Workbench.Avalonia.Services;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
public partial class FlowLibraryInfoView : TemplatedControl
|
||||
{
|
||||
|
||||
private IWorkbenchEventService workbenchEventService;
|
||||
|
||||
public FlowLibraryInfoView()
|
||||
{
|
||||
workbenchEventService = App.GetService<IWorkbenchEventService>();
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
base.OnPropertyChanged(change);
|
||||
// 如果改变的属性是 MdsProperty ,则加载方法信息
|
||||
if (change.Property == MdsProperty)
|
||||
{
|
||||
if(change.NewValue is MethodDetailsInfo[] value)
|
||||
{
|
||||
onNext(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取到控件信息
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
#region 动作节点方法信息
|
||||
if (e.NameScope.Find("PART_ActionMethodInfos") is ListBox p_am)
|
||||
{
|
||||
p_am.AddHandler(InputElement.PointerExitedEvent, ListBox_PointerExited);
|
||||
p_am.AddHandler(SelectingItemsControl.SelectionChangedEvent, ListBox_SelectionChanged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 触发器节点方法信息
|
||||
if (e.NameScope.Find("PART_FlipflopMethodInfos") is ListBox p_fm)
|
||||
{
|
||||
p_fm.SelectionChanged += ListBox_SelectionChanged;
|
||||
p_fm.PointerExited += ListBox_PointerExited;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
private void ListBox_SelectionChanged(object? o, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (o is ListBox listBox && listBox.SelectedIndex > 0 && listBox.SelectedItem is MethodDetailsInfo mdInfo)
|
||||
{
|
||||
workbenchEventService.PreviewLibraryMethodInfo(mdInfo); // 通知其它地方预览了某个方法信息
|
||||
}
|
||||
}
|
||||
private void ListBox_PointerExited(object? o, PointerEventArgs e)
|
||||
{
|
||||
if (o is ListBox listBox && listBox.SelectedIndex > -1)
|
||||
{
|
||||
listBox.SelectedIndex = -1; // 如果鼠标离开了,取消已选状态
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将信息加载出来
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
private void onNext(MethodDetailsInfo[] value)
|
||||
{
|
||||
if(value is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var fmd = value.Where(item => nameof(NodeType.Flipflop).Equals(item.NodeType));
|
||||
FlipflopMethods = new ObservableCollection<MethodDetailsInfo>(fmd);
|
||||
var amd = value.Where(item => nameof(NodeType.Action).Equals(item.NodeType));
|
||||
ActionMethods = new ObservableCollection<MethodDetailsInfo>(amd);
|
||||
}
|
||||
|
||||
|
||||
#region Template Public Property / 控件公开属性
|
||||
public static readonly DirectProperty<FlowLibraryInfoView, string> LibraryNameProperty =
|
||||
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, string>(nameof(LibraryName), o => o.LibraryName, (o, v) => o.LibraryName = v);
|
||||
public static readonly DirectProperty<FlowLibraryInfoView, MethodDetailsInfo[]> MdsProperty =
|
||||
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, MethodDetailsInfo[]>(nameof(Mds), o => o.Mds, (o, v) => o.Mds = v);
|
||||
public static readonly DirectProperty<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>> ActionMethodsProperty =
|
||||
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>>(nameof(ActionMethods), o => o.ActionMethods, (o, v) => o.ActionMethods = v);
|
||||
public static readonly DirectProperty<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>> FlipflopMethodsProperty =
|
||||
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>>(nameof(FlipflopMethods), o => o.FlipflopMethods, (o, v) => o.FlipflopMethods = v);
|
||||
|
||||
private string libraryName = string.Empty;
|
||||
private ObservableCollection<MethodDetailsInfo> actionMethods;
|
||||
private ObservableCollection<MethodDetailsInfo> flipflopMethods;
|
||||
private MethodDetailsInfo[] mds = [];
|
||||
|
||||
|
||||
public string LibraryName
|
||||
{
|
||||
get { return libraryName; }
|
||||
set { SetAndRaise(LibraryNameProperty, ref libraryName, value); }
|
||||
}
|
||||
|
||||
/*
|
||||
public static readonly AttachedProperty<string> LibraryName2Property = AvaloniaProperty.RegisterAttached<FlowLibraryInfoView, Control, string>("LibraryName2");
|
||||
|
||||
public static string GetLibraryName2(Control element)
|
||||
{
|
||||
return element.GetValue(LibraryName2Property);
|
||||
}
|
||||
|
||||
public static void SetLibraryName2(Control element, string value)
|
||||
{
|
||||
element.SetValue(LibraryName2Property, value);
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Method Info
|
||||
/// 方法信息
|
||||
/// </summary>
|
||||
public MethodDetailsInfo[] Mds
|
||||
{
|
||||
get { return mds; }
|
||||
set
|
||||
{
|
||||
SetAndRaise(MdsProperty, ref mds, value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 动作节点的方法
|
||||
/// </summary>
|
||||
public ObservableCollection<MethodDetailsInfo> ActionMethods
|
||||
{
|
||||
get { return actionMethods; }
|
||||
set
|
||||
{
|
||||
SetAndRaise(ActionMethodsProperty, ref actionMethods, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发器节点的方法
|
||||
/// </summary>
|
||||
public ObservableCollection<MethodDetailsInfo> FlipflopMethods
|
||||
{
|
||||
get { return flipflopMethods; }
|
||||
set
|
||||
{
|
||||
|
||||
SetAndRaise(FlipflopMethodsProperty, ref flipflopMethods, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
public class ItemsChangeObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
|
||||
{
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
RegisterPropertyChanged(e.NewItems);
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
UnRegisterPropertyChanged(e.OldItems);
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
UnRegisterPropertyChanged(e.OldItems);
|
||||
RegisterPropertyChanged(e.NewItems);
|
||||
}
|
||||
|
||||
|
||||
base.OnCollectionChanged(e);
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
UnRegisterPropertyChanged(this);
|
||||
base.ClearItems();
|
||||
}
|
||||
private void RegisterPropertyChanged(IList items)
|
||||
{
|
||||
foreach (INotifyPropertyChanged item in items)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void UnRegisterPropertyChanged(IList items)
|
||||
{
|
||||
foreach (INotifyPropertyChanged item in items)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
//launch an event Reset with name of property changed
|
||||
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Serein.Workbench.Avalonia.Custom.Views.FlowLibraryMethodInfoView"
|
||||
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
|
||||
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
|
||||
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
|
||||
x:DataType="vm:FlowLibraryMethodInfoViewModel">
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:FlowLibraryMethodInfoViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<UserControl.Styles>
|
||||
<Style Selector="StackPanel">
|
||||
<Setter Property="Margin" Value="2,1,2,1" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="节点类型 - "/>
|
||||
<TextBlock Text="{Binding MethodDetailsInfo.NodeType}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="方法描述 - "/>
|
||||
<TextBlock Text="{Binding MethodDetailsInfo.MethodAnotherName}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="方法名称 - "/>
|
||||
<TextBlock Text="{Binding MethodDetailsInfo.MethodName}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text=" 返 回 值 - "/>
|
||||
<TextBlock Text="{Binding MethodDetailsInfo.ReturnTypeFullName}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Serein.Library;
|
||||
using Serein.Workbench.Avalonia.Custom.ViewModels;
|
||||
using Serein.Workbench.Avalonia.Custom.Views;
|
||||
using Serein.Workbench.Avalonia.Services;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
public partial class FlowLibraryMethodInfoView : UserControl
|
||||
{
|
||||
private FlowLibraryMethodInfoViewModel _vm;
|
||||
public FlowLibraryMethodInfoView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = App.GetService<FlowLibraryMethodInfoViewModel>();
|
||||
DataContext = _vm;
|
||||
//this.PointerPressed += FlowLibraryMethodInfoView_PointerPressed;
|
||||
}
|
||||
|
||||
//private async void FlowLibraryMethodInfoView_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
//{
|
||||
// if (_vm.MethodDetailsInfo is null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// DataObject dragData = new DataObject();
|
||||
// dragData.Set(DataFormats.Text, $"{_vm.MethodDetailsInfo.MethodAnotherName}");
|
||||
// var result = await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
|
||||
// Debug.WriteLine("DoDrag :" + result);
|
||||
// switch (result)
|
||||
// {
|
||||
// case DragDropEffects.Copy:
|
||||
// Debug.WriteLine("文本来自 Copy");
|
||||
// break;
|
||||
// case DragDropEffects.Link:
|
||||
// Debug.WriteLine("文本来自 Link");
|
||||
// break;
|
||||
// case DragDropEffects.None:
|
||||
// Debug.WriteLine("拖拽操作被取消");
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Serein.Workbench.Avalonia.Custom.Views.FlowLibrarysView"
|
||||
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
|
||||
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
|
||||
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
|
||||
x:DataType="vm:FlowLibrarysViewModel"
|
||||
>
|
||||
<Design.DataContext>
|
||||
<vm:FlowLibrarysViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<UserControl.Resources>
|
||||
<cv:FlowLibraryInfoView x:Key="FlowLibraryInfoView">
|
||||
</cv:FlowLibraryInfoView>
|
||||
</UserControl.Resources>
|
||||
|
||||
<!-- , DataType={x:Type vm:FlowLibrarysViewModel} -->
|
||||
<!-- x:DataType="baselibrary:LibraryMds" -->
|
||||
<!-- LibraryInfo="{Binding}"-->
|
||||
<ScrollViewer HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<!--Displays dependecy information loaded from runtime environment-->
|
||||
<!--显示从运行环境加载的所有依赖信息-->
|
||||
<ItemsControl ItemsSource="{Binding LibraryList}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="baselibrary:LibraryMds" >
|
||||
<cv:FlowLibraryInfoView LibraryName="{Binding AssemblyName}" Mds="{Binding Mds}"/>
|
||||
<!--<StackPanel Background="{DynamicResource SystemRegionBrush}" Margin="2,2,2,8">
|
||||
|
||||
</StackPanel>-->
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,17 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Serein.Library;
|
||||
using Serein.Workbench.Avalonia.Custom.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
public partial class FlowLibrarysView : UserControl
|
||||
{
|
||||
public FlowLibrarysView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = App.GetService<FlowLibrarysViewModel>();
|
||||
}
|
||||
}
|
||||
43
Serein.Workbench.Avalonia/Custom/Views/MainMenuBarView.axaml
Normal file
43
Serein.Workbench.Avalonia/Custom/Views/MainMenuBarView.axaml
Normal file
@@ -0,0 +1,43 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Serein.Workbench.Avalonia.Custom.Views.MainMenuBarView"
|
||||
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
|
||||
x:DataType="vm:MainMenuBarViewModel">
|
||||
<UserControl.Styles>
|
||||
<Style Selector="MenuItem">
|
||||
<Setter Property="FontSize" Value="20" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:MainMenuBarViewModel />
|
||||
</Design.DataContext>
|
||||
<StackPanel HorizontalAlignment="Center" >
|
||||
<StackPanel.Resources>
|
||||
<SolidColorBrush x:Key="MenuFlyoutBackground">#FFFFFF</SolidColorBrush>
|
||||
</StackPanel.Resources>
|
||||
<Menu Background="Transparent">
|
||||
<MenuItem Header="项目">
|
||||
<MenuItem Header="保存项目" Command="{Binding SaveProjectCommand}"/>
|
||||
<MenuItem Header="打开本地项目"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="调试">
|
||||
<MenuItem Header="运行(从起始节点)"/>
|
||||
<MenuItem Header="运行(从当前节点)"/>
|
||||
<MenuItem Header="结束流程"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="视图">
|
||||
<MenuItem Header="输出窗口"/>
|
||||
<MenuItem Header="重置画布"/>
|
||||
<MenuItem Header="定位节点"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="远程">
|
||||
<MenuItem Header="启动远程服务" />
|
||||
<MenuItem Header="连接远程环境" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,16 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Serein.Workbench.Avalonia.Custom.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
public partial class MainMenuBarView : UserControl
|
||||
{
|
||||
public MainMenuBarView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = App.GetService<MainMenuBarViewModel>();
|
||||
}
|
||||
}
|
||||
142
Serein.Workbench.Avalonia/Custom/Views/MatrixHelper.cs
Normal file
142
Serein.Workbench.Avalonia/Custom/Views/MatrixHelper.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using static System.Math;
|
||||
|
||||
namespace Avalonia.Controls.PanAndZoom;
|
||||
|
||||
/// <summary>
|
||||
/// Avalonia Matrix helper methods.
|
||||
/// </summary>
|
||||
public static class MatrixHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a translation matrix using the specified offsets.
|
||||
/// </summary>
|
||||
/// <param name="offsetX">X-coordinate offset.</param>
|
||||
/// <param name="offsetY">Y-coordinate offset.</param>
|
||||
/// <returns>The created translation matrix.</returns>
|
||||
public static Matrix Translate(double offsetX, double offsetY)
|
||||
{
|
||||
return new Matrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends a translation around the center of provided matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix to prepend translation.</param>
|
||||
/// <param name="offsetX">X-coordinate offset.</param>
|
||||
/// <param name="offsetY">Y-coordinate offset.</param>
|
||||
/// <returns>The created translation matrix.</returns>
|
||||
public static Matrix TranslatePrepend(Matrix matrix, double offsetX, double offsetY)
|
||||
{
|
||||
return Translate(offsetX, offsetY) * matrix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix that scales along the x-axis and y-axis.
|
||||
/// </summary>
|
||||
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
|
||||
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
|
||||
/// <returns>The created scaling matrix.</returns>
|
||||
public static Matrix Scale(double scaleX, double scaleY)
|
||||
{
|
||||
return new Matrix(scaleX, 0, 0, scaleY, 0.0, 0.0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix that is scaling from a specified center.
|
||||
/// </summary>
|
||||
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
|
||||
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
|
||||
/// <param name="centerX">The center X-coordinate of the scaling.</param>
|
||||
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
|
||||
/// <returns>The created scaling matrix.</returns>
|
||||
public static Matrix ScaleAt(double scaleX, double scaleY, double centerX, double centerY)
|
||||
{
|
||||
return new Matrix(scaleX, 0, 0, scaleY, centerX - (scaleX * centerX), centerY - (scaleY * centerY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends a scale around the center of provided matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix to prepend scale.</param>
|
||||
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
|
||||
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
|
||||
/// <param name="centerX">The center X-coordinate of the scaling.</param>
|
||||
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
|
||||
/// <returns>The created scaling matrix.</returns>
|
||||
public static Matrix ScaleAtPrepend(Matrix matrix, double scaleX, double scaleY, double centerX, double centerY)
|
||||
{
|
||||
return ScaleAt(scaleX, scaleY, centerX, centerY) * matrix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a translation and scale matrix using the specified offsets and scales along the x-axis and y-axis.
|
||||
/// </summary>
|
||||
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
|
||||
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
|
||||
/// <param name="offsetX">X-coordinate offset.</param>
|
||||
/// <param name="offsetY">Y-coordinate offset.</param>
|
||||
/// <returns>The created translation and scale matrix.</returns>
|
||||
public static Matrix ScaleAndTranslate(double scaleX, double scaleY, double offsetX, double offsetY)
|
||||
{
|
||||
return new Matrix(scaleX, 0.0, 0.0, scaleY, offsetX, offsetY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a skew matrix.
|
||||
/// </summary>
|
||||
/// <param name="angleX">Angle of skew along the X-axis in radians.</param>
|
||||
/// <param name="angleY">Angle of skew along the Y-axis in radians.</param>
|
||||
/// <returns>When the method completes, contains the created skew matrix.</returns>
|
||||
public static Matrix Skew(float angleX, float angleY)
|
||||
{
|
||||
return new Matrix(1.0, Tan(angleX), Tan(angleY), 1.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix that rotates.
|
||||
/// </summary>
|
||||
/// <param name="radians">Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis.</param>
|
||||
/// <returns>The created rotation matrix.</returns>
|
||||
public static Matrix Rotation(double radians)
|
||||
{
|
||||
double cos = Cos(radians);
|
||||
double sin = Sin(radians);
|
||||
return new Matrix(cos, sin, -sin, cos, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix that rotates about a specified center.
|
||||
/// </summary>
|
||||
/// <param name="angle">Angle of rotation in radians.</param>
|
||||
/// <param name="centerX">The center X-coordinate of the rotation.</param>
|
||||
/// <param name="centerY">The center Y-coordinate of the rotation.</param>
|
||||
/// <returns>The created rotation matrix.</returns>
|
||||
public static Matrix Rotation(double angle, double centerX, double centerY)
|
||||
{
|
||||
return Translate(-centerX, -centerY) * Rotation(angle) * Translate(centerX, centerY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a matrix that rotates about a specified center.
|
||||
/// </summary>
|
||||
/// <param name="angle">Angle of rotation in radians.</param>
|
||||
/// <param name="center">The center of the rotation.</param>
|
||||
/// <returns>The created rotation matrix.</returns>
|
||||
public static Matrix Rotation(double angle, Vector center)
|
||||
{
|
||||
return Translate(-center.X, -center.Y) * Rotation(angle) * Translate(center.X, center.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a point by this matrix.
|
||||
/// </summary>
|
||||
/// <param name="matrix">The matrix to use as a transformation matrix.</param>
|
||||
/// <param name="point">>The original point to apply the transformation.</param>
|
||||
/// <returns>The result of the transformation for the input point.</returns>
|
||||
public static Point TransformPoint(Matrix matrix, Point point)
|
||||
{
|
||||
return new Point(
|
||||
(point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31,
|
||||
(point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Serein.Workbench.Avalonia.Custom.Views.NodeContainerView"
|
||||
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
|
||||
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
|
||||
x:DataType="vm:NodeContainerViewModel"
|
||||
Background="#F8FBF1"
|
||||
DragDrop.AllowDrop="True">
|
||||
|
||||
<Design.DataContext>
|
||||
<vm:NodeContainerViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<DockPanel>
|
||||
<!--放置节点-->
|
||||
<Canvas x:Name="PART_NodeContainer">
|
||||
</Canvas>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,427 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.PanAndZoom;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.VisualTree;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Workbench.Avalonia.Api;
|
||||
using Serein.Workbench.Avalonia.Custom.ViewModels;
|
||||
using Serein.Workbench.Avalonia.Extension;
|
||||
using Serein.Workbench.Avalonia.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Point = Avalonia.Point;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
public partial class NodeContainerView : UserControl
|
||||
{
|
||||
private readonly NodeContainerViewModel _vm;
|
||||
private readonly INodeOperationService nodeOperationService;
|
||||
private readonly IKeyEventService keyEventService;
|
||||
|
||||
#region 与画布相关的字段
|
||||
/// <summary>
|
||||
/// 是否正在预览节点控件
|
||||
/// </summary>
|
||||
private bool IsPreviewNodeControl;
|
||||
/// <summary>
|
||||
/// 标记是否正在尝试选取控件
|
||||
/// </summary>
|
||||
private bool IsSelectControl;
|
||||
/// <summary>
|
||||
/// 标记是否正在拖动控件
|
||||
/// </summary>
|
||||
private bool IsControlDragging;
|
||||
/// <summary>
|
||||
/// 标记是否正在拖动画布
|
||||
/// </summary>
|
||||
private bool IsCanvasDragging;
|
||||
/// <summary>
|
||||
/// 标记是否正在选取节点
|
||||
/// </summary>
|
||||
private bool IsSelectDragging;
|
||||
/// <summary>
|
||||
/// 当前选取的控件
|
||||
/// </summary>
|
||||
private readonly List<Control> selectNodeControls = [];
|
||||
|
||||
/// <summary>
|
||||
/// 记录开始拖动节点控件时的鼠标位置
|
||||
/// </summary>
|
||||
private Point startControlDragPoint;
|
||||
/// <summary>
|
||||
/// 记录移动画布开始时的鼠标位置
|
||||
/// </summary>
|
||||
private Point startCanvasDragPoint;
|
||||
/// <summary>
|
||||
/// 记录开始选取节点控件时的鼠标位置
|
||||
/// </summary>
|
||||
private Point startSelectControolPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 组合变换容器
|
||||
/// </summary>
|
||||
private readonly TransformGroup canvasTransformGroup;
|
||||
/// <summary>
|
||||
/// 缩放画布
|
||||
/// </summary>
|
||||
private readonly ScaleTransform scaleTransform;
|
||||
/// <summary>
|
||||
/// 平移画布
|
||||
/// </summary>
|
||||
private readonly TranslateTransform translateTransform;
|
||||
#endregion
|
||||
|
||||
|
||||
public NodeContainerView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm= App.GetService<NodeContainerViewModel>();
|
||||
DataContext = _vm;
|
||||
|
||||
#region 获取UI相关的服务
|
||||
keyEventService = App.GetService<IKeyEventService>();
|
||||
nodeOperationService = App.GetService<INodeOperationService>();
|
||||
nodeOperationService.MainCanvas = PART_NodeContainer;
|
||||
nodeOperationService.OnNodeViewCreate += NodeOperationService_OnNodeViewCreate; // 处理事件
|
||||
keyEventService.KeyUp += (k) =>
|
||||
{
|
||||
if (k == Key.Escape)
|
||||
{
|
||||
IsCanvasDragging = false;
|
||||
IsControlDragging = false;
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region 设置UI事件
|
||||
AddHandler(DragDrop.DropEvent, Drop); // 创建节点相关
|
||||
|
||||
|
||||
PointerPressed += NodeContainerView_PointerPressed;
|
||||
PointerReleased += NodeContainerView_PointerReleased;
|
||||
PointerMoved += NodeContainerView_PointerMoved;
|
||||
PointerWheelChanged += NodeContainerView_PointerWheelChanged;
|
||||
#endregion
|
||||
|
||||
#region 初始化画布动画容器
|
||||
canvasTransformGroup = new TransformGroup();
|
||||
scaleTransform = new ScaleTransform();
|
||||
translateTransform = new TranslateTransform();
|
||||
canvasTransformGroup.Children.Add(scaleTransform);
|
||||
canvasTransformGroup.Children.Add(translateTransform);
|
||||
PART_NodeContainer.RenderTransform = canvasTransformGroup;
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 工具方法
|
||||
|
||||
public Point GetPositionOfCanvas(PointerEventArgs e)
|
||||
{
|
||||
return e.GetPosition(PART_NodeContainer);
|
||||
}
|
||||
public Point GetPositionOfCanvas(DragEventArgs e)
|
||||
{
|
||||
return e.GetPosition(PART_NodeContainer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 画布的移动、缩放、框选,以及拖拽事件
|
||||
|
||||
#region 响应拖拽事件
|
||||
private void Drop(object? sender, DragEventArgs e)
|
||||
{
|
||||
if (e.Data.Contains(DataFormats.Text))
|
||||
{
|
||||
var json = e.Data.GetText();
|
||||
if (string.IsNullOrEmpty(json))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var mdInfo = json.ToJsonObject<MethodDetailsInfo>();
|
||||
if (mdInfo is not null)
|
||||
{
|
||||
var canvasDropPosition = GetPositionOfCanvas(e); // 更新画布落点
|
||||
PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
|
||||
nodeOperationService.CreateNodeView(mdInfo, position); // 提交创建节点的请求
|
||||
}
|
||||
|
||||
}
|
||||
else // if (e.Data.Contains(DataFormats.FileNames))
|
||||
{
|
||||
var files = e.Data.GetFiles();
|
||||
var str = files?.Select(f => f.Path);
|
||||
if (str is not null)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 拖动画布
|
||||
private void NodeContainerView_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
if (IsPreviewNodeControl)
|
||||
{
|
||||
IsCanvasDragging = false;
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
if (!IsCanvasDragging)
|
||||
{
|
||||
IsCanvasDragging = true;
|
||||
startCanvasDragPoint = e.GetPosition(this);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
private void NodeContainerView_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
IsCanvasDragging = false; // 不再拖动
|
||||
}
|
||||
|
||||
private void NodeContainerView_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
|
||||
var myData = nodeOperationService.ConnectingData;
|
||||
if (myData.IsCreateing)
|
||||
{
|
||||
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsLeftButtonPressed);
|
||||
//Debug.WriteLine("canvas ispass = " + isPass);
|
||||
if (isPass)
|
||||
{
|
||||
if (myData.Type == JunctionOfConnectionType.Invoke)
|
||||
{
|
||||
_vm.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_vm.IsConnectionArgSourceNode = true; // 正在连接节点的调用关系
|
||||
}
|
||||
var currentPoint = e.GetPosition(PART_NodeContainer);
|
||||
myData.UpdatePoint(new Point(currentPoint.X - 5, currentPoint.Y - 5));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (IsCanvasDragging)
|
||||
{
|
||||
// 拖动画布
|
||||
Point currentMousePosition = e.GetPosition(this);
|
||||
double deltaX = currentMousePosition.X - startCanvasDragPoint.X;
|
||||
double deltaY = currentMousePosition.Y - startCanvasDragPoint.Y;
|
||||
translateTransform.X += deltaX;
|
||||
translateTransform.Y += deltaY;
|
||||
startCanvasDragPoint = currentMousePosition;
|
||||
}
|
||||
}
|
||||
|
||||
// 缩放
|
||||
private void NodeContainerView_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
|
||||
{
|
||||
var delta = e.Delta.Y;
|
||||
if (delta < 0 && scaleTransform.ScaleX < 0.02) return;
|
||||
if (delta > 0 && scaleTransform.ScaleY > 4.0) return;
|
||||
|
||||
// 缩放因子,根据滚轮方向调整
|
||||
double zoomFactor = delta > 0 ? 1.23 : 0.78;
|
||||
|
||||
// 当前缩放比例
|
||||
double oldScale = scaleTransform.ScaleX;
|
||||
double newScale = oldScale * zoomFactor;
|
||||
|
||||
// 记录缩放前的鼠标位置
|
||||
var mousePosition = GetPositionOfCanvas(e);
|
||||
|
||||
// 更新缩放比例
|
||||
scaleTransform.ScaleX = newScale;
|
||||
scaleTransform.ScaleY = newScale;
|
||||
|
||||
// 记录缩放后的鼠标位置
|
||||
var newMousePosition = GetPositionOfCanvas(e);
|
||||
|
||||
// 更新 TranslateTransform,确保以鼠标位置为中心进行缩放
|
||||
var s_position = newMousePosition - mousePosition; // 计算偏移量
|
||||
translateTransform.X += s_position.X * newScale; // 根据缩放比例进行偏移
|
||||
translateTransform.Y += s_position.Y * newScale; // 根据缩放比例进行偏移
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region 节点事件处理相关方法
|
||||
/// <summary>
|
||||
/// 拖拽创建控件
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <returns></returns>
|
||||
private bool NodeOperationService_OnNodeViewCreate(NodeViewCreateEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.NodeControl is not Control control)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var position = eventArgs.Position;// 坐标
|
||||
SetNodeEvent(control); // 设置该控件与画布交互的相关事件
|
||||
|
||||
DragControl(control, position.X, position.Y);
|
||||
PART_NodeContainer.Children.Add(control);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置节点与画布容器相关的操作事件
|
||||
/// </summary>
|
||||
/// <param name="nodeControl"></param>
|
||||
private void SetNodeEvent(Control nodeControl)
|
||||
{
|
||||
nodeControl.PointerMoved += NodeControl_PointerMoved; ;
|
||||
nodeControl.PointerExited += NodeControl_PointerExited;
|
||||
nodeControl.PointerPressed += Block_MouseLeftButtonDown;
|
||||
nodeControl.PointerMoved += Block_MouseMove;
|
||||
nodeControl.PointerReleased += (s, e) => IsControlDragging = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 控件交互的相关方法
|
||||
|
||||
/// <summary>
|
||||
/// 移动控件
|
||||
/// </summary>
|
||||
/// <param name="nodeControl"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
private void DragControl(Control nodeControl, double x, double y)
|
||||
{
|
||||
Canvas.SetLeft(nodeControl, x);
|
||||
Canvas.SetTop(nodeControl, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件的鼠标左键按下事件,启动拖动操作。
|
||||
/// </summary>
|
||||
private void Block_MouseLeftButtonDown(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsRightButtonPressed);
|
||||
if (!isPass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender is Control nodeControl)
|
||||
{
|
||||
IsControlDragging = true;
|
||||
startControlDragPoint = GetPositionOfCanvas(e); // 记录鼠标按下时的位置
|
||||
e.Handled = true; // 防止事件传播影响其他控件
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
||||
/// </summary>
|
||||
private void Block_MouseMove(object? sender, PointerEventArgs e)
|
||||
{
|
||||
|
||||
if (sender is not Control nodeControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsCanvasDragging)
|
||||
return;
|
||||
if (IsSelectControl)
|
||||
return;
|
||||
|
||||
if (IsControlDragging) // 如果正在拖动控件
|
||||
{
|
||||
Point currentPosition = GetPositionOfCanvas(e); // 获取当前鼠标位置
|
||||
|
||||
// 单个移动
|
||||
if (selectNodeControls.Count == 0 || !selectNodeControls.Contains(nodeControl))
|
||||
{
|
||||
double deltaX = currentPosition.X - startControlDragPoint.X; // 计算X轴方向的偏移量
|
||||
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
|
||||
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
|
||||
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
|
||||
DragControl(nodeControl, newLeft, newTop);
|
||||
}
|
||||
// 批量移动
|
||||
else
|
||||
{
|
||||
// 进行批量移动
|
||||
// 获取旧位置
|
||||
var oldLeft = Canvas.GetLeft(nodeControl);
|
||||
var oldTop = Canvas.GetTop(nodeControl);
|
||||
|
||||
// 计算被选择控件的偏移量
|
||||
var deltaX = /*(int)*/(currentPosition.X - startControlDragPoint.X);
|
||||
var deltaY = /*(int)*/(currentPosition.Y - startControlDragPoint.Y);
|
||||
|
||||
// 移动被选择的控件
|
||||
var newLeft = oldLeft + deltaX;
|
||||
var newTop = oldTop + deltaY;
|
||||
|
||||
//this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
|
||||
DragControl(nodeControl, newLeft, newTop);
|
||||
// 计算控件实际移动的距离
|
||||
var actualDeltaX = newLeft - oldLeft;
|
||||
var actualDeltaY = newTop - oldTop;
|
||||
|
||||
// 移动其它选中的控件
|
||||
foreach (var selectItemNode in selectNodeControls)
|
||||
{
|
||||
if (selectItemNode != nodeControl) // 跳过已经移动的控件
|
||||
{
|
||||
var otherNewLeft = Canvas.GetLeft(selectItemNode) + actualDeltaX;
|
||||
var otherNewTop = Canvas.GetTop(selectItemNode) + actualDeltaY;
|
||||
DragControl(selectItemNode, otherNewLeft, otherNewTop);
|
||||
//this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点
|
||||
}
|
||||
}
|
||||
|
||||
// 更新节点之间线的连接位置
|
||||
//foreach (var nodeControl in selectNodeControls)
|
||||
//{
|
||||
// //nodeControl.UpdateLocationConnections();
|
||||
//}
|
||||
}
|
||||
startControlDragPoint = currentPosition; // 更新起始点位置
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void NodeControl_PointerExited(object? sender, PointerEventArgs e)
|
||||
{
|
||||
IsPreviewNodeControl = false;
|
||||
}
|
||||
|
||||
private void NodeControl_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
IsPreviewNodeControl = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Serein.Workbench.Avalonia.Custom.Views">
|
||||
|
||||
<!--
|
||||
Additional resources
|
||||
Using Control Themes:
|
||||
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
|
||||
Using Theme Variants:
|
||||
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
|
||||
-->
|
||||
|
||||
<Design.PreviewWith>
|
||||
<StackPanel Width="400" Spacing="10">
|
||||
<StackPanel Background="{DynamicResource SystemRegionBrush}">
|
||||
<controls:NodeJunctionView />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Design.PreviewWith>
|
||||
|
||||
<ControlTheme x:Key="{x:Type controls:NodeJunctionView}" TargetType="controls:NodeJunctionView">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
215
Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs
Normal file
215
Serein.Workbench.Avalonia/Custom/Views/NodeJunctionView.axaml.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media;
|
||||
using Serein.Library;
|
||||
using Serein.Workbench.Avalonia.Views;
|
||||
using System.Drawing;
|
||||
using System;
|
||||
using Color = Avalonia.Media.Color;
|
||||
using Point = Avalonia.Point;
|
||||
using System.Diagnostics;
|
||||
using Avalonia.Threading;
|
||||
using Serein.Workbench.Avalonia.Api;
|
||||
using Serein.Workbench.Avalonia.Extension;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
/// <summary>
|
||||
/// 连接控制点
|
||||
/// </summary>
|
||||
public class NodeJunctionView : TemplatedControl
|
||||
{
|
||||
private readonly INodeOperationService nodeOperationService;
|
||||
|
||||
/// <summary>
|
||||
/// Render方法中控制自绘内容
|
||||
/// </summary>
|
||||
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
|
||||
|
||||
/// <summary>
|
||||
/// 正在查看
|
||||
/// </summary>
|
||||
private bool IsPreviewing;
|
||||
|
||||
public NodeJunctionView()
|
||||
{
|
||||
nodeOperationService = App.GetService<INodeOperationService>();
|
||||
this.PointerMoved += NodeJunctionView_PointerMoved;
|
||||
this.PointerExited += NodeJunctionView_PointerExited;
|
||||
|
||||
this.PointerPressed += NodeJunctionView_PointerPressed;
|
||||
this.PointerReleased += NodeJunctionView_PointerReleased;
|
||||
}
|
||||
|
||||
private void NodeJunctionView_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
nodeOperationService.ConnectingData.IsCreateing = false;
|
||||
}
|
||||
|
||||
private void NodeJunctionView_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
{
|
||||
nodeOperationService.TryCreateConnectionOnJunction(this); // 尝试开始创建
|
||||
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取到控件信息
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
//if (e.NameScope.Find("PART_FlipflopMethodInfos") is ListBox p_fm)
|
||||
//{
|
||||
// //p_fm.SelectionChanged += ListBox_SelectionChanged;
|
||||
// //p_fm.PointerExited += ListBox_PointerExited;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public static readonly DirectProperty<NodeJunctionView, JunctionType> JunctionTypeProperty =
|
||||
AvaloniaProperty.RegisterDirect<NodeJunctionView, JunctionType>(nameof(JunctionType), o => o.JunctionType, (o, v) => o.JunctionType = v);
|
||||
private JunctionType junctionType;
|
||||
public JunctionType JunctionType
|
||||
{
|
||||
get { return junctionType; }
|
||||
set { SetAndRaise(JunctionTypeProperty, ref junctionType, value); }
|
||||
}
|
||||
|
||||
public static readonly DirectProperty<NodeJunctionView, NodeModelBase?> MyNodeProperty =
|
||||
AvaloniaProperty.RegisterDirect<NodeJunctionView, NodeModelBase?>(nameof(MyNode), o => o.MyNode, (o, v) => o.MyNode = v);
|
||||
private NodeModelBase? myNode;
|
||||
public NodeModelBase? MyNode
|
||||
{
|
||||
get { return myNode; }
|
||||
set { SetAndRaise(MyNodeProperty, ref myNode, value); }
|
||||
}
|
||||
|
||||
#region 重绘UI视觉
|
||||
|
||||
/// <summary>
|
||||
/// 控件重绘事件
|
||||
/// </summary>
|
||||
/// <param name="drawingContext"></param>
|
||||
|
||||
public override void Render(DrawingContext drawingContext)
|
||||
{
|
||||
double width = 44;
|
||||
double height = 26;
|
||||
var background = GetBackgrounp();
|
||||
var pen = new Pen(Brushes.Black, 1);
|
||||
|
||||
// 输入连接器的背景
|
||||
var connectorRect = new Rect(0, 0, width, height);
|
||||
drawingContext.DrawRectangle(Brushes.Transparent, new Pen(), connectorRect);
|
||||
|
||||
|
||||
double circleCenterX = width / 2 ; // 中心 X 坐标
|
||||
double circleCenterY = height / 2 ; // 中心 Y 坐标
|
||||
//_myCenterPoint = new Point(circleCenterX - Width / 2, circleCenterY); // 中心坐标
|
||||
|
||||
// 定义圆形的大小和位置
|
||||
var diameterCircle = width - 20;
|
||||
Rect rect = new(4, 2, diameterCircle / 2, diameterCircle / 2);
|
||||
var ellipse = new EllipseGeometry(rect);
|
||||
drawingContext.DrawGeometry(background, pen, ellipse);
|
||||
|
||||
// 定义三角形的间距
|
||||
double triangleCenterX = width / 2 - 2; // 三角形中心 X 坐标
|
||||
double triangleCenterY = height / 2 -5; // 三角形中心 Y 坐标
|
||||
|
||||
// 绘制三角形
|
||||
var pathGeometry = new StreamGeometry();
|
||||
using (var context = pathGeometry.Open())
|
||||
{
|
||||
int t = 6;
|
||||
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - t), true);
|
||||
context.LineTo(new Point(triangleCenterX + 8, triangleCenterY), true);
|
||||
context.LineTo(new Point(triangleCenterX, triangleCenterY + t), true);
|
||||
context.LineTo(new Point(triangleCenterX, triangleCenterY - t), true);
|
||||
}
|
||||
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry);
|
||||
|
||||
}
|
||||
|
||||
#region 处理鼠标事件
|
||||
|
||||
private void NodeJunctionView_PointerExited(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if (IsPreviewing)
|
||||
{
|
||||
IsPreviewing = false;
|
||||
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
|
||||
}
|
||||
}
|
||||
|
||||
private void NodeJunctionView_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
if (!IsPreviewing)
|
||||
{
|
||||
IsPreviewing = true;
|
||||
Dispatcher.UIThread.InvokeAsync(InvalidateVisual, DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取背景颜色
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected IBrush GetBackgrounp()
|
||||
{
|
||||
var myData = nodeOperationService.ConnectingData;
|
||||
if (!myData.IsCreateing)
|
||||
{
|
||||
//Debug.WriteLine($"return color is {Brushes.BurlyWood}");
|
||||
return new SolidColorBrush(Color.Parse("#76ABEE"));
|
||||
}
|
||||
|
||||
if (myData.IsCanConnected)
|
||||
{
|
||||
if (myData.Type == JunctionOfConnectionType.Invoke)
|
||||
{
|
||||
return myData.ConnectionInvokeType.ToLineColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
return myData.ConnectionArgSourceType.ToLineColor();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Brushes.Red;
|
||||
}
|
||||
|
||||
if (IsPreviewing)
|
||||
{
|
||||
//return new SolidColorBrush(Color.Parse("#04FC10"));
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Debug.WriteLine($"return color is {Brushes.BurlyWood}");
|
||||
return new SolidColorBrush(Color.Parse("#76ABEE"));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="390" d:DesignHeight="40"
|
||||
x:Class="Serein.Workbench.Avalonia.Custom.Views.ParameterDetailsInfoView"
|
||||
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
|
||||
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
|
||||
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
|
||||
xmlns:converter="using:Serein.Workbench.Avalonia.Converters"
|
||||
x:DataType="vm:ParameterDetailsViewModel"
|
||||
VerticalAlignment="Center">
|
||||
<Design.DataContext>
|
||||
<vm:ParameterDetailsViewModel/>
|
||||
</Design.DataContext>
|
||||
<StackPanel >
|
||||
<StackPanel.Resources>
|
||||
<converter:IsVisibleOfParameterConverter x:Key="visibleConverter"/>
|
||||
|
||||
|
||||
</StackPanel.Resources>
|
||||
|
||||
|
||||
|
||||
<Grid ColumnDefinitions="20,40,90,auto" Margin="6,0,10,0">
|
||||
|
||||
<!--<ToolTip.Tip>
|
||||
<StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ToolTip.Tip>-->
|
||||
<!--<ToolTip Background="LightYellow" Foreground="#071042" Content="" />-->
|
||||
|
||||
<cv:NodeJunctionView Grid.Column="0" JunctionType="ArgData" MyNode="{Binding ParameterDetails.NodeModel}" Width="30" Height="15" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding ParameterDetails.IsExplicitData, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" >
|
||||
|
||||
</CheckBox>
|
||||
<!--<TextBlock Grid.Column="2" Text="{Binding ParameterDetails.Index, StringFormat='arg{0} '}" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />-->
|
||||
<TextBlock Grid.Column="2" Text="{Binding ParameterDetails.Name}" FontSize="14"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
ToolTip.Placement="Bottom" ToolTip.VerticalOffset="6">
|
||||
<ToolTip.Tip>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding ParameterDetails}" FontSize="14" TextTrimming="None" TextWrapping="WrapWithOverflow"/>
|
||||
</StackPanel>
|
||||
</ToolTip.Tip>
|
||||
</TextBlock>
|
||||
<TextBlock Grid.Column="3" IsVisible="{Binding IsVisibleA}" FontSize="14" Text=" [ 自动取参 ]" MinWidth="100" MaxWidth="300" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="3" IsVisible="{Binding IsVisibleB}" FontSize="14" Text="{Binding ParameterDetails.DataValue, Mode=TwoWay}" MinWidth="100" MaxWidth="300" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
<ComboBox Grid.Column="3" IsVisible="{Binding IsVisibleC}"
|
||||
ItemsSource="{Binding ParameterDetails.Items}"
|
||||
SelectedValue="{Binding ParameterDetails.DataValue,Mode=OneTime}"
|
||||
MinWidth="100" MaxWidth="300">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" FontFamily="{Binding}" FontSize="14"/>
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,31 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Serein.Library;
|
||||
using Serein.Workbench.Avalonia.Custom.ViewModels;
|
||||
|
||||
namespace Serein.Workbench.Avalonia.Custom.Views;
|
||||
|
||||
internal partial class ParameterDetailsInfoView : UserControl
|
||||
{
|
||||
private readonly ParameterDetailsViewModel _vm;
|
||||
public ParameterDetailsInfoView()
|
||||
{
|
||||
InitializeComponent();
|
||||
var pd = new ParameterDetails();
|
||||
pd.Name = "param name";
|
||||
pd.IsParams = true;
|
||||
pd.DataValue = "data value";
|
||||
pd.Items = ["A","B","C"];
|
||||
_vm = new (pd);
|
||||
DataContext = _vm;
|
||||
}
|
||||
public ParameterDetailsInfoView(ParameterDetailsViewModel parameterDetailsViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = parameterDetailsViewModel;
|
||||
DataContext = _vm;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user