Dock Panel for Connections

A simple panel to graphically show the Connections in the Bench Settings.

using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Keysight.OpenTap.Wpf;
using OpenTap;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using OxyPlot.Wpf;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using VerticalAlignment = System.Windows.VerticalAlignment;

namespace TTap.Plugins.Core.Common
    [Display("Connections Path Loss")]
    public class DockPanelConnections : ITapDockPanel
        static TraceSource _log = Log.CreateSource("ConnectionsPanel");

        private PlotModel _plotModel;

        public FrameworkElement CreateElement(ITapDockContext context)
            _log.Info($"Creating Connections chart...");

            var loadPlanBtn = new Button() { Content = "Update Chart" };

            var panel = new Grid();

            panel.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); // For the button
            panel.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) }); // For the PlotView
            Grid.SetRow(loadPlanBtn, 0); // Place the button in the first row
            loadPlanBtn.Click += (s, e) =>
                _log.Info($"Updating Connections chart...");

                _plotModel = CreatePlotModel(_plotModel);

            _plotModel = new PlotModel { Title = "Connection Path Loss", IsLegendVisible = true };


            var plotView = new PlotView
                Model = _plotModel,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Stretch,

            // Add the PlotView to your WPF Grid
            Grid.SetRow(plotView, 1);

            // Refresh the plot

            //// Move the tracker to the top left-hand corner, but it also removes the cross-hairs
            //// Create a new ControlTemplate
            //var template = new ControlTemplate();
            //// Create a FrameworkElementFactory that creates a TextBlock
            //var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
            //// Set the TextBlock's Text binding and Foreground color
            //textBlockFactory.SetBinding(TextBlock.TextProperty, new Binding());
            //textBlockFactory.SetValue(TextBlock.ForegroundProperty, Brushes.Black);
            //// Set the ControlTemplate's VisualTree to the created TextBlock
            //template.VisualTree = textBlockFactory;
            //// Set the PlotView's DefaultTrackerTemplate to the created ControlTemplate
            //plotView.DefaultTrackerTemplate = template;

            // Changes the tracker text to red.
            // Create a new Style for TrackerControl
            var style = new Style(typeof(TrackerControl));
            // Create a Setter for the Foreground property
            var setter = new Setter(TrackerControl.ForegroundProperty, Brushes.Red);
            // Add the Setter to the Style
            // Add the Style to the application's resources
            Application.Current.Resources.Add(typeof(TrackerControl), style);

            return panel;

        public double? DesiredWidth => 200;
        public double? DesiredHeight => 200;

        private static PlotModel CreatePlotModel(PlotModel plotModel)
            //var freqMin = ComponentSettings<ConnectionSettings>.Current.OfType<RfConnection>().Select(x => x.CableLoss.Min(cl => cl.Frequency)).Min();
            //var freqMax = ComponentSettings<ConnectionSettings>.Current.OfType<RfConnection>().Select(x => x.CableLoss.Max(cl => cl.Frequency)).Max();

            var xAxis = new LinearAxis
                Position = AxisPosition.Bottom,
                Title = "Frequency (MHz)",
                LabelFormatter = value => $"{value / 1e6} MHz"

            // Set up the Y axis
            var yAxis = new LinearAxis
                Position = AxisPosition.Left,
                Title = "Loss (dB)",

            if (plotModel.Axes.Any())


            if (plotModel.Series.Any())

            foreach (var con in ComponentSettings<ConnectionSettings>.Current)
                if (!(con is RfConnection rfCon)) continue;

                _log.Info($"Plotting {con.Name}");
                // Set up the series
                var series = new LineSeries { Title = rfCon.Name, TrackerFormatString = "{0}\n{1}: {2:###0,,.000}\n{3}: {4:F2}" };

                foreach (var point in rfCon.CableLoss)
                    series.Points.Add(new DataPoint(point.Frequency, point.Loss));

            if (plotModel.Legends.Any())

            plotModel.Legends.Add(new Legend() { LegendPosition = LegendPosition.RightTop, LegendPlacement = LegendPlacement.Outside });

            return plotModel;




Looks cool!

Reminds me of something similar I worked on recently: Live Results Panel

How many panels are you at now? :slight_smile:

Also did you make use of presets with this?

Cheers Rolf,

To be honest I only put this together because I was tired of going to Connections whenever I wanted to get the loss at a particular frequency when I was doing manual measurements using the test system. This is nice and easy because it’s simply a standalone panel, but using @alexander.larsen suggestions for ITapDockPanel to display information from an executing TestStep - #4 by alexander.larsen, is resulting in some nice ways to display much richer information to the operator during test execution. A bit trickier to implement but proving very useful.
I wonder if it’s possible to bind this to the RfConnection CableLoss table PropertyChanged event so I can do away with the Update Chart button?
My team is working on a few panels bound to specific test steps, but only because human intervention is required and because long executing test steps require a bit more than simple log entries to display information. But, yeah we have a few panels, now.
I’m not sure what you mean by ‘make use of presets with this?’. Is this a hidden feature, or something I to implement so the panel can be used with presets?

The Live Results Panel sounds useful and I will take a look in the new year.