chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//Copyright 2012-2021 Keysight Technologies
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
 
using System.ComponentModel;
using System.Linq;
 
namespace OpenTap.PluginDevelopment.Advanced_Examples
{
    // Icon annotations can be used to provide custom context menu / right-click functionality
    // in settings user interfaces. This can for example be used to clear the value of a property.
    // let's try this.
    // to do this, we need a IMenuModel, a IMenuModelFactory and some custom Icon names.
 
 
    /// <summary>
    /// This menu model factory takes an IMemberData and decides if it can generate a model for it.
    /// </summary>
    public class MenuModelExampleFactory : IMenuModelFactory
    {
        public IMenuModel CreateModel(IMemberData member)
        {
            // our MenuModelExample works for all members that defines DefaultValueAttribute.
            if (member.HasAttribute<DefaultValueAttribute>())
            {
                return new MenuModelExample(member);
            }
 
            return null;
        }
    }
    
    /// <summary>
    /// Shows how to add a menu model. This specific example is about DefaultValueAttribute.
    /// After compiling, try creating a DefaultValueExample test step and right-click it's setting. 
    /// </summary>
    public class MenuModelExample : IMenuModel
    {
        readonly IMemberData member;
 
        /// <summary> This class requires the member to be specified. </summary>
        public MenuModelExample(IMemberData member)
        {
            this.member = member;   
        }
        
        /// <summary>  When multi-selecting, this holds more than one value. Otherwise, it just contains one step. </summary>
        public object[] Source { get; set; }
 
        // some code to check that the test step is in a state that is allowed to be modified.
        static bool testStepLocked(ITestStep step)
        {
            if (step.IsReadOnly) return true;
            if (step.Parent is ITestStep step2) return testStepLocked(step2);
            if (step.Parent is TestPlan plan)
                return plan.IsRunning || plan.Locked;
            return true;
        }
        
        public bool CanExecuteRevert => member.Writable && !Source.OfType<ITestStep>().Any(testStepLocked);
        
        public bool IsAlreadyDefaultValues => Source.All(source =>
            Equals(member.GetValue(source), member.GetAttribute<DefaultValueAttribute>()?.Value));
        
        [Display("Revert to default", "Revert the value to it's default.")]
        // The is IconAnnotation is not generally important, it can be used by
        // user interfaces to identify which icon to associate with the action, if applicable.
        [IconAnnotation("MenuModelExample.RevertToDefault")]
        // This is needed because we are using Method annotations (not by default browsable).
        [Browsable(true)]
        // show it as grayed if not writable.
        [EnabledIf(nameof(CanExecuteRevert))] 
        // show as grayed if the current value is the same as the default
        [EnabledIf(nameof(IsAlreadyDefaultValues), false)] 
        public void RevertToDefault() // multiple methods can be added, each corresponding to another menu item.    
        {
            // get the default value and set the property value(s).
            // try multi selecting to understand why we do a 'for-each'.
            var defaultValue = member.GetAttribute<DefaultValueAttribute>().Value;
            foreach (var source in Source)
            {
                member.SetValue(source, defaultValue);
            }
        }
    }
}