981 lines
45 KiB
C#
981 lines
45 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics.CodeAnalysis;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Runtime.CompilerServices;
|
||
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 Deedy.Activity
|
||
{
|
||
/// <summary>
|
||
/// 用于为Action提供显示视图
|
||
/// </summary>
|
||
[IconKey(ThemeKeys.KEY_Activity_DefaultIcon)]
|
||
[TemplatePart(Name = PART_ItemsContainer, Type = typeof(ItemsControl))]
|
||
[TemplatePart(Name = PART_TitleContainer, Type = typeof(FrameworkElement))]
|
||
[TemplatePart(Name = PART_StateDecorator, Type = typeof(ViewerStateDecorator))]
|
||
public class ActionViewer : Control, IActionViewer
|
||
{
|
||
public static double IndentRuler { get; set; } = 30;
|
||
|
||
public ExpandoMapping ExpandoMapping { get; set; } = new ExpandoMapping();
|
||
|
||
public const string PART_ItemsContainer = "PART_ItemsContainer";
|
||
public const string PART_TitleContainer = "PART_TitleContainer";
|
||
public const string PART_StateDecorator = "PART_StateDecorator";
|
||
[AllowNull]
|
||
protected internal ItemsControl ItemsContainer;
|
||
[AllowNull]
|
||
protected internal FrameworkElement TitleContainer;
|
||
[AllowNull]
|
||
protected internal ViewerStateDecorator StateDecorator;
|
||
static ActionViewer()
|
||
{
|
||
DefaultStyleKeyProperty.OverrideMetadata(typeof(ActionViewer), new FrameworkPropertyMetadata(typeof(ActionViewer)));
|
||
}
|
||
public ActionViewer()
|
||
{
|
||
ToolTipService.SetInitialShowDelay(this, 0);
|
||
ToolTipService.SetBetweenShowDelay(this, 0);
|
||
this.LogInfos = new();
|
||
}
|
||
public override void OnApplyTemplate()
|
||
{
|
||
base.OnApplyTemplate();
|
||
this.ItemsContainer = this.GetTemplateChild(PART_ItemsContainer) as ItemsControl;
|
||
this.TitleContainer = this.GetTemplateChild(PART_TitleContainer) as FrameworkElement;
|
||
this.StateDecorator = this.GetTemplateChild(PART_StateDecorator) as ViewerStateDecorator;
|
||
this.RefreshViewerState();
|
||
}
|
||
public event PropertyChangedEventHandler? PropertyChanged;
|
||
|
||
/// <summary>
|
||
/// 发送属性变更通知
|
||
/// </summary>
|
||
/// <param name="propertyName">发生变更的属性</param>
|
||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
/// <summary>
|
||
/// 读取一个字段的值
|
||
/// </summary>
|
||
/// <typeparam name="T">值的类型,可以通过参数进行自动判定</typeparam>
|
||
/// <param name="field">字段的引用【注意:此参数通过引用传递】</param>
|
||
/// <param name="propertyName">属性名称;如果在属性的读取访问器中调用可以自动注入</param>
|
||
/// <returns>字段中的值</returns>
|
||
protected virtual T GetField<T>(ref T field, [CallerMemberName] string? propertyName = null)
|
||
{
|
||
//TODO:这里处理内部属性获取逻辑(包括运行时参数映射逻辑)
|
||
return field;
|
||
}
|
||
/// <summary>
|
||
/// 检查新值是否与原值相等,如果不相等便赋值并发出通知
|
||
/// </summary>
|
||
/// <typeparam name="T">值的类型,可以通过参数进行自动判定</typeparam>
|
||
/// <param name="field">字段的引用【注意:此参数通过引用传递】</param>
|
||
/// <param name="value">字段的新值</param>
|
||
/// <param name="propertyName">要进行变更通知的属性名称;如果在属性的设置访问器中调用可以自动注入</param>
|
||
/// <returns>值是否有变更</returns>
|
||
protected virtual bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||
{
|
||
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
|
||
//TODO:这里处理内部属性变更逻辑(包括运行时参数映射逻辑)
|
||
field = value;
|
||
OnPropertyChanged(propertyName);
|
||
return true;
|
||
}
|
||
[AllowNull]
|
||
public Runtime Runtime { get; protected internal set; }
|
||
|
||
/// <summary>
|
||
/// 是否显示Break控制手柄
|
||
/// </summary>
|
||
public Visibility ShowBreakHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowBreakHandleProperty); }
|
||
protected internal set { SetValue(ShowBreakHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowBreakHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowBreakHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowBreakHandleProperty = ShowBreakHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示重手柄
|
||
/// </summary>
|
||
public Visibility ShowContinueHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowContinueHandleProperty); }
|
||
protected internal set { SetValue(ShowContinueHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowContinueHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowContinueHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowContinueHandleProperty = ShowContinueHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示退出手柄
|
||
/// </summary>
|
||
public Visibility ShowExitHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowExitHandleProperty); }
|
||
protected internal set { SetValue(ShowExitHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowExitHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowExitHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowExitHandleProperty = ShowExitHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示返回手柄
|
||
/// </summary>
|
||
public Visibility ShowReturnHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowReturnHandleProperty); }
|
||
protected internal set { SetValue(ShowReturnHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowReturnHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowReturnHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowReturnHandleProperty = ShowReturnHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示自定义手柄
|
||
/// </summary>
|
||
public Visibility ShowCustomizeHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowCustomizeHandleProperty); }
|
||
protected internal set { SetValue(ShowCustomizeHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowCustomizeHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowCustomizeHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowCustomizeHandleProperty = ShowCustomizeHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示进入区
|
||
/// </summary>
|
||
public Visibility ShowEntryZone
|
||
{
|
||
get { return (Visibility)GetValue(ShowEntryZoneProperty); }
|
||
protected internal set { SetValue(ShowEntryZonePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowEntryZonePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowEntryZone", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Visible));
|
||
public static readonly DependencyProperty ShowEntryZoneProperty = ShowEntryZonePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示退出区
|
||
/// </summary>
|
||
public Visibility ShowLeaveZone
|
||
{
|
||
get { return (Visibility)GetValue(ShowLeaveZoneProperty); }
|
||
protected internal set { SetValue(ShowLeaveZonePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowLeaveZonePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowLeaveZone", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Visible));
|
||
public static readonly DependencyProperty ShowLeaveZoneProperty = ShowLeaveZonePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示消息回显
|
||
/// </summary>
|
||
public Visibility ShowInfoHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowInfoHandleProperty); }
|
||
protected internal set { SetValue(ShowInfoHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowInfoHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowInfoHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowInfoHandleProperty = ShowInfoHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否显示删除图标
|
||
/// </summary>
|
||
public Visibility ShowDeleteHandle
|
||
{
|
||
get { return (Visibility)GetValue(ShowDeleteHandleProperty); }
|
||
protected internal set { SetValue(ShowDeleteHandlePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowDeleteHandlePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowDeleteHandle", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowDeleteHandleProperty = ShowDeleteHandlePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 显示左侧退出线
|
||
/// </summary>
|
||
public Visibility ShowLeftExitline
|
||
{
|
||
get { return (Visibility)GetValue(ShowLeftExitlineProperty); }
|
||
protected internal set { SetValue(ShowLeftExitlinePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowLeftExitlinePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowLeftExitline", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowLeftExitlineProperty = ShowLeftExitlinePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 显示右侧退出线
|
||
/// </summary>
|
||
public Visibility ShowRightExitline
|
||
{
|
||
get { return (Visibility)GetValue(ShowRightExitlineProperty); }
|
||
protected internal set { SetValue(ShowRightExitlinePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ShowRightExitlinePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ShowRightExitline", typeof(Visibility), typeof(ActionViewer), new PropertyMetadata(Visibility.Collapsed));
|
||
public static readonly DependencyProperty ShowRightExitlineProperty = ShowRightExitlinePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 即时消息
|
||
/// </summary>
|
||
public LogInfo InstantInfo
|
||
{
|
||
get { return (LogInfo)GetValue(InstantInfoProperty); }
|
||
protected internal set { SetValue(InstantInfoPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey InstantInfoPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("InstantInfo", typeof(LogInfo), typeof(ActionViewer), new PropertyMetadata(LogInfo.Empty,
|
||
(d, e) => (d as ActionViewer)?.InstantInfo_PropertyChangedCallback(e)));
|
||
public static readonly DependencyProperty InstantInfoProperty = InstantInfoPropertyKey.DependencyProperty;
|
||
/// <summary>
|
||
/// 处理「ActionViewer.InstantInfo」属性变更
|
||
/// </summary>
|
||
protected virtual void InstantInfo_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
|
||
{
|
||
if (e.NewValue is LogInfo logInfo)
|
||
{
|
||
this.ToolTip = logInfo.ToString();
|
||
//TODO:更新消息显示图标
|
||
this.LogInfos.Add(logInfo);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 信息列表
|
||
/// </summary>
|
||
public LogInfoCollection LogInfos
|
||
{
|
||
get { return (LogInfoCollection)GetValue(LogInfosProperty); }
|
||
protected internal set { SetValue(LogInfosPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey LogInfosPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("LogInfos", typeof(LogInfoCollection), typeof(ActionViewer), new PropertyMetadata(null));
|
||
public static readonly DependencyProperty LogInfosProperty = LogInfosPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 子元素数量
|
||
/// </summary>
|
||
public int ElementCount
|
||
{
|
||
get { return (int)GetValue(ElementCountProperty); }
|
||
protected internal set { SetValue(ElementCountPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ElementCountPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ElementCount", typeof(int), typeof(ActionViewer), new PropertyMetadata(0));
|
||
public static readonly DependencyProperty ElementCountProperty = ElementCountPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否可被选中
|
||
/// </summary>
|
||
public bool IsSelectable
|
||
{
|
||
get { return (bool)GetValue(IsSelectableProperty); }
|
||
protected internal set { SetValue(IsSelectablePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsSelectablePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsSelectable", typeof(bool), typeof(ActionViewer), new PropertyMetadata(true));
|
||
public static readonly DependencyProperty IsSelectableProperty = IsSelectablePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否选中
|
||
/// </summary>
|
||
public bool IsSelected
|
||
{
|
||
get { return (bool)GetValue(IsSelectedProperty); }
|
||
protected internal set { SetValue(IsSelectedPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsSelectedPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsSelected", typeof(bool), typeof(ActionViewer), new PropertyMetadata(false,
|
||
(d, e) => (d as ActionViewer)?.IsSelected_PropertyChangedCallback(e)));
|
||
public static readonly DependencyProperty IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty;
|
||
/// <summary>
|
||
/// 处理「ActionViewer.IsSelected」属性变更
|
||
/// </summary>
|
||
protected virtual void IsSelected_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
|
||
{
|
||
if ((bool)e.NewValue)
|
||
{
|
||
RoutedEventArgs args = new RoutedEventArgs(SelectedEvent, this.ActionElement);
|
||
this.RaiseEvent_Selected(args);
|
||
|
||
if (this.ActionElement is IContainerForFunction container)
|
||
{
|
||
if (container.IsEmbedded) this.IsExpanded = true;
|
||
else
|
||
{
|
||
RequestOpenDesignerEventArgs reqArgs = new RequestOpenDesignerEventArgs(RequestOpenDesignerEvent, this.ActionElement);
|
||
this.RaiseEvent_RequestOpenDesigner(reqArgs);
|
||
}
|
||
}
|
||
}
|
||
this.RefreshViewerState();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否可被展开
|
||
/// </summary>
|
||
public bool IsExpandable
|
||
{
|
||
get { return (bool)GetValue(IsExpandableProperty); }
|
||
protected internal set { SetValue(IsExpandablePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsExpandablePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsExpandable", typeof(bool), typeof(ActionViewer), new PropertyMetadata(true));
|
||
public static readonly DependencyProperty IsExpandableProperty = IsExpandablePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否展开
|
||
/// </summary>
|
||
public bool IsExpanded
|
||
{
|
||
get { return (bool)GetValue(IsExpandedProperty); }
|
||
protected internal set { SetValue(IsExpandedPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsExpandedPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsExpanded", typeof(bool), typeof(ActionViewer), new PropertyMetadata(true));
|
||
public static readonly DependencyProperty IsExpandedProperty = IsExpandedPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否可被拖动
|
||
/// </summary>
|
||
public bool IsDraggable
|
||
{
|
||
get { return (bool)GetValue(IsDraggableProperty); }
|
||
protected internal set { SetValue(IsDraggablePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsDraggablePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsDraggable", typeof(bool), typeof(ActionViewer), new PropertyMetadata(true));
|
||
public static readonly DependencyProperty IsDraggableProperty = IsDraggablePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 是否正在拖动
|
||
/// </summary>
|
||
public bool IsDragging
|
||
{
|
||
get { return (bool)GetValue(IsDraggingProperty); }
|
||
protected internal set { SetValue(IsDraggingPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey IsDraggingPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("IsDragging", typeof(bool), typeof(ActionViewer), new PropertyMetadata(false));
|
||
public static readonly DependencyProperty IsDraggingProperty = IsDraggingPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 视图状态
|
||
/// </summary>
|
||
public ViewerState ViewerState
|
||
{
|
||
get { return (ViewerState)GetValue(ViewerStateProperty); }
|
||
protected internal set { SetValue(ViewerStatePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ViewerStatePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ViewerState", typeof(ViewerState), typeof(ActionViewer), new PropertyMetadata(ViewerState.Normal,
|
||
(d, e) => (d as ActionViewer)?.ViewerState_PropertyChangedCallback(e)));
|
||
public static readonly DependencyProperty ViewerStateProperty = ViewerStatePropertyKey.DependencyProperty;
|
||
/// <summary>
|
||
/// 处理「ActionViewer.ViewerState」属性变更
|
||
/// </summary>
|
||
protected virtual void ViewerState_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
|
||
{
|
||
this.StyleSelector?.SelectStyle(this.ActionElement, this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 工作模式:非设计模式下不允许拖放操作;编辑、运行、回显三种模式不可相互切换,但可以与等待和预览状态切换。
|
||
/// </summary>
|
||
public WorkMode WorkMode
|
||
{
|
||
get { return (WorkMode)GetValue(WorkModeProperty); }
|
||
protected internal set { SetValue(WorkModePropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey WorkModePropertyKey =
|
||
DependencyProperty.RegisterReadOnly("WorkMode", typeof(WorkMode), typeof(ActionViewer), new PropertyMetadata(WorkMode.Waiting));
|
||
public static readonly DependencyProperty WorkModeProperty = WorkModePropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 填充画刷
|
||
/// </summary>
|
||
public Brush FillBrush
|
||
{
|
||
get { return (Brush)GetValue(FillBrushProperty); }
|
||
set { SetValue(FillBrushProperty, value); }
|
||
}
|
||
public static readonly DependencyProperty FillBrushProperty =
|
||
DependencyProperty.Register("FillBrush", typeof(Brush), typeof(ActionViewer), new PropertyMetadata(Brushes.Gray));
|
||
|
||
/// <summary>
|
||
/// 画线画刷
|
||
/// </summary>
|
||
public Brush StrokeBrush
|
||
{
|
||
get { return (Brush)GetValue(StrokeBrushProperty); }
|
||
set { SetValue(StrokeBrushProperty, value); }
|
||
}
|
||
public static readonly DependencyProperty StrokeBrushProperty =
|
||
DependencyProperty.Register("StrokeBrush", typeof(Brush), typeof(ActionViewer), new PropertyMetadata(Brushes.Orange));
|
||
/// <summary>
|
||
/// 画线宽度
|
||
/// </summary>
|
||
public double StrokeThickness
|
||
{
|
||
get { return (double)GetValue(StrokeThicknessProperty); }
|
||
set { SetValue(StrokeThicknessProperty, value); }
|
||
}
|
||
public static readonly DependencyProperty StrokeThicknessProperty =
|
||
DependencyProperty.Register("StrokeThickness", typeof(double), typeof(ActionViewer), new PropertyMetadata(2.0,
|
||
(d, e) => (d as ActionViewer)?.StrokeThickness_PropertyChangedCallback(e)));
|
||
/// <summary>
|
||
/// 处理「ActionViewer.StrokeThickness」属性变更
|
||
/// </summary>
|
||
protected virtual void StrokeThickness_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
|
||
{
|
||
if (e.NewValue is double value)
|
||
{
|
||
if (value < 2)
|
||
{
|
||
this.StrokeThickness = 2;
|
||
return;
|
||
}
|
||
double halfValue = value / 2;
|
||
this.MarginCorrection_Top = new Thickness(0, halfValue, 0, 0);
|
||
this.MarginCorrection_Bottom = new Thickness(0, 0, 0, halfValue);
|
||
this.MarginCorrection_TopBottom = new Thickness(0, halfValue, 0, halfValue);
|
||
}
|
||
else this.StrokeThickness = 2;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 被托管动作元素的图标
|
||
/// </summary>
|
||
public ImageSource ElementIcon
|
||
{
|
||
get { return (ImageSource)GetValue(ElementIconProperty); }
|
||
protected internal set { SetValue(ElementIconPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey ElementIconPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("ElementIcon", typeof(ImageSource), typeof(ActionViewer), new PropertyMetadata(null));
|
||
public static readonly DependencyProperty ElementIconProperty = ElementIconPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 视图样式选择器
|
||
/// </summary>
|
||
[AllowNull]
|
||
public StyleSelector StyleSelector
|
||
{
|
||
get { return (StyleSelector)GetValue(StyleSelectorProperty); }
|
||
set { SetValue(StyleSelectorProperty, value); }
|
||
}
|
||
public static readonly DependencyProperty StyleSelectorProperty =
|
||
DependencyProperty.Register("StyleSelector", typeof(StyleSelector), typeof(ActionViewer), new PropertyMetadata(null));
|
||
/// <summary>
|
||
/// 上边距修正
|
||
/// </summary>
|
||
public Thickness MarginCorrection_Top
|
||
{
|
||
get { return (Thickness)GetValue(MarginCorrection_TopProperty); }
|
||
protected internal set { SetValue(MarginCorrection_TopPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey MarginCorrection_TopPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("MarginCorrection_Top", typeof(Thickness), typeof(ActionViewer), new PropertyMetadata(new Thickness()));
|
||
public static readonly DependencyProperty MarginCorrection_TopProperty = MarginCorrection_TopPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 下边距修正
|
||
/// </summary>
|
||
public Thickness MarginCorrection_Bottom
|
||
{
|
||
get { return (Thickness)GetValue(MarginCorrection_BottomProperty); }
|
||
protected internal set { SetValue(MarginCorrection_BottomPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey MarginCorrection_BottomPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("MarginCorrection_Bottom", typeof(Thickness), typeof(ActionViewer), new PropertyMetadata(new Thickness()));
|
||
public static readonly DependencyProperty MarginCorrection_BottomProperty = MarginCorrection_BottomPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 上下边距修正
|
||
/// </summary>
|
||
public Thickness MarginCorrection_TopBottom
|
||
{
|
||
get { return (Thickness)GetValue(MarginCorrection_TopBottomProperty); }
|
||
protected internal set { SetValue(MarginCorrection_TopBottomPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey MarginCorrection_TopBottomPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("MarginCorrection_TopBottom", typeof(Thickness), typeof(ActionViewer), new PropertyMetadata(new Thickness()));
|
||
public static readonly DependencyProperty MarginCorrection_TopBottomProperty = MarginCorrection_TopBottomPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 缩进边距修正
|
||
/// </summary>
|
||
public Thickness MarginCorrection_Indentation
|
||
{
|
||
get { return (Thickness)GetValue(MarginCorrection_IndentationProperty); }
|
||
protected internal set { SetValue(MarginCorrection_IndentationPropertyKey, value); }
|
||
}
|
||
public static readonly DependencyPropertyKey MarginCorrection_IndentationPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("MarginCorrection_Indentation", typeof(Thickness), typeof(ActionViewer), new PropertyMetadata(new Thickness()));
|
||
public static readonly DependencyProperty MarginCorrection_IndentationProperty = MarginCorrection_IndentationPropertyKey.DependencyProperty;
|
||
|
||
/// <summary>
|
||
/// 动作节点
|
||
/// </summary>
|
||
[AllowNull, DefaultValue(null)]
|
||
public IActionElement ActionElement
|
||
{
|
||
get { return (IActionElement)GetValue(ActionElementProperty); }
|
||
set { SetValue(ActionElementProperty, value); }
|
||
}
|
||
|
||
public static readonly DependencyProperty ActionElementProperty =
|
||
DependencyProperty.Register("ActionElement", typeof(IActionElement), typeof(ActionViewer), new PropertyMetadata(null,
|
||
(d, e) => (d as ActionViewer)?.ActionElement_PropertyChangedCallback(e)));
|
||
/// <summary>
|
||
/// 处理「ActionElementViewer.ActionElement」属性变更
|
||
/// </summary>
|
||
protected virtual void ActionElement_PropertyChangedCallback(DependencyPropertyChangedEventArgs e)
|
||
{
|
||
if (e.OldValue is IActionElement oldValue)
|
||
{
|
||
oldValue.PropertyChanged -= ActionElement_PropertyChanged;
|
||
if (oldValue is ICombinedElement combinedElement)
|
||
{
|
||
this.ElementCount = 0;
|
||
combinedElement.Elements.CollectionChanged -= Elements_CollectionChanged;
|
||
}
|
||
this.ShowEntryZone = Visibility.Visible;
|
||
this.ShowLeaveZone = Visibility.Visible;
|
||
this.ShowLeftExitline = Visibility.Collapsed;
|
||
this.ShowRightExitline = Visibility.Collapsed;
|
||
this.ShowBreakHandle = Visibility.Collapsed;
|
||
this.ShowContinueHandle = Visibility.Collapsed;
|
||
this.ShowCustomizeHandle = Visibility.Collapsed;
|
||
this.ShowExitHandle = Visibility.Collapsed;
|
||
this.ShowReturnHandle = Visibility.Collapsed;
|
||
}
|
||
if (e.NewValue is IActionElement newValue)
|
||
{
|
||
newValue.PropertyChanged += ActionElement_PropertyChanged;
|
||
|
||
Type elementType = newValue.GetType();
|
||
IconKeyAttribute? iconKey = elementType.GetCustomAttribute<IconKeyAttribute>();
|
||
if (iconKey != null)
|
||
{
|
||
ImageSource? icon = iconKey.TryFindResource(this) as ImageSource;
|
||
if (icon != null) this.ElementIcon = icon;
|
||
else
|
||
{
|
||
iconKey = this.GetType().GetCustomAttribute<IconKeyAttribute>();
|
||
if (iconKey != null) icon = iconKey.TryFindResource(this) as ImageSource;
|
||
|
||
if (icon != null) this.ElementIcon = icon;
|
||
else this.ClearValue(ElementIconPropertyKey);
|
||
}
|
||
}
|
||
|
||
if (newValue.IsLocked)
|
||
{
|
||
this.IsDraggable = false;
|
||
// 功能容器类型的锁定元素允许被选中
|
||
this.IsSelectable = newValue is IContainerForFunction;
|
||
}
|
||
else
|
||
{
|
||
this.IsDraggable = true;
|
||
this.IsSelectable = true;
|
||
}
|
||
|
||
if (newValue is ICombinedElement newCombined)
|
||
{
|
||
this.IsExpandable = true;
|
||
this.ElementCount = newCombined.Elements.Count;
|
||
newCombined.Elements.CollectionChanged += Elements_CollectionChanged;
|
||
}
|
||
else this.IsExpandable = false;
|
||
|
||
if (newValue is ICriticalZoneManageable criticalZone)
|
||
{
|
||
if (criticalZone.IsLeaveZoneHidden) this.ShowLeaveZone = Visibility.Collapsed;
|
||
if (criticalZone.IsEntryZoneHidden) this.ShowEntryZone = Visibility.Collapsed;
|
||
}
|
||
|
||
if (newValue is IContainerWithExitline withExitline)
|
||
{
|
||
if (withExitline.ExitlinePosition.HasFlag(ExitlinePosition.LeftLower))
|
||
this.ShowLeftExitline = Visibility.Visible;
|
||
if (withExitline.ExitlinePosition.HasFlag(ExitlinePosition.Rightlower))
|
||
this.ShowRightExitline = Visibility.Visible;
|
||
// 新元素被托管渲染后调整子元素退出线位置
|
||
withExitline.AdjustExitlinePosition();
|
||
}
|
||
|
||
if (newValue is ILogicController logicController)
|
||
{
|
||
switch (logicController.LogicalBehavior)
|
||
{
|
||
case LogicalBehavior.Exit: this.ShowExitHandle = Visibility.Visible; break;
|
||
case LogicalBehavior.Return: this.ShowReturnHandle = Visibility.Visible; break;
|
||
case LogicalBehavior.Break: this.ShowBreakHandle = Visibility.Visible; break;
|
||
case LogicalBehavior.Continue: this.ShowContinueHandle = Visibility.Visible; break;
|
||
case LogicalBehavior.Customize: this.ShowCustomizeHandle = Visibility.Visible; break;
|
||
default: break;
|
||
}
|
||
}
|
||
//TODO:根据节点的特征与当前工作状态来判定如何处理当前视图的属性
|
||
}
|
||
}
|
||
|
||
protected virtual void Elements_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||
{
|
||
if (this.ActionElement is ICombinedElement combined)
|
||
{
|
||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
|
||
this.IsExpanded = true;
|
||
this.ElementCount = combined.Elements.Count;
|
||
}
|
||
// 节点集合变更时调整退出线位置
|
||
if (this.ActionElement is IContainerWithExitline container) container.AdjustExitlinePosition();
|
||
}
|
||
|
||
protected virtual void ActionElement_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||
{
|
||
switch (e.PropertyName)
|
||
{
|
||
//TODO:处理托管的IActionElement节点的属性变更
|
||
case nameof(IElement.DepthLevel):
|
||
this.MarginCorrection_Indentation = new Thickness(ActionViewer.IndentRuler * this.ActionElement.DepthLevel, 0, 0, 0);
|
||
break;
|
||
case nameof(IActionElement.InstantInfo):
|
||
this.InstantInfo = this.ActionElement.InstantInfo;
|
||
break;
|
||
case nameof(IContainerWithExitline.ExitlinePosition):
|
||
// 自身退出线变更时调整所有子节点退出线位置
|
||
(this.ActionElement as IContainerWithExitline)?.AdjustExitlinePosition();
|
||
break;
|
||
default: break;
|
||
}
|
||
}
|
||
|
||
public void ReadyToWorking(IElement? element = null, Output? output = null)
|
||
{
|
||
if (this.ActionElement != element && element is IActionElement action) this.ActionElement = action;
|
||
if (this.WorkMode == WorkMode.Waiting) this.WorkMode = WorkMode.Preview;
|
||
}
|
||
|
||
public void ReadyToEditing(Runtime runtime)
|
||
{
|
||
this.ReadyToWorking();
|
||
this.WorkMode = WorkMode.Editing;
|
||
}
|
||
|
||
public void ReadyToRunning(Runtime runtime)
|
||
{
|
||
this.ReadyToWorking();
|
||
this.WorkMode = WorkMode.Running;
|
||
}
|
||
/// <summary>
|
||
/// 刷新视图样式
|
||
/// </summary>
|
||
protected virtual void RefreshViewerState(bool isOverMe = false)
|
||
{
|
||
if (this.IsDragging)
|
||
{
|
||
if (this.IsSelected)
|
||
{
|
||
if (isOverMe) this.ViewerState = ViewerState.SelectedDragHover;
|
||
else this.ViewerState = ViewerState.Selected;
|
||
}
|
||
else
|
||
{
|
||
if (isOverMe) this.ViewerState = ViewerState.NormalDragHover;
|
||
else this.ViewerState = ViewerState.Normal;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (this.IsSelected)
|
||
{
|
||
if (isOverMe) this.ViewerState = ViewerState.SelectedHover;
|
||
else this.ViewerState = ViewerState.Selected;
|
||
}
|
||
else
|
||
{
|
||
if (isOverMe) this.ViewerState = ViewerState.NormalHover;
|
||
else this.ViewerState = ViewerState.Normal;
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 检查事件源是否需要自身处理
|
||
/// </summary>
|
||
/// <param name="eventSource">引发事件的源</param>
|
||
/// <returns>只有事件源是自身,或事件源的第一个SmartView类型的祖辈是自身时才需要处理</returns>
|
||
protected virtual bool IsEventNeedToHandle(object eventSource)
|
||
{
|
||
bool result = false;
|
||
if (eventSource is ActionViewer view && view != this) result = false;
|
||
if (eventSource is not ActionViewer && eventSource is DependencyObject dobj && this == dobj.FindAncestor<ActionViewer>()) result = true;
|
||
return result;
|
||
}
|
||
protected virtual void RemoveDragAdorner()
|
||
{
|
||
//TODO:移除拖拽装饰器
|
||
}
|
||
protected virtual void UpdateDragAdorner(Dock? dock)
|
||
{
|
||
//TODO:更新拖拽装饰器
|
||
}
|
||
#region 订阅的交互事件
|
||
protected override void OnDragEnter(DragEventArgs e)
|
||
{
|
||
this.IsDragging = true;
|
||
ToolTipService.SetIsEnabled(this, false);
|
||
|
||
base.OnDragEnter(e);
|
||
}
|
||
protected override void OnDragLeave(DragEventArgs e)
|
||
{
|
||
this.IsDragging = false;
|
||
|
||
base.OnDragLeave(e);
|
||
}
|
||
protected internal bool _CanDropInParent = false;
|
||
protected internal bool _CanDropChild = false;
|
||
protected internal DropPlacement _DropPlacement = DropPlacement.Uncertain;
|
||
protected override void OnDragOver(DragEventArgs e)
|
||
{
|
||
if (this.IsEventNeedToHandle(e.OriginalSource))
|
||
{
|
||
this.RefreshViewerState(true);
|
||
FrameworkElement hitElement = this;
|
||
if (this.TitleContainer != null && this.TitleContainer.Visibility == Visibility.Visible)
|
||
hitElement = this.TitleContainer;
|
||
|
||
Point currentPoint = e.GetPosition(hitElement);
|
||
if (this._CanDropInParent && (currentPoint.Y < 10 || currentPoint.Y < 10))
|
||
{
|
||
this._DropPlacement = DropPlacement.BeforeMe;
|
||
this.UpdateDragAdorner(Dock.Top);
|
||
}
|
||
else if (this._CanDropInParent && (hitElement.ActualHeight - currentPoint.Y < 10 || hitElement.ActualWidth - currentPoint.X < 10))
|
||
{
|
||
this._DropPlacement = DropPlacement.BehindMe;
|
||
this.UpdateDragAdorner(Dock.Bottom);
|
||
}
|
||
else if (this._CanDropChild)
|
||
{
|
||
this._DropPlacement = DropPlacement.WithinMe;
|
||
this.UpdateDragAdorner(null);
|
||
}
|
||
else
|
||
{
|
||
this._DropPlacement = DropPlacement.Rejected;
|
||
this.RemoveDragAdorner();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
this.RefreshViewerState();
|
||
this.RemoveDragAdorner();
|
||
}
|
||
|
||
base.OnDragOver(e);
|
||
}
|
||
protected override void OnDrop(DragEventArgs e)
|
||
{
|
||
this.IsDragging = false;
|
||
this.RefreshViewerState();
|
||
this.RemoveDragAdorner();
|
||
if (this.IsEventNeedToHandle(e.OriginalSource))
|
||
{
|
||
//TODO:发送准备放置事件,如果事件被处理则「return」放弃默认逻辑
|
||
//注意:这里不能标记e.Handle=true;否则可能会造成视图显示状态异常
|
||
if (e.Data.GetData(typeof(DragDropData)) is DragDropData data)
|
||
{
|
||
data.Placement = this._DropPlacement;
|
||
//TODO:执行放置操作
|
||
}
|
||
}
|
||
|
||
base.OnDrop(e);
|
||
}
|
||
protected override void OnMouseEnter(MouseEventArgs e)
|
||
{
|
||
base.OnMouseEnter(e);
|
||
ToolTipService.SetIsEnabled(this, true);
|
||
if (!this.IsDragging) this.RefreshViewerState(true);
|
||
}
|
||
protected override void OnMouseLeave(MouseEventArgs e)
|
||
{
|
||
base.OnMouseLeave(e);
|
||
|
||
if (!this.IsDragging) this.RefreshViewerState();
|
||
}
|
||
protected override void OnMouseDown(MouseButtonEventArgs e)
|
||
{
|
||
base.OnMouseDown(e);
|
||
|
||
if (e.LeftButton == MouseButtonState.Pressed)
|
||
{
|
||
if (this.IsSelectable)
|
||
{
|
||
CancelRoutedEventArgs args = new CancelRoutedEventArgs(PreviewSelectedEvent, this.ActionElement);
|
||
this.RaiseEvent_PreviewSelected(args);
|
||
this.IsSelected = !args.Cancel;
|
||
}
|
||
if (this.IsDraggable) this._DragStartPoint = e.GetPosition(this);
|
||
}
|
||
}
|
||
protected override void OnMouseUp(MouseButtonEventArgs e)
|
||
{
|
||
base.OnMouseUp(e);
|
||
|
||
this._DragStartPoint = null;
|
||
}
|
||
protected override void OnMouseMove(MouseEventArgs e)
|
||
{
|
||
base.OnMouseMove(e);
|
||
if (this.IsDragging) return;
|
||
bool isEventNeedHandle = this.IsEventNeedToHandle(e.OriginalSource);
|
||
this.RefreshViewerState(isEventNeedHandle);
|
||
if (isEventNeedHandle)
|
||
{
|
||
if (this.ActionElement == null || this.ActionElement.IsLocked) return;
|
||
|
||
var position = e.GetPosition(this);
|
||
ToolTipService.SetPlacement(this, PlacementMode.Relative);
|
||
ToolTipService.SetPlacementTarget(this, this);
|
||
ToolTipService.SetHorizontalOffset(this, position.X + 16);
|
||
ToolTipService.SetVerticalOffset(this, position.Y + 16);
|
||
}
|
||
}
|
||
protected internal readonly object _DragLock = new();
|
||
protected internal Point? _DragStartPoint = null;
|
||
|
||
protected override void OnPreviewMouseMove(MouseEventArgs e)
|
||
{
|
||
base.OnPreviewMouseMove(e);
|
||
if (!this.IsDraggable) return;
|
||
if (e.LeftButton == MouseButtonState.Pressed)
|
||
{
|
||
lock (_DragLock)
|
||
{
|
||
if (this.IsDragging || !_DragStartPoint.HasValue) return;
|
||
if (_DragStartPoint.HasValue)
|
||
{
|
||
Point currentPoint = e.GetPosition(this);
|
||
|
||
if (Math.Abs(this._DragStartPoint.Value.X - currentPoint.X) > SystemParameters.MinimumHorizontalDragDistance
|
||
||
|
||
Math.Abs(this._DragStartPoint.Value.Y - currentPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
|
||
{
|
||
//TODO:发送准备拖动事件,如果事件被响应则「return」退出原本逻辑
|
||
|
||
DragDropEffects dragDropEffects = DragDropEffects.Link;
|
||
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
|
||
dragDropEffects |= DragDropEffects.Copy;
|
||
else dragDropEffects |= DragDropEffects.Move;
|
||
//TODO:构造拖动数据
|
||
e.Handled = true;
|
||
this._DragStartPoint = null;
|
||
//TODO:启动拖动逻辑
|
||
//DragDrop.DoDragDrop(this, data, data.dragEffect);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 声明的交互事件
|
||
/// <summary>
|
||
/// 节点被选中
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 节点被选中:当视图节点被选中时发生
|
||
/// </remarks>
|
||
public static readonly RoutedEvent SelectedEvent = EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble,
|
||
typeof(EventHandler<RoutedEventArgs>), typeof(ActionViewer));
|
||
/// <summary>
|
||
/// 事件「节点被选中」封装
|
||
/// </summary>
|
||
public event EventHandler<RoutedEventArgs> Selected
|
||
{
|
||
add { AddHandler(SelectedEvent, value); }
|
||
remove { RemoveHandler(SelectedEvent, value); }
|
||
}
|
||
/// <summary>
|
||
/// 事件「节点被选中」触发
|
||
/// </summary>
|
||
/// <param name="args">事件参数</param>
|
||
protected virtual void RaiseEvent_Selected(RoutedEventArgs args)
|
||
{
|
||
// 事件触发前可以进行一定的处理逻辑
|
||
RaiseEvent(args);
|
||
}
|
||
/// <summary>
|
||
/// 节点将要被选中
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 当节点要被选中前发生,此事件可以被取消
|
||
/// </remarks>
|
||
public static readonly RoutedEvent PreviewSelectedEvent = EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Tunnel,
|
||
typeof(EventHandler<CancelRoutedEventArgs>), typeof(ActionViewer));
|
||
/// <summary>
|
||
/// 事件「节点将要被选中」封装
|
||
/// </summary>
|
||
public event EventHandler<CancelRoutedEventArgs> PreviewSelected
|
||
{
|
||
add { AddHandler(SelectedEvent, value); }
|
||
remove { RemoveHandler(SelectedEvent, value); }
|
||
}
|
||
/// <summary>
|
||
/// 事件「节点将要被选中」触发
|
||
/// </summary>
|
||
/// <param name="args">事件参数</param>
|
||
protected virtual void RaiseEvent_PreviewSelected(CancelRoutedEventArgs args)
|
||
{
|
||
// 事件触发前可以进行一定的处理逻辑
|
||
RaiseEvent(args);
|
||
}
|
||
/// <summary>
|
||
/// 请求打开独立的设计器
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 当一个逻辑容器节点被选中,且要求系统打开独立设计器时发送此事件
|
||
/// </remarks>
|
||
public static readonly RoutedEvent RequestOpenDesignerEvent = EventManager.RegisterRoutedEvent("RequestOpenDesigner", RoutingStrategy.Bubble,
|
||
typeof(EventHandler<RequestOpenDesignerEventArgs>), typeof(ActionViewer));
|
||
/// <summary>
|
||
/// 事件「请求打开独立的设计器」封装
|
||
/// </summary>
|
||
public event EventHandler<RequestOpenDesignerEventArgs> RequestOpenDesigner
|
||
{
|
||
add { AddHandler(RequestOpenDesignerEvent, value); }
|
||
remove { RemoveHandler(RequestOpenDesignerEvent, value); }
|
||
}
|
||
/// <summary>
|
||
/// 事件「请求打开独立的设计器」触发
|
||
/// </summary>
|
||
/// <param name="args">事件参数</param>
|
||
protected virtual void RaiseEvent_RequestOpenDesigner(RequestOpenDesignerEventArgs args)
|
||
{
|
||
// 事件触发前可以进行一定的处理逻辑
|
||
RaiseEvent(args);
|
||
}
|
||
#endregion
|
||
}
|
||
}
|