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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//Copyright 2012-2019 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 Keysight.OpenTap.Wpf;
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media.Imaging;
 
namespace OpenTap.Plugins.PluginDevelopment
{
    // This example shows how to create a custom IControlProvider implementation.
    // This is a WPF specific extension to the Editor application that allows
    // adding custom controls to property grids. 
    //
    // In this example it will be shown how to display a picture in the settings of a test step in a generic way.
    //
    // In practice it should rarely be necessesary to add custom controls, but for some use cases it can be convenient.
    // For more flexible and platform independent extensions, consider adding custom annotations instead.
    //
 
    /// <summary>
    /// This control provider displays a string as an image when the PictureAttribute is used.
    /// </summary>
    [Display("Picture Control Provider", Group: "Example")]
    public class PictureControlProvider : IControlProvider
    {
        /// <summary> Select a fairly high order value. </summary>
        public double Order => 21;
 
        /// <summary>
        /// If the member has a PictureAttribute and it support being a string value, then it can probably be loaded as an image. 
        /// </summary>
        /// <param name="annotation"></param>
        /// <returns></returns>
        public FrameworkElement CreateControl(AnnotationCollection annotation)
        {
            // [Picture] is used.
            bool ispic = annotation.Get<IMemberAnnotation>()?.Member.HasAttribute<PictureAttribute>() ?? false;
            if (ispic == false) return null;
 
            // It is something that acts like a string.
            var stringValueProvider = annotation.Get<IStringValueAnnotation>();
            if (stringValueProvider == null) return null;
 
            // create a WPF image control.
            var img = new Image();
            BindingOperations.SetBinding(img, Image.SourceProperty, 
                new Binding(nameof(IStringValueAnnotation.Value))
                {
                    Source = stringValueProvider,
                    Converter = new FileToImageConverter() // this converter converts a file path (local or web) to an image source.
                });
            
            // ** Performance Notice ** 
            // This way of showing an image is quite slow. If it is a large image from a slow network drive it could cause the GUI to lag whenever the step is selected.
            // consider adding in memory caching of that is the case. For brevity this is not shown here.
            //
 
            // Get updates when changes happen to the annotation. For example when the value has changed.
            var update = annotation.Get<UpdateMonitor>(true);
            if(update != null)
            {
                // When the value changes explicitly reload the SourceProperty of the image.
                update.RegisterSourceUpdated(img, () => img.GetBindingExpression(Image.SourceProperty).UpdateTarget());
            }
 
            return img;
        }
        /// <summary> Slow but simple URI to image converter. </summary>
        class FileToImageConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                string path = (string)value;
                if (string.IsNullOrWhiteSpace(path)) return null;
                try
                {
                    // this could also be a url.
                    if (File.Exists(Path.GetFullPath(path)))
                        path = Path.GetFullPath(path);
                }
                catch
                {
 
                }
                try
                {
                    return new BitmapImage(new Uri(path), new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.CacheIfAvailable));
                }
                catch
                {
                    return null;
                }
            }
 
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException(); // this is not needed.
            }
        }
 
    }
 
    /// <summary> The PictureAttribute is used to mark that a string should be shown as a picture. </summary>
    public class PictureAttribute : Attribute
    {
 
    }
 
    [Display("Step With Picture", "This test step shows a picture", Groups: new[] { "Examples", "Plugin Development", "GUI" })]
    public class StepWithPicture : TestStep
    {
        [Picture] // 'Picture' should be shown as a picture.
        [Layout(LayoutMode.FullRow)] // Hide the name of the setting.
        public string Picture { get; set; } 
 
        /// <summary> The path to the picture, this can be a local file path or a HTTP address. </summary>
        [FilePath]
        [Display("Path")]
        public string Path
        {
            get { return Picture; }
            set { Picture = value; }
        }
 
        [Browsable(true)]
        public void ListAllControlProviders()
        {
            // it can sometimes be useful to list all plugins of a kind to see the order in which they are used.
            Log.Info("Listing Control Providers:");
            foreach (var controlProvider in PluginManager.GetPlugins<IControlProvider>().Select(x => (IControlProvider)Activator.CreateInstance(x)).ToArray().OrderBy(x => x.Order))
            {
                Log.Info("{0} : {1}", controlProvider.Order, controlProvider.ToString());
            }
        }
 
        public override void Run()
        {
 
        }
    }
}