/// <summary>
        /// Edits items in the specified subscription.
        /// </summary>
        private void EditItem(TreeNode node, TsCDaSubscription subscription, TsCDaItem item)
        {
            try
            {
                // save existing item id.
                OpcItem itemID = new OpcItem(item);

                TsCDaItem[] items = new ItemListEditDlg().ShowDialog(new TsCDaItem[] { item }, false, false);

                if (items == null)
                {
                    return;
                }

                // modify an existing item.
                if (itemID.Key == items[0].Key)
                {
                    subscription.ModifyItems((int)TsCDaStateMask.All, items);
                }

                // add/remove item because the item id changed.
                else
                {
                    items = subscription.AddItems(items);
                    subscription.RemoveItems(new OpcItem[] { itemID });
                }

                node.Text = items[0].ItemName;
                node.Tag  = items[0];
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }
        /// <summary>
        /// Edits the item template.
        /// </summary>
        private void EditTemplate(TsCDaItem template)
        {
            // prompt user to edit the template.
            TsCDaItem[] templates = new ItemListEditDlg().ShowDialog(new TsCDaItem[] { template }, IsReadItem, false);

            if (templates == null || templates.Length != 1)
            {
                return;
            }

            // get existing items without applying defaults.
            ArrayList items = new ArrayList();

            foreach (ListViewItem item in ItemListLV.Items)
            {
                if (item.Tag != null && item.Tag.GetType() == typeof(TsCDaItem))
                {
                    if (item.Tag != m_template)
                    {
                        items.Add(item.Tag);
                    }
                }
            }

            // re-initialize the list with the new template.
            Initialize(templates[0]);

            // add items back.
            foreach (TsCDaItem item in items)
            {
                AddItem(item);
            }
        }
        /// <summary>
        /// Edits a group of items.
        /// </summary>
        private void EditMI_Click(object sender, System.EventArgs e)
        {
            // check if the template if being editied.
            if (ItemListLV.SelectedItems.Count == 1)
            {
                if (ItemListLV.SelectedItems[0].Tag == m_template)
                {
                    EditTemplate(m_template);
                    return;
                }
            }

            // build list of items to edit (exclude template).
            ArrayList itemList = new ArrayList(ItemListLV.SelectedItems.Count);

            foreach (ListViewItem item in ItemListLV.SelectedItems)
            {
                if (item.Tag != null && item.Tag.GetType() == typeof(TsCDaItem))
                {
                    if (item.Tag != m_template)
                    {
                        itemList.Add(item.Tag);
                    }
                }
            }

            // prompt user to edit list of items.
            TsCDaItem[] items = new ItemListEditDlg().ShowDialog((TsCDaItem[])itemList.ToArray(typeof(TsCDaItem)), IsReadItem, false);

            if (items == null)
            {
                return;
            }

            // remove changed items.
            RemoveMI_Click(sender, e);

            // add changed items.
            foreach (TsCDaItem item in items)
            {
                AddItem(item);
            }
        }
        /// <summary>
        /// Creates a new item.
        /// </summary>
        private void NewMI_Click(object sender, System.EventArgs e)
        {
            TsCDaItem template = null;

            // copy the current selection.
            if (ItemListLV.SelectedItems.Count > 0)
            {
                template = (TsCDaItem)((TsCDaItem)ItemListLV.SelectedItems[0].Tag).Clone();
            }

            // prompt user to edit new item.
            TsCDaItem[] items = new ItemListEditDlg().ShowDialog(new TsCDaItem[] { template }, IsReadItem, true);

            if (items == null)
            {
                return;
            }

            // add new items.
            foreach (TsCDaItem item in items)
            {
                AddItem(item);
            }
        }
        /// <summary>
        /// Edits items in the specified subscription.
        /// </summary>
        private void EditItems(TreeNode node, TsCDaSubscription subscription)
        {
            try
            {
                // save item ids to detect item id changes.
                OpcItem[] itemIDs = new OpcItem[subscription.Items.Length];

                for (int ii = 0; ii < itemIDs.Length; ii++)
                {
                    itemIDs[ii] = new OpcItem(subscription.Items[ii]);
                }

                // edit the items.
                TsCDaItem[] items = new ItemListEditDlg().ShowDialog(subscription.Items, false, false);

                if (items == null)
                {
                    return;
                }

                // separate the items in lists depending on whether item id changed.
                ArrayList insertItems = new ArrayList();
                ArrayList updateItems = new ArrayList();
                ArrayList deleteItems = new ArrayList();

                for (int ii = 0; ii < itemIDs.Length; ii++)
                {
                    if (items[ii].Key != itemIDs[ii].Key)
                    {
                        insertItems.Add(items[ii]);
                        deleteItems.Add(itemIDs[ii]);
                    }
                    else
                    {
                        updateItems.Add(items[ii]);
                    }
                }

                // update existing items.
                if (updateItems.Count > 0)
                {
                    subscription.ModifyItems((int)TsCDaStateMask.All, (TsCDaItem[])updateItems.ToArray(typeof(TsCDaItem)));
                }

                // insert new items.
                if (insertItems.Count > 0)
                {
                    subscription.AddItems((TsCDaItem[])insertItems.ToArray(typeof(TsCDaItem)));
                }

                // delete old items.
                if (deleteItems.Count > 0)
                {
                    subscription.RemoveItems((OpcItem[])deleteItems.ToArray(typeof(OpcItem)));
                }

                // update tree.
                node.Nodes.Clear();

                foreach (TsCDaItem item in subscription.Items)
                {
                    AddItem(node, item);
                }

                node.Expand();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }