Skip to content

File RangeSliderControl.xaml.cs

File List > PGRControls > RangeSliderControl.xaml.cs

Go to the documentation of this file

//=============================================================================
// Copyright (c) 2026 FLIR Integrated Imaging Solutions, Inc. All Rights Reserved.
//
// This software is the confidential and proprietary information of FLIR
// Integrated Imaging Solutions, Inc. ("Confidential Information"). You
// shall not disclose such Confidential Information and shall use it only in
// accordance with the terms of the license agreement you entered into
// with FLIR Integrated Imaging Solutions, Inc. (FLIR)
//
// FLIR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
// SOFTWARE, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE, OR NON-INFRINGEMENT. FLIR SHALL NOT BE LIABLE FOR ANY DAMAGES
// SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
// THIS SOFTWARE OR ITS DERIVATIVES.
//=============================================================================

using SpinnakerNET;
using SpinnakerNET.GenApi;
using SpinnakerNET.GUI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Security;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace SpinnakerNET.GUI.WPFControls
{


    public partial class RangeSliderControl : BaseClass
    {
        // AutoExposureTimeLowerLimit
        // AutoExposureTimeUpperLimit
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(RangeSliderControl));
        private static readonly log4net.ILog analytics = log4net.LogManager.GetLogger("Analytics");

        private bool _skipCallbacks = false;
        private bool _nameLabelOnTop;
        private bool _disposed = false;

        // Node names
        private string _lowerLimitNodeName;
        private string _upperLimitNodeName;

        // Float nodes
        IFloat _lowerLimitFloatNode;
        IFloat _upperLimitFloatNode;

        // Integer nodes
        IInteger _lowerLimitIntegerNode;
        IInteger _upperLimitIntegerNode;

        // back store
        private double _minimum = 0;
        private double _maximum = 10;
        private double _smallChange = 1;
        private double _largeChange = 2;
        private double _lowerValue = 0;
        private double _upperValue = 10;
        private string _unit = "unit";
        private System.Windows.Visibility _unitLabelVisibility = System.Windows.Visibility.Visible;
        private System.Windows.Visibility _upperLimitEditBoxVisibility = System.Windows.Visibility.Visible;
        private System.Windows.Visibility _lowerLimitEditBoxVisibility = System.Windows.Visibility.Visible;
        private double _minLabelWidth = 120;
        private double _maxLabelWidth = 300;
        private double _textBoxWidth = 60;
        private double _minEditboxWidth = 120;
        private double _maxEditboxWidth = 300;
        private string _upperTextboxTooltip = "";
        private string _lowerTextboxTooltip = "";

        // GenICam callback update thread
        BackgroundWorker _floatUpperLimitNodeUpdateWorker;
        BackgroundWorker _floatLowerLimitNodeUpdateWorker;

        BackgroundWorker _integerUpperLimitNodeUpdateWorker;
        BackgroundWorker _integerLowerLimitNodeUpdateWorker;

#region PROPERTIES
        public string LowerLimitNodeName
        {
            get
            {
                return _lowerLimitNodeName;
            }
        }

        public string UpperLimitNodeName
        {
            get
            {
                return _upperLimitNodeName;
            }
        }

        public double TextBoxWidth
        {
            get
            {
                return _textBoxWidth;
            }
            set
            {
                _textBoxWidth = value;
                NotifyPropertyChanged("TextBoxWidth");
            }
        }

        public double SmallChange
        {
            get
            {
                return _smallChange;
            }
            set
            {
                this._smallChange = value;
                NotifyPropertyChanged("SmallChange");
            }
        }

        public double LargeChange
        {
            get
            {
                return _largeChange;
            }
            set
            {
                this._largeChange = value;
                NotifyPropertyChanged("LargeChange");
            }
        }

        public double RangeSliderLowerValue
        {
            get
            {
                return _lowerValue;
            }
            set
            {
                _skipCallbacks = true;
                this._lowerValue = value;
                _skipCallbacks = false;
                NotifyPropertyChanged(nameof(RangeSliderLowerValue));
                NotifyPropertyChanged("LowerValueFormatted");
            }
        }

        public string LowerValueFormatted
        {
            get
            {
                return NumericFormatter.FormatNumericToString(RangeSliderLowerValue, DecimalPlaces);
            }
            set
            {
                double parsed;
                if (double.TryParse(value, out parsed))
                {
                    RangeSliderLowerValue = parsed;
                }
            }
        }

        public double RangeSliderUpperValue
        {
            get
            {
                return _upperValue;
            }
            set
            {
                _skipCallbacks = true;
                this._upperValue = value;
                _skipCallbacks = false;
                NotifyPropertyChanged(nameof(RangeSliderUpperValue));
                NotifyPropertyChanged("UpperValueFormatted");
            }
        }

        public string UpperValueFormatted
        {
            get
            {
                return NumericFormatter.FormatNumericToString(RangeSliderUpperValue, DecimalPlaces);
            }
            set
            {
                double parsed;
                if (double.TryParse(value, out parsed))
                {
                    RangeSliderUpperValue = parsed;
                }
            }
        }

        public double Minimum
        {
            get
            {
                return _minimum;
            }
            set
            {
                this._minimum = value;
                NotifyPropertyChanged("Minimum");
            }
        }

        public double Maximum
        {
            get
            {
                return _maximum;
            }
            set
            {
                this._maximum = value;
                NotifyPropertyChanged("Maximum");
            }
        }

        public string Unit
        {
            get
            {
                return _unit;
            }
            set
            {
                this._unit = value;
                NotifyPropertyChanged("Unit");
            }
        }

        public System.Windows.Visibility UnitLabelVisibility
        {
            get
            {
                return _unitLabelVisibility;
            }
            set
            {
                this._unitLabelVisibility = value;
                NotifyPropertyChanged("UnitLabelVisibility");
            }
        }

        public System.Windows.Visibility UpperLimitEditBoxVisibility
        {
            get
            {
                return _upperLimitEditBoxVisibility;
            }
            set
            {
                _upperLimitEditBoxVisibility = value;
                NotifyPropertyChanged("UpperLimitEditBoxVisibility");
            }
        }

        public System.Windows.Visibility LowerLimitEditBoxVisibility
        {
            get
            {
                return _lowerLimitEditBoxVisibility;
            }
            set
            {
                _lowerLimitEditBoxVisibility = value;
                NotifyPropertyChanged("LowerLimitEditBoxVisibility");
            }
        }

        public double MinLabelWidth
        {
            get
            {
                return _minLabelWidth;
            }
            set
            {
                _minLabelWidth = value;
                NotifyPropertyChanged("MinLabelWidth");
            }
        }

        public double MaxLabelWidth
        {
            get
            {
                return _maxLabelWidth;
            }
            set
            {
                _maxLabelWidth = value;
                NotifyPropertyChanged("MaxLabelWidth");
            }
        }

        public double MinEditBoxWidth
        {
            get
            {
                return _minEditboxWidth;
            }
            set
            {
                _minEditboxWidth = value;
                NotifyPropertyChanged("MinEditBoxWidth");
            }
        }

        public double MaxEditBoxWidth
        {
            get
            {
                return _maxEditboxWidth;
            }
            set
            {
                _maxEditboxWidth = value;
                NotifyPropertyChanged("MaxEditBoxWidth");
            }
        }

        public bool NameLabelOnTop
        {
            get
            {
                return _nameLabelOnTop;
            }
            set
            {
                this._nameLabelOnTop = value;
                if (_nameLabelOnTop)
                {
                    LabelAppearOnTop();
                }
            }
        }

        public string LowerValueTextboxTooltip
        {
            get
            {
                return _lowerTextboxTooltip;
            }
            set
            {
                if (_nodeType == typeof(Float))
                {
                    if (_lowerLimitFloatNode != null && _lowerLimitFloatNode != null && _lowerLimitFloatNode != null)
                    {
                        _lowerTextboxTooltip = string.Format(
                            "{0} Min:{1:0.##} Max:{2:0.##}",
                            _lowerLimitFloatNode.DisplayName,
                            _lowerLimitFloatNode.Min,
                            _lowerLimitFloatNode.Max);
                    }
                    else
                    {
                        _lowerTextboxTooltip = "";
                    }
                }
                else if (_nodeType == typeof(Integer))
                {
                    if (_lowerLimitIntegerNode != null && _lowerLimitIntegerNode != null &&
                        _lowerLimitIntegerNode != null)
                    {
                        _lowerTextboxTooltip = string.Format(
                            "{0} Min:{1:0.##} Max:{2:0.##}",
                            _lowerLimitIntegerNode.DisplayName,
                            _lowerLimitIntegerNode.Min,
                            _lowerLimitIntegerNode.Max);
                    }
                    else
                    {
                        _lowerTextboxTooltip = "";
                    }
                }
                else
                {
                    _lowerTextboxTooltip = "";
                }

                NotifyPropertyChanged("LowerValueTextboxTooltip");
            }
        }

        public string UpperValueTextboxTooltip
        {
            get
            {

                return _upperTextboxTooltip;
            }
            set
            {
                if (_nodeType == typeof(Float))
                {
                    if (_upperLimitFloatNode != null && _upperLimitFloatNode != null && _upperLimitFloatNode != null)
                    {
                        _upperTextboxTooltip = string.Format(
                            "{0} Min:{1:0.##} Max:{2:0.##}",
                            _upperLimitFloatNode.DisplayName,
                            _upperLimitFloatNode.Min,
                            _upperLimitFloatNode.Max);
                    }
                    else
                    {
                        _upperTextboxTooltip = "";
                    }
                }
                else if (_nodeType == typeof(Integer))
                {
                    if (_upperLimitIntegerNode != null && _upperLimitIntegerNode != null &&
                        _upperLimitIntegerNode != null)
                    {
                        _upperTextboxTooltip = string.Format(
                            "{0} Min:{1:0.##} Max:{2:0.##}",
                            _upperLimitIntegerNode.DisplayName,
                            _upperLimitIntegerNode.Min,
                            _upperLimitIntegerNode.Max);
                    }
                    else
                    {
                        _upperTextboxTooltip = "";
                    }
                }
                else
                {
                    _upperTextboxTooltip = "";
                }

                NotifyPropertyChanged("UpperValueTextboxTooltip");
            }
        }
#endregion

        public RangeSliderControl()
        {
            InitializeComponent();
            this.DataContext = this;

            _floatUpperLimitNodeUpdateWorker = new BackgroundWorker();
            _floatUpperLimitNodeUpdateWorker.DoWork += floatUpperLimitNodeUpdateWorker_DoWork;
            _floatUpperLimitNodeUpdateWorker.RunWorkerCompleted += UpperLimitNodeUpdateWorker_RunWorkerCompleted;

            _floatLowerLimitNodeUpdateWorker = new BackgroundWorker();
            _floatLowerLimitNodeUpdateWorker.DoWork += floatLowerLimitNodeUpdateWorker_DoWork;
            _floatLowerLimitNodeUpdateWorker.RunWorkerCompleted += LowerLimitNodeUpdateWorker_RunWorkerCompleted;

            _integerUpperLimitNodeUpdateWorker = new BackgroundWorker();
            _integerUpperLimitNodeUpdateWorker.DoWork += integerUpperLimitUpdateWorker_DoWork;
            _integerUpperLimitNodeUpdateWorker.RunWorkerCompleted += UpperLimitNodeUpdateWorker_RunWorkerCompleted;

            _integerLowerLimitNodeUpdateWorker = new BackgroundWorker();
            _integerLowerLimitNodeUpdateWorker.DoWork += integerLowerLimitUpdateWorker_DoWork;
            _integerLowerLimitNodeUpdateWorker.RunWorkerCompleted += LowerLimitNodeUpdateWorker_RunWorkerCompleted;
        }

        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                }

                // Consider GenICam callbacks as unmanaged resource
                Disconnect();
                _disposed = true;
            }
            // Call Dispose in the base class.
            base.Dispose(disposing);
        }

        private void LowerSlider_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (_nodeType == typeof(Integer))
            {
                AnalyticsNodeUpdateAction(analytics, _lowerLimitIntegerNode, RangeSliderLowerValue.ToString());
            }
            else if (_nodeType == typeof(Float))
            {
                AnalyticsNodeUpdateAction(analytics, _lowerLimitFloatNode, RangeSliderLowerValue.ToString());
            }
        }

        private void UpperSlider_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (_nodeType == typeof(Integer))
            {
                AnalyticsNodeUpdateAction(analytics, _upperLimitIntegerNode, RangeSliderUpperValue.ToString());
            }
            else if (_nodeType == typeof(Float))
            {
                AnalyticsNodeUpdateAction(analytics, _upperLimitFloatNode, RangeSliderUpperValue.ToString());
            }
        }

#region ICAMERACONTROL_INTERFACE
        public override void SetMin(double min)
        {
            this.UpperSlider.Minimum = min;
            this.LowerSlider.Minimum = min;
        }

        public override void SetMax(double max)
        {
            this.UpperSlider.Maximum = max;
            this.LowerSlider.Maximum = max;
        }

        public override void SetNameLabelVisibility(System.Windows.Visibility visibility)
        {
            this.NameLabelVisibility = visibility;
        }

        public override void SetRefreshTime(int seconds)
        {
            this.RefreshTime = seconds;

            // Start timer if an valid refresh time was provided
            if (this._refreshTime > 0)
            {
                try
                {
                    _timer = new System.Windows.Forms.Timer();
                    _timer.Interval = this._refreshTime;
                    _timer.Tick += new EventHandler(timer_Tick);
                    _timer.Start();
                }
                catch (System.Exception /*ex*/)
                {
                }
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                Refresh();
            }
            catch (System.Exception /*ex*/)
            {
                Debug.WriteLine(string.Format("Problem retrieving new value from {0} node", _propertyToControl));
            }
        }

        public override void Refresh()
        {
            try
            {
                RetrieveLatestLowerLimitNodeValue();
                RetrieveLatestUpperLimitNodeValue();

                // Cause tooltip to be reevaluated
                LowerValueTextboxTooltip = "";
                UpperValueTextboxTooltip = "";
            }
            catch (System.Exception /*ex*/)
            {
                Debug.WriteLine("Problem updating control values.");
            }
        }

        public void SetUnitLabel(string unitLabel)
        {
            this.Unit = unitLabel;
        }

        public void SetUnitLabelVisibility(System.Windows.Visibility visibility)
        {
            this.UnitLabelVisibility = visibility;
        }

        public void SetLowerLimitEditBoxVisibility(bool overrideDefault, System.Windows.Visibility visibility)
        {
            if (overrideDefault)
            {
                LowerLimitEditBoxVisibility = visibility;
            }
        }

        public void SetUpperLimitEditBoxVisibility(bool overrideDefault, System.Windows.Visibility visibility)
        {
            if (overrideDefault)
            {
                UpperLimitEditBoxVisibility = visibility;
            }
        }

        public override double GetMin()
        {
            return Minimum;
        }

        public override double GetMax()
        {
            return Maximum;
        }

        public override void Connect(INodeMap nodemap, string nodename, string namelabel = "")
        {
            throw new NotImplementedException(string.Format(
                @"See Connect(NodeMap nodemap, string lowerLimitNodeName, string upperLimitNodeName, string namelabel"));
        }

        public void Connect(
            INodeMap nodemap,
            string lowerLimitNodeName,
            string upperLimitNodeName,
            string namelabel = "")
        {
            if (nodemap != null && lowerLimitNodeName.Length > 0 && upperLimitNodeName.Length > 0)
            {
                try
                {
                    // Acquire device serial number
                    if (string.IsNullOrWhiteSpace(_serialNumber))
                    {
                        _serialNumber = RetrieveSerialNumber(nodemap);
                    }

                    // Acquire device model name
                    if (string.IsNullOrWhiteSpace(_deviceModelName))
                    {
                        _deviceModelName = RetrieveModelName(nodemap);
                    }

                    // Get Node type
                    var node = nodemap.GetNode<INode>(lowerLimitNodeName);
                    if (node == null)
                    {
                        _featureAvailable = false;
                        log.Debug(string.Format(
                            "{2}Unsupported feature in RangeSlider: {0} {1}",
                            lowerLimitNodeName,
                            upperLimitNodeName,
                            FormattedSerialNumber));
                        throw new Exception(string.Format(
                            "{2}Unsupported feature in RangeSlider: {0} {1}",
                            lowerLimitNodeName,
                            upperLimitNodeName,
                            FormattedSerialNumber));
                    }

                    this._nodeType = node.GetType();

                    _lowerLimitNodeName = lowerLimitNodeName;
                    _upperLimitNodeName = upperLimitNodeName;

                    if (_nodeType == typeof(Float))
                    {
                        // Connect to nodes
                        _lowerLimitFloatNode = nodemap.GetNode<Float>(lowerLimitNodeName);
                        _upperLimitFloatNode = nodemap.GetNode<Float>(upperLimitNodeName);

                        // Set display precision from camera XML
                        if (_lowerLimitFloatNode.IsReadable)
                        {
                            try
                            {
                                DecimalPlaces = ClampDisplayPrecision(_lowerLimitFloatNode.DisplayPrecision);
                            }
                            catch (System.Exception /*ex*/)
                            {
                                // If DisplayPrecision is not available, keep existing DecimalPlaces value (default is 2)
                            }
                        }

                        // Update Min and Max
                        this.Minimum = _lowerLimitFloatNode.Min;
                        this.Maximum = _upperLimitFloatNode.Max;

                        /* There is no increment for IFloat node */
                        // Set Small and Large changes
                        this.SmallChange = 1;
                        this.LargeChange = 2;

                        // Get current values
                        this.RangeSliderLowerValue = _lowerLimitFloatNode.Value;
                        this.RangeSliderUpperValue = _upperLimitFloatNode.Value;

                        // Set unit label
                        unitLabel.Content = _lowerLimitFloatNode.Unit;

                        // Set name label
                        if (namelabel.Length != 0)
                        {
                            this.nameLabel.Content = namelabel;
                        }
                    }
                    else if (_nodeType == typeof(Integer))
                    {
                        // Connect to nodes
                        _lowerLimitIntegerNode = nodemap.GetNode<Integer>(lowerLimitNodeName);
                        _upperLimitIntegerNode = nodemap.GetNode<Integer>(upperLimitNodeName);

                        // Update Min and Max
                        this.Minimum = _lowerLimitIntegerNode.Min;
                        this.Maximum = _upperLimitIntegerNode.Max;

                        // Set Small and Large changes
                        this.SmallChange = _lowerLimitIntegerNode.Increment;
                        this.LargeChange = SmallChange * 2;

                        // Get current values
                        this.RangeSliderLowerValue = _lowerLimitIntegerNode.Value;
                        this.RangeSliderUpperValue = _upperLimitIntegerNode.Value;

                        // Set unit label
                        // GenICam Integer node does not have a unit member
                        unitLabel.Content = "";

                        // Set name label
                        if (namelabel.Length != 0)
                        {
                            this.nameLabel.Content = namelabel;
                        }
                    }
                    else
                    {
                        // Unsupported node type
                        throw new Exception("Unsupported node types. RangeSlider supports IFloat and IInteger types");
                    }

                    RegisterInterfaceEvents();

                    _featureAvailable = true;
                }
                catch (System.Exception ex)
                {
                    _featureAvailable = false;
                    UpdateControlStatus();
                    log.Debug(string.Format("{0}Problem accessing nodes in RangeSlider.", FormattedSerialNumber), ex);
                    throw new Exception(string.Format(
                        "{1}Problem accessing nodes in RangeSlider. {0}", ex.Message, FormattedSerialNumber));
                }
            }

            _initializing = true;

            try
            {
                RetrieveLatestLowerLimitNodeValue();
                RetrieveLatestUpperLimitNodeValue();
                UpdateControlStatus();
                RegisterGenICamCallbacks();

                this.Loaded += Slider_Loaded;

                _featureAvailable = true;
            }
            catch (System.Exception /*ex*/)
            {
            }
            finally
            {
                _initializing = false;
            }
        }

        public override void Disconnect()
        {
            try
            {
                UnregisterGenICamCallbacks();

                if (_nodeType == typeof(Float))
                {
                    _lowerLimitFloatNode = null;
                    _upperLimitFloatNode = null;
                }
                else if (_nodeType == typeof(Integer))
                {
                    _lowerLimitIntegerNode = null;
                    _upperLimitIntegerNode = null;
                }
            }
            catch (System.Exception /*ex*/)
            {
            }
        }

        private void RegisterGenICamCallbacks()
        {
            if (_nodeType == typeof(Float))
            {
                _lowerLimitFloatNode.Updated += LowerLimitFloatNode_Updated;
                _upperLimitFloatNode.Updated += UpperLimitFloatNode_Updated;
            }
            else if (_nodeType == typeof(Integer))
            {
                _lowerLimitIntegerNode.Updated += LowerLimitIntegerNode_Updated;
                _upperLimitIntegerNode.Updated += UpperLimitIntegerNode_Updated;
            }
        }

        private void UnregisterGenICamCallbacks()
        {
            if (_lowerLimitFloatNode != null && _upperLimitFloatNode != null)
            {
                if (_nodeType == typeof(Float))
                {
                    _lowerLimitFloatNode.Updated -= LowerLimitFloatNode_Updated;
                    _upperLimitFloatNode.Updated -= UpperLimitFloatNode_Updated;
                }
                else if (_nodeType == typeof(Integer))
                {
                    _lowerLimitIntegerNode.Updated -= LowerLimitIntegerNode_Updated;
                    _upperLimitIntegerNode.Updated -= UpperLimitIntegerNode_Updated;
                }
            }
        }

        [HandleProcessCorruptedStateExceptions]
        [SecurityCritical]
        private void
        UpperLimitNodeUpdateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                _initializing = true;
                NodeStatus currentNodeStatus = (NodeStatus)e.Result;

                RangeSliderUpperValue = currentNodeStatus.CurrentValue;
                NotifyPropertyChanged("UpperValueFormatted");

                if (!currentNodeStatus.IsAvailable)
                {
                    SetControlStatus(false);
                    return;
                }

                if (!currentNodeStatus.IsWritable)
                {
                    SetControlStatus(false);
                    return;
                }
                else
                {
                    SetControlStatus(true);
                }

                // Cause tooltip to be reevaluated
                LowerValueTextboxTooltip = "";
                UpperValueTextboxTooltip = "";
            }
            catch (System.Exception /*ex*/)
            {
            }
            finally
            {
                _initializing = false;
            }
        }

        private void LowerLimitNodeUpdateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                _initializing = true;
                NodeStatus currentNodeStatus = (NodeStatus)e.Result;

                RangeSliderLowerValue = currentNodeStatus.CurrentValue;
                NotifyPropertyChanged("LowerValueFormatted");

                if (!currentNodeStatus.IsAvailable)
                {
                    SetControlStatus(false);
                    return;
                }

                if (!currentNodeStatus.IsWritable)
                {
                    SetControlStatus(false);
                    return;
                }
                else
                {
                    SetControlStatus(true);
                }

                // Cause tooltip to be reevaluated
                LowerValueTextboxTooltip = "";
                UpperValueTextboxTooltip = "";
            }
            catch (System.Exception /*ex*/)
            {
            }
            finally
            {
                _initializing = false;
            }
        }

        private void floatUpperLimitNodeUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            INode node = e.Argument as INode;
            BackgroundWorker worker = sender as BackgroundWorker;
            NodeStatus currentNodeStatus = new NodeStatus(node);

            string logMessage = string.Format("Spinnaker Callback: Feature=\"{0}\"", currentNodeStatus.NodeName);

            logMessage += string.Format(", Value=\"{0}\"", currentNodeStatus.CurrentStringValue);

            LogNodeCallback(log, currentNodeStatus.NodeName, logMessage);

            e.Result = currentNodeStatus;
        }

        private void floatLowerLimitNodeUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            INode node = e.Argument as INode;
            BackgroundWorker worker = sender as BackgroundWorker;
            NodeStatus currentNodeStatus = new NodeStatus(node);

            string logMessage = string.Format("Spinnaker Callback: Feature=\"{0}\"", currentNodeStatus.NodeName);

            logMessage += string.Format(", Value=\"{0}\"", currentNodeStatus.CurrentStringValue);

            LogNodeCallback(log, currentNodeStatus.NodeName, logMessage);

            e.Result = currentNodeStatus;
        }

        private void integerUpperLimitUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            INode node = e.Argument as INode;
            BackgroundWorker worker = sender as BackgroundWorker;
            NodeStatus currentNodeStatus = new NodeStatus(node);

            string logMessage = string.Format("Spinnaker Callback: Feature=\"{0}\"", currentNodeStatus.NodeName);

            logMessage += string.Format(", Value=\"{0}\"", currentNodeStatus.CurrentStringValue);

            LogNodeCallback(log, currentNodeStatus.NodeName, logMessage);

            e.Result = currentNodeStatus;
        }

        private void integerLowerLimitUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            INode node = e.Argument as INode;
            BackgroundWorker worker = sender as BackgroundWorker;
            NodeStatus currentNodeStatus = new NodeStatus(node);

            string logMessage = string.Format("Spinnaker Callback: Feature=\"{0}\"", currentNodeStatus.NodeName);

            logMessage += string.Format(", Value=\"{0}\"", currentNodeStatus.CurrentStringValue);

            LogNodeCallback(log, currentNodeStatus.NodeName, logMessage);

            e.Result = currentNodeStatus;
        }

        void UpperLimitIntegerNode_Updated(INode node)
        {
            if (_initializing || _skipCallbacks)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_integerUpperLimitNodeUpdateWorker.IsBusy)
            {
                return;
            }

            if (!IsVisible)
            {
                return;
            }

            _integerUpperLimitNodeUpdateWorker.RunWorkerAsync(node);
        }

        void LowerLimitIntegerNode_Updated(INode node)
        {
            if (_initializing || _skipCallbacks)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_integerLowerLimitNodeUpdateWorker.IsBusy)
            {
                return;
            }

            if (!IsVisible)
            {
                return;
            }

            _integerLowerLimitNodeUpdateWorker.RunWorkerAsync(node);
        }

        void UpperLimitFloatNode_Updated(INode node)
        {
            if (_initializing || _skipCallbacks)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_floatUpperLimitNodeUpdateWorker.IsBusy)
            {
                return;
            }

            if (!IsVisible)
            {
                return;
            }

            _floatUpperLimitNodeUpdateWorker.RunWorkerAsync(node);
        }

        void LowerLimitFloatNode_Updated(INode node)
        {
            if (_initializing || _skipCallbacks)
            {
                return;
            }

            if (_nodeMap == null)
            {
                return;
            }

            if (_floatLowerLimitNodeUpdateWorker.IsBusy)
            {
                return;
            }

            if (!IsVisible)
            {
                return;
            }

            _floatLowerLimitNodeUpdateWorker.RunWorkerAsync(node);
        }

        private void SetControlStatus(bool enabledStatus)
        {
            IsEditable = enabledStatus;
        }

        private void UpdateControlStatus()
        {
            try
            {
                if (_nodeType == typeof(Float))
                {
                    if (_lowerLimitFloatNode == null || _upperLimitFloatNode == null ||
                        !_lowerLimitFloatNode.IsAvailable || !_upperLimitFloatNode.IsAvailable)
                    {
                        SetControlStatus(false);
                        return;
                    }

                    if (!_lowerLimitFloatNode.IsWritable || !_upperLimitFloatNode.IsWritable)
                    {
                        SetControlStatus(false);
                        return;
                    }
                    else
                    {
                        SetControlStatus(true);
                    }

                    // Cause tooltip to be reevaluated
                    LowerValueTextboxTooltip = "";
                    UpperValueTextboxTooltip = "";
                }
                else if (_nodeType == typeof(Integer))
                {
                    if (_lowerLimitIntegerNode == null || _upperLimitIntegerNode == null ||
                        !_lowerLimitIntegerNode.IsAvailable || !_upperLimitIntegerNode.IsAvailable)
                    {
                        SetControlStatus(false);
                        return;
                    }

                    if (!_lowerLimitIntegerNode.IsWritable || !_upperLimitIntegerNode.IsWritable)
                    {
                        SetControlStatus(false);
                        return;
                    }
                    else
                    {
                        SetControlStatus(true);
                    }

                    // Cause tooltip to be reevaluated
                    LowerValueTextboxTooltip = "";
                    UpperValueTextboxTooltip = "";
                }
            }
            catch (System.Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

#endregion

#region Events

        protected void Slider_Loaded(object sender, RoutedEventArgs e)
        {
            LowerSlider.ValueChanged += LowerSlider_ValueChanged;
            UpperSlider.ValueChanged += UpperSlider_ValueChanged;
        }

        private void LowerSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_skipCallbacks)
            {
                return;
            }

            SetValueToLowerNode();
        }

        private void UpperSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_skipCallbacks)
            {
                return;
            }

            SetValueToUpperNode();
        }

        private void lowerValueTextbox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                BindingExpression exp = this.lowerValueTextbox.GetBindingExpression(TextBox.TextProperty);
                exp.UpdateSource();

                if (_nodeType == typeof(Integer))
                {
                    AnalyticsNodeUpdateAction(analytics, _lowerLimitIntegerNode, RangeSliderLowerValue.ToString());
                }
                else if (_nodeType == typeof(Float))
                {
                    AnalyticsNodeUpdateAction(analytics, _lowerLimitFloatNode, RangeSliderLowerValue.ToString());
                }
            }
        }

        private void upperValueTextbox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                BindingExpression exp = this.upperValueTextbox.GetBindingExpression(TextBox.TextProperty);
                exp.UpdateSource();

                if (_nodeType == typeof(Integer))
                {
                    AnalyticsNodeUpdateAction(analytics, _upperLimitIntegerNode, RangeSliderUpperValue.ToString());
                }
                else if (_nodeType == typeof(Float))
                {
                    AnalyticsNodeUpdateAction(analytics, _upperLimitFloatNode, RangeSliderUpperValue.ToString());
                }
            }
        }

#endregion

        private void SetValueToLowerNode()
        {
            if (_lowerLimitFloatNode == null)
            {
                return;
            }

            try
            {
                double valueToWrite = NormalizeFloatValueToIncrement(_lowerLimitFloatNode, RangeSliderLowerValue);

                // Log user initiated changes
                LogNodeUpdateAction(log, _lowerLimitFloatNode, valueToWrite.ToString());

                _lowerLimitFloatNode.Value = valueToWrite;
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(string.Format("Error setting Lower Value {0}", ex.Message));
            }

            RetrieveLatestLowerLimitNodeValue();
            RetrieveLatestUpperLimitNodeValue();
        }

        private void SetValueToUpperNode()
        {
            if (_upperLimitFloatNode == null)
            {
                return;
            }

            try
            {
                double valueToWrite = NormalizeFloatValueToIncrement(_upperLimitFloatNode, RangeSliderUpperValue);

                // Log user initiated changes
                LogNodeUpdateAction(log, _upperLimitFloatNode, valueToWrite.ToString());

                _upperLimitFloatNode.Value = valueToWrite;
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(string.Format("Error setting Upper Value {0}", ex.Message));
            }

            RetrieveLatestUpperLimitNodeValue();
            RetrieveLatestLowerLimitNodeValue();
        }

        private double NormalizeFloatValueToIncrement(IFloat node, double value)
        {
            return NormalizeFloatValueToNodeIncrement(node, value);
        }

        private void RetrieveLatestLowerLimitNodeValue()
        {
            try
            {
                _skipCallbacks = true;
                if (_nodeType == typeof(Integer) && _lowerLimitIntegerNode != null)
                {
                    RangeSliderLowerValue = _lowerLimitIntegerNode.Value;
                }
                else if (_nodeType == typeof(Float) && _lowerLimitFloatNode != null)
                {
                    RangeSliderLowerValue = _lowerLimitFloatNode.Value;
                }
                NotifyPropertyChanged("LowerValueFormatted");
            }
            catch (System.Exception /*ex*/)
            {
            }
            finally
            {
                _skipCallbacks = false;
            }
        }

        private void RetrieveLatestUpperLimitNodeValue()
        {
            try
            {
                _skipCallbacks = true;
                if (_nodeType == typeof(Float) && _upperLimitFloatNode != null )
                {
                    RangeSliderUpperValue = _upperLimitFloatNode.Value;
                }
                else if (_nodeType == typeof(Integer) && _upperLimitIntegerNode != null)
                {
                    RangeSliderUpperValue = _upperLimitIntegerNode.Value;
                }
                NotifyPropertyChanged("UpperValueFormatted");
            }
            catch (System.Exception /*ex*/)
            {
            }
            finally
            {
                _skipCallbacks = false;
            }
        }

        private void LabelAppearOnTop()
        {
            this.rootLayout.Orientation = Orientation.Vertical;
        }
    }

}