Skip to content

File ComboBoxControl.xaml.cs

File List > PGRControls > ComboBoxControl.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 System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using SpinnakerNET.GenApi;
using System.Windows.Threading;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using System.Security;
using System.Threading;

namespace SpinnakerNET.GUI
{
    namespace WPFControls
    {


        public sealed partial class ComboBoxControl : BaseClass
        {
            private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(ComboBoxControl));
            private static readonly log4net.ILog analytics = log4net.LogManager.GetLogger("Analytics");
#region FIELDS
            private IEnum _enumNode;
            private Dictionary<string, long>_entryTable;
            private bool _skipCallback = false;
            private bool _disposed = false;
            private ObservableCollection<Entry>_entries;
            private Entry _currentlySelectedEntry;
            private bool _isEnabled; // whether combobox is enabled
            private bool _userEditing = false;

            // GenICam callback update thread
            BackgroundWorker _updateWorker;

#endregion

#region PROPERTIES
            public ObservableCollection<Entry>Entries
            {
                get
                {
                    if (_entries == null)
                    {
                        _entries = new ObservableCollection<Entry>();
                    }

                    return _entries;
                }
                private set
                {
                    _entries = value;
                    NotifyPropertyChanged("Entries");
                }
            }

            public Entry CurrentlySelectedEntry
            {
                get
                {
                    return _currentlySelectedEntry;
                }
                set
                {
                    _currentlySelectedEntry = value;
                    NotifyPropertyChanged("CurrentlySelectedEntry");
                }
            }

            public bool IsCBOEnabled
            {
                get
                {
                    return _isEnabled;
                }
                private set
                {
                    _isEnabled = value;
                    NotifyPropertyChanged("IsCBOEnabled");
                }
            }

            public double ControlMargin
            {
                get
                {
                    return this.Margin.Left;
                }
                set
                {
                    this.Margin = new Thickness(value);
                }
            }
#endregion

#region Constructor
            public ComboBoxControl()
            {
                InitializeComponent();
                _entryTable = new Dictionary<string, long>();
                this.DataContext = this;
                _updateWorker = new BackgroundWorker();
                _updateWorker.DoWork += _updateWorker_DoWork;
                _updateWorker.RunWorkerCompleted += _updateWorker_RunWorkerCompleted;
            }

            ~ComboBoxControl()
            {
                Disconnect();
            }

            internal ComboBoxControl(ComboBoxControl originalControl)
            {
                InitializeComponent();
                _entryTable = new Dictionary<string, long>();
                this.DataContext = this;
                _updateWorker = new BackgroundWorker();
                _updateWorker.DoWork += _updateWorker_DoWork;
                _updateWorker.RunWorkerCompleted += _updateWorker_RunWorkerCompleted;

                this.Entries = originalControl.Entries;
                this.CurrentlySelectedEntry = originalControl.CurrentlySelectedEntry;

                // Common Properties
                this.SetMapper(originalControl.GetMapper());
                this.Connect(
                    originalControl.NodeMap, originalControl.GetNodeToControl(), originalControl.GetControlNameLabel());
                this.SetNameLabelBoldness(originalControl.NameLabelBoldness);
                this.SetVisibility(originalControl.Visibility);
                this.SetControlID(originalControl.ControlID);
                this.SetMin(originalControl.GetMin());
                this.SetMax(originalControl.GetMax());
                this.SetControlNameLabel(originalControl.ControlNameLabel);
                this.SetNameLabelVisibility(originalControl.GetNameLabelVisibility());
                this.SetNameLabelBoldness(originalControl.GetNameLabelBoldness());
                try
                {
                    this.SetToolTip(originalControl.GetToolTip());
                }
                catch (System.Exception /*ex*/)
                {
                }
                this.SetMaximumControlSize(originalControl.GetMaximumControlSize());
                this.SetMinimumControlSize(originalControl.GetMinimumControlSize());
                this.SetControlHeight(originalControl.GetControlHeight());
                this.SetControlWidth(originalControl.GetControlWidth());
                this.SetRefreshTime(originalControl.GetRefreshTime());
                this.SetMargin(originalControl.GetMargin());
            }

            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);
            }
#endregion

#region ICAMERACONTROL
            public override void Connect(INodeMap nodemap, string nodename, string namelabel = "")
            {
                if (nodename.Length != 0 && nodemap != null)
                {
                    _initializing = true;

                    // Acquire device serial number
                    if (string.IsNullOrWhiteSpace(_serialNumber))
                    {
                        _serialNumber = RetrieveSerialNumber(nodemap);
                    }

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

                    try
                    {
                        this._nodeMap = nodemap;
                        this._propertyToControl = nodename;

                        _enumNode = nodemap.GetNode<IEnum>(this._propertyToControl);

                        if (_enumNode == null)
                        {
                            throw new Exception("Feature not found");
                        }

                        this._nodeType = _enumNode.GetType();
                        ControlToolTip = _enumNode.ToolTip;

                        _featureAvailable = _enumNode.IsImplemented;

                        if (namelabel.Length > 0)
                        {
                            SetControlNameLabel(namelabel);
                            SetNameLabelVisibility(System.Windows.Visibility.Visible);
                            SetToolTip(_enumNode.ToolTip.ToString());
                        }
                        else
                        {
                            SetControlNameLabel(_enumNode.DisplayName);
                        }

                        PopulateEntries();

                        // Register GenIcam callback
                        _enumNode.Updated += enumNode_Updated;

                        _callbackRegistered = true;

                        RegisterInterfaceEvents();
                    }
                    catch (System.Exception ex)
                    {
                        // Debug.WriteLine(string.Format("Problem accessing {0} node", this._propertyToControl));
                        _featureAvailable = false;
                        log.Debug(
                            string.Format(
                                "{1}Problem accessing {0} node.", this._propertyToControl, FormattedSerialNumber),
                            ex);
                        throw new Exception(string.Format(
                            "{2}Problem accessing {0} node. {1}",
                            this._propertyToControl,
                            ex.Message,
                            FormattedSerialNumber));
                    }
                    finally
                    {
                        _initializing = false;
                    }
                }
                else
                {
                    Entries.Clear();

                    Entry entry = new Entry("N/A", -1);
                    Entries.Add(entry);

                    CurrentlySelectedEntry = entry;
                    IsCBOEnabled = false;
                    _featureAvailable = false;
                }
            }

            private void PopulateEntries()
            {
                if (_entries == null)
                {
                    _entries = new ObservableCollection<Entry>();
                }
                else
                {
                    Entries.Clear();
                }

                if (_enumNode == null)
                {
                    log.Debug(string.Format("{1} {0} node is null.", this._propertyToControl, FormattedSerialNumber));
                    return;
                }

                try
                {
                    foreach(var node in _enumNode.Entries)
                    {
                        // Keep track of all possible entries and their Node value
                        if (node.IsImplemented && node.IsAvailable)
                        {
                            Entry newEntry = new Entry(node.DisplayName, node.Value);
                            if (newEntry != null)
                            {
                                Entries.Add(newEntry);
                            }
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.WriteLine(
                        string.Format("Problem updating ComboBox for {0}. {1}", PropertyToControl, ex.Message));
                }

                UpdateControlStatus();
            }

            public override void Disconnect()
            {
                try
                {
                    if (_enumNode != null && _callbackRegistered)
                    {
                        _enumNode.Updated -= enumNode_Updated;
                    }

                    _enumNode = null;
                    _mapper = null;
                    _callbackRegistered = false;
                    base.Disconnect();
                }
                catch (System.Exception /*ex*/)
                {
                }
            }

            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*/)
                    {
                    }
                }
            }

            public override void Refresh()
            {
                if (_enumNode == null || _nodeMap == null)
                {
                    return;
                }

                PopulateEntries();
            }
#endregion

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

            [HandleProcessCorruptedStateExceptions]
            [SecurityCritical]
            private void
            _updateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Cancelled)
                {
                    return;
                }

                try
                {
                    _isUpdating = true;
                    _skipCallback = true;
                    _initializing = true;

                    INode node = e.Result as INode;
                    NodeStatus currentNodeStatus = new NodeStatus(node);
                    if (currentNodeStatus.ExceptionObject != null)
                    {
                        return;
                    }

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

                    if (currentNodeStatus.IsReadable)
                    {
                        try
                        {
                            logMessage += string.Format(", Value=\"{0}\"", currentNodeStatus.CurrentEnumValue.String);
                        }
                        catch (Exception ex)
                        {
                            log.Debug(string.Format(
                                "Exception while getting the value of currentNodeStatus.CurrentEnumValue: Error: {0}",
                                ex.Message));
                        }
                    }

                    LogNodeCallback(log, currentNodeStatus.NodeName, logMessage);

                    if (ControlNameLabel != currentNodeStatus.NodeDisplayName)
                    {
                        ControlNameLabel = currentNodeStatus.NodeDisplayName;
                    }

                    if (!Dispatcher.CheckAccess())
                    {
                        Dispatcher.BeginInvoke(
                            (Action)(() => {
                                if (_entries == null)
                                {
                                    _entries = new ObservableCollection<Entry>();
                                }
                                else
                                {
                                    Entries.Clear();
                                }

                                if (Entries != currentNodeStatus.Entries)
                                {
                                    Entries = currentNodeStatus.Entries;
                                }

                                foreach(var entry in Entries)
                                {
                                    if (currentNodeStatus.CurrentEnumValue.Int == entry.Value)
                                    {
                                        combobox.SelectionChanged -= combobox_SelectionChanged;
                                        CurrentlySelectedEntry = entry;
                                        combobox.SelectionChanged += combobox_SelectionChanged;
                                        break;
                                    }
                                }
                            }),
                            DispatcherPriority.Background,
                            null);
                    }
                    else
                    {
                        if (_entries == null)
                        {
                            _entries = new ObservableCollection<Entry>();
                        }
                        else
                        {
                            Entries.Clear();
                        }

                        if (Entries != currentNodeStatus.Entries)
                        {
                            Entries = currentNodeStatus.Entries;
                        }

                        foreach(var entry in Entries)
                        {
                            if (currentNodeStatus.CurrentEnumValue.Int == entry.Value)
                            {
                                combobox.SelectionChanged -= combobox_SelectionChanged;
                                CurrentlySelectedEntry = entry;
                                combobox.SelectionChanged += combobox_SelectionChanged;
                                break;
                            }
                        }
                    }

                    if (currentNodeStatus.IsWritable)
                    {
                        IsEditable = true;
                        IsCBOEnabled = true;
                    }
                    else
                    {
                        IsCBOEnabled = false;
                        IsEditable = false;
                    }
                }
                catch (System.Exception ex)
                {
                    log.Debug(
                        string.Format(
                            "{1}Problem updating control for {0} node", _propertyToControl, FormattedSerialNumber),
                        ex);
                }
                finally
                {
                    _initializing = false;
                    _isUpdating = false;
                    _skipCallback = false;
                }

                if (!Dispatcher.CheckAccess())
                {
                    Dispatcher.BeginInvoke((Action)(() => { Refresh(); }), DispatcherPriority.Background, null);
                }
                else
                {
                    Refresh();
                }
            }

            private void _updateWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                // Limit the refresh rate
                Thread.Sleep(1000 / _maxRefreshRateHz);

                INode node = e.Argument as INode;
                e.Result = node;
            }

            void enumNode_Updated(INode node)
            {
                if (_userEditing || _initializing)
                {
                    return;
                }

                if (_skipCallback)
                {
                    return;
                }

                if (_nodeMap == null)
                {
                    return;
                }

                if (_updateWorker.IsBusy || _isUpdating)
                {
                    return;
                }

                if (!IsVisible)
                {
                    return;
                }

                _updateWorker.RunWorkerAsync(node);
            }

            new private void PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
                e.Handled = true;
            }

            private void combobox_DropDownOpened(object sender, EventArgs e)
            {
                _userEditing = true;
            }

            private void combobox_DropDownClosed(object sender, EventArgs e)
            {
                _userEditing = false;
            }

            public static RoutedEvent SelectionChangeEvent = EventManager.RegisterRoutedEvent(
                "SelectionChange",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(ComboBoxControl));

            public event RoutedEventHandler SelectionChange
            {
                add
                {
                    AddHandler(SelectionChangeEvent, value);
                }
                remove
                {
                    RemoveHandler(SelectionChangeEvent, value);
                }
            }

            public void OnSelectionChange()
            {
                RoutedEventArgs args = new RoutedEventArgs(SelectionChangeEvent, this);
                RaiseEvent(args);
            }

            private void combobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                if (_initializing)
                {
                    return;
                }

                try
                {
                    if (_enumNode != null && _enumNode.IsWritable && CurrentlySelectedEntry != null)
                    {
                        _skipCallback = true;

                        // Log user initiated changes
                        LogNodeUpdateAction(log, _enumNode, CurrentlySelectedEntry.Name);
                        AnalyticsNodeUpdateAction(analytics, _enumNode, CurrentlySelectedEntry.Name);

                        _enumNode.Value = CurrentlySelectedEntry.Value;
                        _skipCallback = false;

                        // Update Dependency
                        if (_dependencyControlList != null && _dependencyControlList.Count > 0)
                        {
                            _dependencyControlList.Refresh();
                        }

                        Refresh();

                        // Notify event listeners on combobox selection change
                        OnSelectionChange();
                    }
                }
                catch (Exception ex)
                {
                    log.Debug(
                        string.Format("There was a problem updating {0} node. {1}", _propertyToControl, ex.Message));
                }
            }

            public void UpdateControlStatus()
            {
                if (_nodeMap == null)
                {
                    IsEditable = false;
                    IsCBOEnabled = false;
                    return;
                }

                try
                {
                    _initializing = true;

                    _enumNode = _nodeMap.GetNode<IEnum>(this._propertyToControl);

                    if (_enumNode != null && _enumNode.IsReadable)
                    {
                        // Enable combobox
                        IsCBOEnabled = true;

                        long nodeValue = _enumNode.Value.Int;

                        foreach(var entry in Entries)
                        {
                            if (nodeValue == entry.Value)
                            {
                                CurrentlySelectedEntry = entry;
                                break;
                            }
                        }

                        if (_enumNode.IsWritable)
                        {
                            IsCBOEnabled = true;
                            IsEditable = true;
                        }
                        else
                        {
                            IsCBOEnabled = false;
                            IsEditable = false;
                        }
                    }
                    else
                    {
                        IsEditable = false;
                        IsCBOEnabled = false;
                    }
                }
                catch (System.Exception /*ex*/)
                {
                }
                finally
                {
                    _initializing = false;
                }
            }
#endregion

#region ORIENTATION_CONTROL
            public override Orientation ContentOrientation
            {
                get
                {
                    return rootLayout.Orientation;
                }
                set
                {
                    rootLayout.Orientation = value;
                }
            }

            public void SetContentOrientation(Orientation newOrientation)
            {
                this.ContentOrientation = newOrientation;
            }

            public Orientation GetContentOrientation()
            {
                return this.ContentOrientation;
            }
#endregion

            public class Entry : INotifyPropertyChanged
            {
                string _name = "";
                long _value = 0;

#region INOTIFYPROPERTYCHANGED_IMPLEMENTATION
                public void NotifyPropertyChanged(String propertyName)
                {
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    }
                }
                public event PropertyChangedEventHandler PropertyChanged;
#endregion

                public Entry(string name, long value)
                {
                    this._name = name;
                    this._value = value;
                }

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

                public long Value
                {
                    get
                    {
                        return _value;
                    }
                    set
                    {
                        this._value = value;
                        NotifyPropertyChanged("Value");
                    }
                }
            }
        }

    }
}