using OpenTap.Addin; using OpenTapEditor.Util; using SeqEditor.SubWin; using SeqEditor.Util; using System.Collections.ObjectModel; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using UILib; namespace OpenTapEditor { public class MultiTestVariableConverter : IMultiValueConverter { public static MultiTestVariableConverter Instance = new MultiTestVariableConverter(); public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values[0] is TestVariable tv && values[1] is TreeListViewItem item) { if (parameter?.ToString() == "NameCanEdit") { var parent = item?.parent?.DataContext as TestVariable; return tv.NameCanEdit ? (parent != null && !parent.IsArray) : false; } if (parameter?.ToString() == "ChangeButton") { var parent = item?.parent?.DataContext as TestVariable; if (!tv.NameCanEdit) { return Visibility.Collapsed; } if (parent?.IsArray == true && parent.Type != TestVariableType.Container) { return Visibility.Collapsed; } return Visibility.Visible; } } return null; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { return null; } } public class TestVariableConverter : IValueConverter { public static TestVariableConverter Instance = new TestVariableConverter(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is TestVariable tv) { if (parameter?.ToString() == "TypeName") { return tv.IsArray ? $"{tv.Type}[]" : tv.Type.ToString(); } if (parameter?.ToString() == "StringFormat") { return tv.IsArray || tv.Type != TestVariableType.String ? false : true; } if (parameter?.ToString() == "CanEdit") { return !tv.IsArray && tv.Type != TestVariableType.Container && tv.Type != TestVariableType.Refrence; } if (parameter?.ToString() == "EditType") { return tv.Type == TestVariableType.Bool ? EditBoxType.Bool : EditBoxType.String; } } else if (value is RunningVar rv) { if (parameter?.ToString() == "TypeName") { return rv.IsArray ? $"{rv.Type}[]" : rv.Type.ToString(); } if (parameter?.ToString() == "StringFormat") { return rv.IsArray || rv.Type != TestVariableType.String ? false : true; } if (parameter?.ToString() == "CanEdit") { return !rv.IsArray && rv.Type != TestVariableType.Container && rv.Type != TestVariableType.Refrence; } if (parameter?.ToString() == "EditType") { return rv.Type == TestVariableType.Bool ? EditBoxType.Bool : EditBoxType.String; } } return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return value; } } public class TestVariableMenuItem : MenuItem { public IEnumerable Source { get; set; } public TestVariable Variable { get; set; } public TestVariable Parent { get; set; } public TreeListViewItem SourceItem { get; set; } public bool TargetIsArray { get; set; } public TestVariableType TargetType { get; set; } } /// /// VariableTree.xaml 的交互逻辑 /// public partial class VariableTree : UserControl { public VariableTree() { InitializeComponent(); BindingOperations.SetBinding(treeView, TreeListView.ItemsSourceProperty, new Binding(nameof(Datasource)) { Source = this, Mode = BindingMode.TwoWay, }); } public static DependencyProperty DatasourceProperty = DependencyProperty.Register( "Datasource", typeof(ObservableCollection), typeof(VariableTree), new PropertyMetadata(null) ); public string Prefix { get; set; } public ObservableCollection Datasource { get => (ObservableCollection)GetValue(DatasourceProperty); set { SetValue(DatasourceProperty, value); } } private void OnPreviewMouseRightButtonDown(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 variable = item.DataContext; CreateContextMenu(item, variable); } else { CreateContextMenu(null, null); } } public List RefreshMenuItems(TreeListViewItem parentItem, TestVariable parent, TestVariable source) { var allSteps = StepTypeLoader.Steps; var StepMenuItems = new List(allSteps.Count); foreach (TestVariableType type in Enum.GetValues(typeof(TestVariableType))) { TestVariableMenuItem menuItem = new TestVariableMenuItem(); menuItem.Header = type.ToString(); menuItem.SourceItem = parentItem; menuItem.Variable = source; menuItem.Click += MenuItem_Click; menuItem.TargetIsArray = false; menuItem.TargetType = type; menuItem.Parent = parent; StepMenuItems.Add(menuItem); } var arrItem = new MenuItem(); arrItem.Header = "Array Of..."; foreach (TestVariableType type in Enum.GetValues(typeof(TestVariableType))) { TestVariableMenuItem menuItem = new TestVariableMenuItem(); menuItem.Header = type.ToString(); menuItem.SourceItem = parentItem; menuItem.Variable = source; menuItem.Click += ArrayMenuItem_Click; menuItem.TargetIsArray = true; menuItem.TargetType = type; menuItem.Parent = parent; arrItem.Items.Add(menuItem); } StepMenuItems.Add(arrItem); return StepMenuItems; } /// /// 显示菜单 /// /// private void CreateContextMenu(TreeListViewItem item, object obj) { var v = obj as TestVariable; ContextMenu contextMenu = new ContextMenu(); contextMenu.Closed += ContextMenu_Closed; var parentItem = item?.parent as TreeListViewItem; var parent = item?.parent?.DataContext as TestVariable; TestVariableMenuItem menuItem = new TestVariableMenuItem(); if (v == null) { return; //menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("插入").ProvideValue(null)); //foreach (var i in RefreshMenuItems(item, null)) //{ // menuItem.Items.Add(i); //} //contextMenu.Items.Add(menuItem); } if (v.IsArray) { menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("添加元素").ProvideValue(null)); menuItem.Parent = v; menuItem.Click += MenuItem_Click; menuItem.SourceItem = item; contextMenu.Items.Add(menuItem); } else if (v.Type == TestVariableType.Container) { menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("添加").ProvideValue(null)); foreach (var i in RefreshMenuItems(item, v, null)) { menuItem.Items.Add(i); } contextMenu.Items.Add(menuItem); } if (parent != null) { if (parent.IsArray) { menuItem = new TestVariableMenuItem(); menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("插入元素").ProvideValue(null)); menuItem.SourceItem = parentItem; menuItem.Parent = parent; menuItem.Variable = v; menuItem.Click += MenuItem_Click; contextMenu.Items.Add(menuItem); } else { menuItem = new TestVariableMenuItem(); menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("插入").ProvideValue(null)); foreach (var i in RefreshMenuItems(parentItem, parent, v)) { menuItem.Items.Add(i); } contextMenu.Items.Add(menuItem); } } if (v.NameCanEdit) { menuItem = new TestVariableMenuItem(); menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("删除").ProvideValue(null)); menuItem.Click += Delete_Click; menuItem.Variable = v; menuItem.Tag = item; contextMenu.Items.Add(menuItem); } menuItem = new TestVariableMenuItem(); menuItem.SetValue(TestVariableMenuItem.HeaderProperty, new DynamicResourceExtension("复制名称").ProvideValue(null)); menuItem.Click += CopyName_Click; menuItem.Tag = item; contextMenu.Items.Add(menuItem); this.treeView.ContextMenu = contextMenu; } private void ContextMenu_Closed(object sender, RoutedEventArgs e) { this.treeView.ContextMenu = null; } /// /// /// /// /// private void MenuItem_Click(object sender, RoutedEventArgs e) { TestVariableMenuItem item = sender as TestVariableMenuItem; TestVariable parent = item.Parent; TestVariable newOne = null; TestVariable source = item.Variable; TreeListViewItem parentItem = item.SourceItem; if (parent == null) { return; } if (parent.IsArray) { newOne = new TestVariable { Name = $"[{parent.Children.Count}]", IsArray = false, Type = parent.Type, Value = TestVariable.DefaultValue(parent.Type) }; if (source == null) { parent.Children.Add(newOne); } else { parent.Children.Insert(parent.Children.IndexOf(source) + 1, newOne); } for (int i = 0; i < parent.Children.Count; i++) { parent.Children[i].Name = $"[{i}]"; } goto End; } if (parent.Type == TestVariableType.Container) { newOne = new TestVariable { IsArray = item.TargetIsArray, Type = item.TargetType, Value = item.TargetIsArray ? null : TestVariable.DefaultValue(item.TargetType) }; if (source == null) { parent.Children.Add(newOne); } else { parent.Children.Insert(parent.Children.IndexOf(source) + 1, newOne); } goto End; } End: parentItem.IsExpanded = true; DispatcherHelper.WaitForPriority(); var scroll = treeView.Template.FindName("scroll", treeView) as ScrollViewer; var tvi = parentItem.ItemContainerGenerator.ContainerFromIndex(parent.Children.IndexOf(newOne)) as TreeListViewItem; if (!DispatcherHelper.IsElementVisibleInScrollViewer(scroll, tvi)) { Point offset = tvi.TransformToAncestor(scroll).Transform(new Point(0, 0)); scroll.ScrollToVerticalOffset(offset.Y); } var eb = DispatcherHelper.FindVisualChild(tvi); eb?.TryEdit(null); tvi.IsSelected = true; } /// /// 添加Array /// /// /// private void ArrayMenuItem_Click(object sender, RoutedEventArgs e) { TestVariableMenuItem item = sender as TestVariableMenuItem; TestVariable parent = item.Parent; TestVariable newOne = null; TestVariable source = item.Variable; TreeListViewItem parentItem = item.SourceItem; if (parent == null) { return; } string ValidateName(string name) { var target = parent.Children?.FirstOrDefault(e => e.Name == name); if (target != null) { return LangUtil.Format("参数名称重复2", name, parent.Name); } return null; } var win = new ArrayVarConfig(ValidateName) { VarType = item.TargetType, Owner = Window.GetWindow(this), }; if (win.ShowDialog() != true) { return; } newOne = new TestVariable { Name = win.VarName, IsArray = item.TargetIsArray, Type = win.VarType, Value = null }; for (int i = 0; i < win.Length; i++) { TestVariable child = new TestVariable { IsArray = false, Type = newOne.Type, Name = $"[{i}]", Value = TestVariable.DefaultValue(newOne.Type) }; newOne.Children.Add(child); } if (source == null) { parent.Children.Add(newOne); } else { parent.Children.Insert(parent.Children.IndexOf(source) + 1, newOne); } goto End; End: parentItem.IsExpanded = true; DispatcherHelper.WaitForPriority(); var scroll = treeView.Template.FindName("scroll", treeView) as ScrollViewer; var tvi = parentItem.ItemContainerGenerator.ContainerFromIndex(parent.Children.IndexOf(newOne)) as TreeListViewItem; if (!DispatcherHelper.IsElementVisibleInScrollViewer(scroll, tvi)) { Point offset = tvi.TransformToAncestor(scroll).Transform(new Point(0, 0)); scroll.ScrollToVerticalOffset(offset.Y); } if (string.IsNullOrEmpty(newOne.Name)) { var eb = DispatcherHelper.FindVisualChild(tvi); eb?.TryEdit(null); } tvi.IsSelected = true; } private void Delete_Click(object sender, RoutedEventArgs e) { var tv = ((TestVariableMenuItem)sender).Variable; var item = ((TestVariableMenuItem)sender).Tag as TreeListViewItem; if (item.parent == null) { Datasource.Remove(tv); } else { var parentData = item.parent.DataContext as TestVariable; parentData.Children.Remove(tv); if (parentData.IsArray) { for (int i = 0; i < parentData.Children.Count; i++) { parentData.Children[i].Name = $"[{i}]"; } } } } private void CopyName_Click(object sender, RoutedEventArgs e) { var item = ((TestVariableMenuItem)sender).Tag as TreeListViewItem; var tv = item?.DataContext as TestVariable; string name = ""; while (tv != null) { if (tv.IsArray) { name = tv.Name + name; } else { name = tv.Name + (string.IsNullOrEmpty(name) ? "" : $".{name}"); } item = item.parent; tv = item?.DataContext as TestVariable; } Clipboard.SetText(string.IsNullOrEmpty(Prefix) ? name : $"{Prefix}.{name}"); } private bool VariableNamePreSubmit(object sender, object value, object source) { EditBox eb = (EditBox)sender; TreeListViewItem item = (TreeListViewItem)eb.Tag; var parentItem = item.parent; if (parentItem == null) { return false; } if (string.IsNullOrEmpty(value.ToString())) { MessageBox.Show(LangUtil.Get("参数名称不能为空")); return false; } TestVariable parent = (TestVariable)parentItem.DataContext; var target = parent.Children.FirstOrDefault(c => c.Name == value.ToString()); if (target != null && target != source) { MessageBox.Show(LangUtil.Format("参数名称重复", value)); return false; } return true; } private void ChangeButton_Click(object sender, RoutedEventArgs e) { Button btn = (Button)sender; var menu = new ContextMenu(); btn.ContextMenu = menu; TestVariable source = (TestVariable)btn.DataContext; TreeListViewItem sourceItem = (TreeListViewItem)btn.Tag; TreeListViewItem parentItem = sourceItem.parent; void Menu_Closed(object sender, RoutedEventArgs e) { btn.ContextMenu = null; } void MenuItem_Click1(object sender, RoutedEventArgs e) { if (parentItem == null) { return; } TestVariable parent = (TestVariable)parentItem.DataContext; int index = parent.Children.IndexOf(source); if (index < 0) { return; } TestVariableMenuItem item = (TestVariableMenuItem)sender; if (item.TargetIsArray) { string ValidateName(string name) { if (string.IsNullOrEmpty(name)) { return LangUtil.Get("参数名称不能为空"); } var target = parent.Children?.FirstOrDefault(e => e.Name == name); if (target != null && target != source) { return LangUtil.Format("参数名称重复2", name, parent.Name); } return null; } var win = new ArrayVarConfig(ValidateName) { VarName = source.Name, VarType = item.TargetType, Length = source.Children.Count, Owner = Window.GetWindow(this), }; if (win.ShowDialog() != true) { return; } if (source.IsArray && source.Type == win.VarType && source.Children.Count == win.Length) { return; } ObservableCollection olds = new ObservableCollection(); if (source.IsArray && source.Type == win.VarType) { olds = source.Children; } TestVariable newOne = new TestVariable { IsArray = true, Name = win.VarName, Type = win.VarType }; for (int i = 0; i < win.Length; i++) { TestVariable child = new TestVariable { IsArray = false, Type = newOne.Type, Name = $"[{i}]", Value = i < olds.Count ? olds[i].Value : TestVariable.DefaultValue(newOne.Type) }; newOne.Children.Add(child); } parent.Children[index] = newOne; } else { if (!source.IsArray && source.Type == item.TargetType) { return; } TestVariable newOne = new TestVariable { IsArray = false, Name = source.Name, Type = item.TargetType, Value = TestVariable.DefaultValue(item.TargetType) }; parent.Children[index] = newOne; } } foreach (TestVariableType type in Enum.GetValues(typeof(TestVariableType))) { TestVariableMenuItem menuItem = new TestVariableMenuItem(); menuItem.Header = type.ToString(); menuItem.Variable = source; menuItem.Click += MenuItem_Click1; ; menuItem.TargetIsArray = false; menuItem.TargetType = type; menu.Items.Add(menuItem); } var arrItem = new MenuItem(); arrItem.Header = "Array Of..."; foreach (TestVariableType type in Enum.GetValues(typeof(TestVariableType))) { TestVariableMenuItem menuItem = new TestVariableMenuItem(); menuItem.Header = type.ToString(); menuItem.Variable = source; menuItem.Click += MenuItem_Click1; menuItem.TargetIsArray = true; menuItem.TargetType = type; arrItem.Items.Add(menuItem); } menu.Items.Add(arrItem); menu.IsOpen = true; } } }