/* ======================================================================== * Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Reflection; namespace Opc.Ua.Client.Controls { /// /// A base class for list controls. /// public partial class BaseListCtrl : UserControl { /// /// Initializes a new instance of the class. /// public BaseListCtrl() { InitializeComponent(); ItemsLV.SmallImageList = new GuiUtils().ImageList; ItemsLV.ListViewItemSorter = new BaseListCtrlSorter(this); } /// /// The ListView contained in the control. /// protected System.Windows.Forms.ListView ItemsLV; #region Public Interface /// /// Whether the control should allow items to be dragged. /// [DefaultValue(false)] public bool EnableDragging { get { return m_enableDragging; } set { m_enableDragging = value; } } /// /// The instructions to display when no items are in the list. /// public string Instructions { get { return m_instructions; } set { m_instructions = value; } } /// /// Whether new items should be pre-pended to the list. /// [DefaultValue(false)] public bool PrependItems { get { return m_prependItems; } set { m_prependItems = value; } } /// /// Raised whenever items are 'picked' in the control. /// public event ListItemActionEventHandler ItemsPicked { add { m_ItemsPicked += value; } remove { m_ItemsPicked -= value; } } /// /// Raised whenever items are selected in the control. /// public event ListItemActionEventHandler ItemsSelected { add { m_ItemsSelected += value; } remove { m_ItemsSelected -= value; } } /// /// Raised whenever items are added to the control. /// public event ListItemActionEventHandler ItemsAdded { add { m_ItemsAdded += value; } remove { m_ItemsAdded -= value; } } /// /// Raised whenever items are modified in the control. /// public event ListItemActionEventHandler ItemsModified { add { m_ItemsModified += value; } remove { m_ItemsModified -= value; } } /// /// Raised whenever items are removed from the control. /// public event ListItemActionEventHandler ItemsRemoved { add { m_ItemsRemoved += value; } remove { m_ItemsRemoved -= value; } } /// /// Returns the number of items in the control. /// public int Count { get { return ItemsLV.Items.Count; } } /// /// Returns the objects associated with the items in the control. /// public Array GetItems(System.Type type) { ArrayList items = new ArrayList(); foreach (ListViewItem listItem in ItemsLV.Items) { items.Add(listItem.Tag); } return items.ToArray(type); } /// /// Returns the objects associated with the selected items in the control. /// public Array GetSelectedItems(System.Type type) { ArrayList items = new ArrayList(); if (ItemsLV.View == View.Details) { foreach (ListViewItem listItem in ItemsLV.SelectedItems) { items.Add(listItem.Tag); } } return items.ToArray(type); } #endregion #region Private Members private bool m_prependItems; private event ListItemActionEventHandler m_ItemsPicked; private event ListItemActionEventHandler m_ItemsSelected; private event ListItemActionEventHandler m_ItemsAdded; private event ListItemActionEventHandler m_ItemsModified; private event ListItemActionEventHandler m_ItemsRemoved; private object[][] m_columns; private bool m_updating; private int m_updateCount; private string m_instructions; private Point m_dragPosition; private bool m_enableDragging; #endregion #region Protected Methods /// /// Returns tag of the selected item. Null if no items or more than one item is selected. /// protected object SelectedTag { get { if (ItemsLV.SelectedItems.Count != 1) { return null; } return ItemsLV.SelectedItems[0].Tag; } } /// /// Deletes the currently selected items. /// protected virtual void DeleteSelection() { List itemsToDelete = new List(); foreach (ListViewItem item in ItemsLV.SelectedItems) { itemsToDelete.Add(item); } foreach (ListViewItem item in itemsToDelete) { item.Remove(); } } /// /// Compares two items in the list. /// protected virtual int CompareItems(object item1, object item2) { IComparable comparable = item1 as IComparable; if (comparable != null) { return comparable.CompareTo(item2); } return 0; } /// /// Returns the data to drag. /// protected virtual object GetDataToDrag() { if (ItemsLV.SelectedItems.Count > 0) { ArrayList data = new ArrayList(); foreach (ListViewItem listItem in ItemsLV.SelectedItems) { data.Add(listItem.Tag); } return data.ToArray(); } return null; } /// /// Adds an item to the list. /// protected virtual ListViewItem AddItem(object item) { return AddItem(item, "SimpleItem", -1); } /// /// Adds an item to the list. /// protected virtual ListViewItem AddItem(object item, string icon, int index) { // switch to detail view as soon as an item is added. if (ItemsLV.View == View.List) { ItemsLV.Items.Clear(); ItemsLV.View = View.Details; } ListViewItem listItem = null; if (m_updating) { if (m_updateCount < ItemsLV.Items.Count) { listItem = ItemsLV.Items[m_updateCount]; } m_updateCount++; } if (listItem == null) { listItem = new ListViewItem(); } listItem.Text = String.Format("{0}", item); listItem.ImageKey = icon; listItem.Tag = item; // fill columns with blanks. for (int ii = listItem.SubItems.Count; ii < ItemsLV.Columns.Count-1; ii++) { listItem.SubItems.Add(String.Empty); } // calculate new index. int newIndex = index; if (index < 0 || index > ItemsLV.Items.Count) { newIndex = ItemsLV.Items.Count; } // update columns. UpdateItem(listItem, item, newIndex); if (listItem.ListView == null) { // add to control. if (index >= 0 && index <= ItemsLV.Items.Count) { ItemsLV.Items.Insert(index, listItem); } else { ItemsLV.Items.Add(listItem); } } NotifyItemAdded(item); // return new item. return listItem; } /// /// Starts overwriting the contents of the control. /// protected void BeginUpdate() { m_updating = true; m_updateCount = 0; } /// /// Finishes overwriting the contents of the control. /// protected void EndUpdate() { m_updating = false; while (ItemsLV.Items.Count > m_updateCount) { ItemsLV.Items[ItemsLV.Items.Count-1].Remove(); } m_updateCount = 0; AdjustColumns(); } /// /// Updates a list item with the current contents of an object. /// protected virtual void UpdateItem(ListViewItem listItem, object item) { listItem.Tag = item; } /// /// Updates a list item with the current contents of an object. /// protected virtual void UpdateItem(ListViewItem listItem, object item, int index) { UpdateItem(listItem, item); } /// /// Sets the columns shown in the list view. /// protected virtual void SetColumns(object[][] columns) { ItemsLV.Clear(); m_columns = columns; foreach (object[] column in columns) { ColumnHeader header = new ColumnHeader(); header.Text = column[0] as string; header.TextAlign = (HorizontalAlignment)column[1]; ItemsLV.Columns.Add(header); } ColumnHeader blank = new ColumnHeader(); blank.Text = String.Empty; ItemsLV.Columns.Add(blank); AdjustColumns(); } /// /// Adjusts the columns shown in the list view. /// protected virtual void AdjustColumns() { if (ItemsLV.View == View.List || ItemsLV.Items.Count == 0) { ItemsLV.View = View.List; if (ItemsLV.Items.Count == 0 && !String.IsNullOrEmpty(m_instructions)) { ListViewItem item = new ListViewItem(m_instructions); item.ImageKey = "Info"; item.ForeColor = Color.Gray; ItemsLV.Items.Add(item); } ItemsLV.Columns[0].Width = -2; return; } ItemsLV.View = View.Details; for (int ii = 0; ii < m_columns.Length && ii < ItemsLV.Columns.Count; ii++) { // check for fixed width columns. if (m_columns[ii].Length >= 4 && m_columns[ii][3] != null) { int width = (int)m_columns[ii][3]; if (ItemsLV.Columns[ii].Width < width) { ItemsLV.Columns[ii].Width = width; } continue; } // check mandatory columns. if (m_columns[ii].Length < 3 || m_columns[ii][2] == null) { ItemsLV.Columns[ii].Width = -2; continue; } // check if all items have the default value for the column. bool display = false; foreach (ListViewItem listItem in ItemsLV.Items) { if (!m_columns[ii][2].Equals(listItem.SubItems[ii].Text)) { display = true; break; } } // only display columns with non-default information. if (display) { ItemsLV.Columns[ii].Width = -2; } else { ItemsLV.Columns[ii].Width = 0; } } if (ItemsLV.Columns.Count > 0) { ItemsLV.Columns[ItemsLV.Columns.Count-1].Width = 0; } } /// /// Enables the state of menu items. /// protected virtual void EnableMenuItems(ListViewItem clickedItem) { // do nothing. } /// /// Sends notifications whenever items in the control are 'picked'. /// protected virtual void PickItems() { if (m_ItemsPicked != null) { ICollection data = GetDataToDrag() as ICollection; if (data != null) { m_ItemsPicked(this, new ListItemActionEventArgs(ListItemAction.Picked, data)); } } } /// /// Sends notifications whenever items in the control are 'selected'. /// protected virtual void SelectItems() { if (m_ItemsSelected != null) { object[] selectedObjects = new object[ItemsLV.SelectedItems.Count]; for (int ii = 0; ii < selectedObjects.Length; ii++) { selectedObjects[ii] = ItemsLV.SelectedItems[ii].Tag; } m_ItemsSelected(this, new ListItemActionEventArgs(ListItemAction.Selected, selectedObjects)); } } /// /// Sends notifications that an item has been added to the control. /// protected virtual void NotifyItemAdded(object item) { NotifyItemsAdded(new object[] { item }); } /// /// Sends notifications that items have been added to the control. /// protected virtual void NotifyItemsAdded(object[] items) { if (m_ItemsAdded != null && items != null && items.Length > 0) { m_ItemsAdded(this, new ListItemActionEventArgs(ListItemAction.Added, items)); } } /// /// Sends notifications that an item has been modified in the control. /// protected virtual void NotifyItemModified(object item) { NotifyItemsModified(new object[] { item }); } /// /// Sends notifications that items have been modified in the control. /// protected virtual void NotifyItemsModified(object[] items) { if (m_ItemsModified != null && items != null && items.Length > 0) { m_ItemsModified(this, new ListItemActionEventArgs(ListItemAction.Modified, items)); } } /// /// Sends notifications that and item has been removed from the control. /// protected virtual void NotifyItemRemoved(object item) { NotifyItemsRemoved(new object[] { item }); } /// /// Sends notifications that items have been removed from the control. /// protected virtual void NotifyItemsRemoved(object[] items) { if (m_ItemsRemoved != null && items != null && items.Length > 0) { m_ItemsRemoved(this, new ListItemActionEventArgs(ListItemAction.Removed, items)); } } /// /// Finds the list item with specified tag in the control, /// protected ListViewItem FindItem(object tag) { foreach (ListViewItem listItem in ItemsLV.Items) { if (Object.ReferenceEquals(tag, listItem.Tag)) { return listItem; } } return null; } /// /// Returns the tag associated with a selected item. /// protected object GetSelectedTag(int index) { if (ItemsLV.SelectedItems.Count > index) { return ItemsLV.SelectedItems[index].Tag; } return null; } #endregion #region BaseListCtrlSorter Class /// /// A class that allows the list to be sorted. /// private class BaseListCtrlSorter : IComparer { /// /// Initializes the sorter. /// public BaseListCtrlSorter(BaseListCtrl control) { m_control = control; } /// /// Compares the two items. /// public int Compare(object x, object y) { ListViewItem itemX = x as ListViewItem; ListViewItem itemY = y as ListViewItem; return m_control.CompareItems(itemX.Tag, itemY.Tag); } private BaseListCtrl m_control; } #endregion #region Event Handlers private void ItemsLV_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { try { // ignore non-right clicks. if (e.Button == MouseButtons.Left) { m_dragPosition = e.Location; return; } // disable everything. if (ItemsLV.ContextMenuStrip != null) { foreach (ToolStripItem item in ItemsLV.ContextMenuStrip.Items) { ToolStripMenuItem menuItem = item as ToolStripMenuItem; if (menuItem == null) { continue; } menuItem.Enabled = false; if (menuItem.DropDown != null) { foreach (ToolStripItem subItem in menuItem.DropDown.Items) { ToolStripMenuItem subMenuItem = subItem as ToolStripMenuItem; if (subMenuItem != null) { subMenuItem.Enabled = false; } } } } } // selects the item that was right clicked on. ListViewItem clickedItem = ItemsLV.GetItemAt(e.X, e.Y); // ensure clicked item is selected. if (clickedItem != null) { clickedItem.Selected = true; } // enable menu items according to context. EnableMenuItems(clickedItem); } catch (Exception exception) { GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception); } } private void ItemsLV_MouseUp(object sender, MouseEventArgs e) { try { if (e.Button == MouseButtons.Left) { m_dragPosition = e.Location; return; } } catch (Exception exception) { GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception); } } private void ItemsLV_DoubleClick(object sender, System.EventArgs e) { try { PickItems(); } catch (Exception exception) { GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception); } } private void ItemsLV_SelectedIndexChanged(object sender, System.EventArgs e) { try { SelectItems(); } catch (Exception exception) { GuiUtils.HandleException(this.Text, MethodBase.GetCurrentMethod(), exception); } } private void ItemsLV_MouseMove(object sender, MouseEventArgs e) { if (m_enableDragging && e.Button == MouseButtons.Left && !m_dragPosition.Equals(e.Location)) { object data = GetDataToDrag(); if (data != null) { ItemsLV.DoDragDrop(data, DragDropEffects.Copy); } } } /// /// Handles the DragEnter event of the ItemsLV control. /// /// The source of the event. /// The instance containing the event data. protected virtual void ItemsLV_DragEnter(object sender, DragEventArgs e) { if (m_enableDragging) { e.Effect = DragDropEffects.Copy; } else { e.Effect = DragDropEffects.None; } } /// /// Handles the DragDrop event of the ItemsLV control. /// /// The source of the event. /// The instance containing the event data. protected virtual void ItemsLV_DragDrop(object sender, DragEventArgs e) { // overriden by sub-class. } #endregion } #region ListItemAction Enumeration /// /// The possible actions that could affect an item. /// public enum ListItemAction { /// /// The item was picked (double clicked). /// Picked, /// /// The item was selected. /// Selected, /// /// The item was added. /// Added, /// /// The item was modified. /// Modified, /// /// The item was removed. /// Removed } #endregion #region ListItemActionEventArgs Class /// /// The event argurments passed when an item event occurs. /// public class ListItemActionEventArgs : EventArgs { #region Constructors /// /// Initializes a new instance of the class. /// /// The action. /// The items. public ListItemActionEventArgs(ListItemAction action, ICollection items) { m_items = items; m_action = action; } #endregion #region Public Properties /// /// Gets the items. /// /// The items. public ICollection Items { get { return m_items; } } /// /// Gets the action. /// /// The action. public ListItemAction Action { get { return m_action; } } #endregion #region Private Fields private ICollection m_items; private ListItemAction m_action; #endregion } /// /// The delegate used to receive item action events. /// public delegate void ListItemActionEventHandler(object sender, ListItemActionEventArgs e); #endregion }