using AddInPlugin; using AddInPlugin.Entity; using CommunityToolkit.Mvvm.ComponentModel; using GongSolutions.Wpf.DragDrop; using OpenTap; using OpenTapEditor.UI; using OpenTapEditor.Util; using System.Collections.ObjectModel; using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using UILib; namespace OpenTapEditor { /// /// StepTree.xaml 的交互逻辑 /// [ObservableObject] public partial class StepTree : UserControl { public event RoutedPropertyChangedEventHandler SelectedItemChanged; private static List StepMenuItems; [ObservableProperty] private TestStepList datasource; [ObservableProperty] private Guid currentId; public TestPlan Plan { get; set; } public IDropTarget DragHandler { get; set; } = new StepTreeDragHandler(); public StepTree() { DataContext = this; InitializeComponent(); } public List RefreshMenuItems(ITestStep selectedStep, bool addChild) { var allSteps = StepTypeLoader.Steps; var StepMenuItems = new List(allSteps.Count); Dictionary groupMap = new Dictionary(); Dictionary groupAdapMap = new Dictionary(); foreach (ITypeData stepType in allSteps) { var display = stepType.GetAttribute(); if (display == null) { continue; } MenuItem groupItem = null; var groups = display.Group; if (groups.Length > 0) { var group = groups[groups.Length - 1]; if (!string.IsNullOrEmpty(group)) { if (!groupMap.ContainsKey(group)) { groupMap[group] = new MenuItem() { Header = group}; StepMenuItems.Add(groupMap[group]); } groupItem = groupMap[group]; } } TypeData data = stepType as TypeData; StepTypeMenuItem menuItem = new StepTypeMenuItem(); string des = string.IsNullOrEmpty(display.Description) ? "" : $" ({display.Description})"; menuItem.Header = $"{display.Name}{des}"; menuItem.Click += addChild ? Add_Child_Step_Click : Add_Generaic_Step_Click; menuItem.TypeData = stepType; menuItem.Tag = selectedStep; if (!addChild) { var parent = selectedStep?.Parent?.GetType() ?? typeof(TestPlan); menuItem.IsEnabled = parent == null || TestStepList.AllowChild(parent, data.Type); } else { menuItem.IsEnabled = (selectedStep != null && TestStepList.AllowChild(selectedStep.GetType(), data.Type)); } if (groupItem != null) { groupItem.Items.Add(menuItem); } else { StepMenuItems.Add(menuItem); } } if (StepTypeLoader.CustomSteps != null && StepTypeLoader.CustomSteps.Count > 0) { StepMenuItems.Add(new Separator { }); foreach (AdapterData stepType in StepTypeLoader.CustomSteps) { MenuItem groupItem = null; if (!string.IsNullOrEmpty(stepType.Group)) { if (!groupAdapMap.ContainsKey(stepType.Group)) { groupAdapMap[stepType.Group] = new MenuItem { Header = stepType.Group }; StepMenuItems.Add(groupAdapMap[stepType.Group]); } groupItem = groupAdapMap[stepType.Group]; } StepTypeMenuItem menuItem = new StepTypeMenuItem(); menuItem.Header = stepType.Name; menuItem.Click += addChild ? Add_Child_Step_Click : Add_Generaic_Step_Click; menuItem.TypeData = stepType; menuItem.Tag = selectedStep; menuItem.IsEnabled = !addChild || (selectedStep != null && TestStepList.AllowChild(selectedStep?.GetType(), typeof(AdapterData))); if (groupItem != null) { groupItem.Items.Add(menuItem); } else { StepMenuItems.Add(menuItem); } } } return StepMenuItems; } private void treeList_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) { SelectedItemChanged?.Invoke(sender, e); } private void treeList_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { DependencyObject source = e.OriginalSource as DependencyObject; while (source != null && source.GetType() != typeof(TreeListViewItem)) { source = System.Windows.Media.VisualTreeHelper.GetParent(source); } if (source != null) { TreeListViewItem item = source as TreeListViewItem; item.Focus(); var step = item.DataContext as ITestStep; CreateContextMenu(step); } else { CreateContextMenu(null); } } /// /// 显示菜单 /// /// private void CreateContextMenu(ITestStep obj) { ContextMenu contextMenu = new ContextMenu(); MenuItem menuItem = new MenuItem(); menuItem.Header = "插入Step"; menuItem.Tag = obj; foreach (var item in RefreshMenuItems(obj, false)) { menuItem.Items.Add(item); } contextMenu.Items.Add(menuItem); menuItem = new MenuItem(); menuItem.Header = "插入子Step"; menuItem.Tag = obj; foreach (var item in RefreshMenuItems(obj, true)) { menuItem.Items.Add(item); } contextMenu.Items.Add(menuItem); menuItem = new MenuItem(); menuItem.Header = "复制"; menuItem.Click += Copy_Step_Click; menuItem.Tag = obj; contextMenu.Items.Add(menuItem); menuItem = new MenuItem(); menuItem.Header = "粘贴"; menuItem.Click += Paste_Step_Click; menuItem.Tag = obj; contextMenu.Items.Add(menuItem); menuItem = new MenuItem(); menuItem.Header = "删除"; menuItem.Click += Delete_Step_Click; menuItem.Tag = obj; contextMenu.Items.Add(menuItem); this.treeList.ContextMenu = contextMenu; } private void AddStep2Plan(ITestStep newStep, ITestStep insertAfter, TestPlan plan) { if (insertAfter != null) { var parent = insertAfter.Parent; if (parent is TestPlan pplan) { pplan.InsertStep(Datasource.SequenceName, pplan.GetStepsByName(Datasource.SequenceName).IndexOf(insertAfter) + 1, newStep); } else { parent.InsertStep(Datasource.SequenceName, parent.ChildTestSteps.IndexOf(insertAfter) + 1, newStep); } } else { plan.AddStep(Datasource.SequenceName, newStep); } } #region add /// /// 添加同级Step /// /// /// private void Add_Generaic_Step_Click(object sender, RoutedEventArgs e) { ITestStep step = ((StepTypeMenuItem)sender).Tag as ITestStep; ITestStep newStep = null; if (((StepTypeMenuItem)sender).TypeData is ITypeData type) { newStep = type.CreateInstance() as ITestStep; } else if (((StepTypeMenuItem)sender).TypeData is AdapterData data) { newStep = new AdapterStep() { Name = data.Name, XmlString = data.Xml }; } if (newStep != null) { AddStep2Plan(newStep, step, Plan); } } /// /// 添加子Step /// /// /// private void Add_Child_Step_Click(object sender, RoutedEventArgs e) { ITestStep step = ((StepTypeMenuItem)sender).Tag as ITestStep; if (step == null) { return; } ITestStep newStep = null; if (((StepTypeMenuItem)sender).TypeData is ITypeData type) { newStep = type.CreateInstance() as ITestStep; } else if (((StepTypeMenuItem)sender).TypeData is AdapterData data) { newStep = new AdapterStep() { Name = data.Name, XmlString = data.Xml }; } if (newStep != null) { step.ChildTestSteps.Add(newStep); } } private void Delete_Step_Click(object sender, RoutedEventArgs e) { ITestStep step = ((MenuItem)sender).Tag as ITestStep; if (step.Parent is ITestStep) { step.Parent.ChildTestSteps.Remove(step); } else { Datasource.Remove(step); } } private void Copy_Step_Click(object sender, RoutedEventArgs e) { ITestStep step = ((MenuItem)sender).Tag as ITestStep; TapSerializer serializer = new TapSerializer(); using (var str = new MemoryStream()) { serializer.Serialize(str, step); Clipboard.SetText(Encoding.UTF8.GetString(str.ToArray())); } } private void Paste_Step_Click(object sender, RoutedEventArgs e) { try { ITestStep step = ((MenuItem)sender).Tag as ITestStep; TapSerializer serializer = new TapSerializer(); ITestStep newStep = (ITestStep)serializer.DeserializeFromString(Clipboard.GetText(), TypeData.FromType(typeof(ITestStep))); newStep.Id = Guid.NewGuid(); foreach (var child in newStep.RecursivelyGetChildSteps(TestStepSearch.All)) { child.Id = Guid.NewGuid(); } AddStep2Plan(newStep, step, Plan); } catch { } } #endregion } }