Где вы размещаете методы событий, которые связаны исключительно с производным классом, но которые вызывают методы, не связанные исключительно с производным классом? - PullRequest
0 голосов
/ 15 мая 2018

Исходная версия пользовательского интерфейса моего WinForms приложения включала стандарт TreeView. Поэтому я поместил методы, используемые для создания узлов TreeView, и методы для обработки событий TreeNode в основной класс формы.

С тех пор я создал класс MyTreeView, который наследуется от класса TreeView (class MyTreeView : TreeView), чтобы я мог инкапсулировать пользовательские свойства, необходимые для управления состоянием объекта MyTreeView. Я начал перемещать весь свой связанный с TreeView и TreeNode код (включая обработчики событий) в MyTreeView. Например, я переместил этот код из основного класса формы в метод MyTreeView:

ToolStripMenuItem toolStripMenuItem = new ToolStripMenuItem(text: "foo", image: null, onClick: OnClickMethod)
{
    CheckOnClick = true
};

Тогда я понял, что OnClickMethod не существует в MyTreeView, потому что я изначально закодировал его в основном классе формы. Я пошел, чтобы переместить OnClickMethod из основного класса формы в MyTreeView, но понял, что OnClickMethod вызывает другие методы, которые работают, связанные с другими основными элементами формы (не только с древовидным представлением).

Должен ли я найти способ передать имя OnClickMethod из основного класса формы в метод MyTreeView (это вообще возможно)?

Должен ли я найти способ передать экземпляр главной формы в MyTreeView, чтобы я мог получить доступ к обработчикам событий, которые находятся в основном классе формы?

Мой общий подход просто плох?

Как бы вы справились с этим сценарием?

Ответы [ 2 ]

0 голосов
/ 16 мая 2018

Эд Планкетт определенно втянул меня в игру, но я отвечаю на свой вопрос, потому что не смог понять всего, чему он пытался научить меня, без дальнейших исследований. Инструкция Эда дала мне информацию, в которой я нуждался, чтобы выяснить тему, в которой я нуждался, чтобы исследовать (спасибо, Эд).

Я полагаю, что намерение Эда состояло в том, чтобы я внедрил метод .NET Framework для обработки и вызова событий . Он основан на delegate model, который следует за observer design pattern. Код ниже показывает, как я ответил на мой вопрос, используя эту технику. Моделируется щелчок по элементу TreeNode ContextMenuStrip. Я буду использовать ту же технику для других событий, которые происходят на TreeView

Короче говоря, требование заключается в том, чтобы инкапсулировать весь код, необходимый для создания, загрузки и обработки событий для TreeView в производном классе MyTreeView, при этом сообщая классу основной формы события, происходящие в объекте MyTreeView таким образом, основной класс формы может выполнять не-TreeView вещи, которые он должен делать в результате взаимодействия пользователя с объектом MyTreeView.

** Основная форма класса *

using System;
using System.Windows.Forms;

namespace MyTreeViewDemo
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();

            //Load the tree view
            myTreeView1.Load();

            //Subscribe to the MyTreeView ContextMenuItemClicked event. 
            // Set it to invoke MyTreeView1_FooClicked when the event is raised
            myTreeView1.ContextMenuItemClicked += MyTreeView1_FooClicked;
        }

        void MyTreeView1_FooClicked(object sender, EventArgs e)
        {
            // Do form stuff (other than treeview stuff) that needs to be done when a treenode context menu item is clicked
        }
    }
}

** MyTreeView Class **

using System;
using System.Windows.Forms;

namespace MyTreeViewDemo
{
    class MyTreeView : TreeView
    {
        public MyTreeView()
        {
            //This event handler does nothing more than select the node the user clicked
            this.NodeMouseClick += TreeViewTableList_NodeMouseClick;
        }

        // Associate a delegate with an event by including the delegate type in the event declaration
        // Declare an event named ContextMenuItemClicked. 
        // The event is associated with the EventHandler delegate and raised in a method named OnContextMenuItemClicked. 
        public event EventHandler ContextMenuItemClicked;

        /// <summary>
        /// When the user clicks on a treenode context menu item, this method is invoked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void OnContextMenuItemClicked(object sender, EventArgs e)
        {
            // Do treeview stuff that needs to be done when a treenode context menu item is clicked

            //if EventHandler ContextMenuItemClicked is not null, invoke it so that subscribers are notified.
            ContextMenuItemClicked?.Invoke(sender, e);
        }

        /// <summary>
        /// Method to build a minimum treeview with one node
        /// </summary>
        public void Load()
        {
            TreeNode treeNodeRoot = new TreeNode("tree node item");

            this.Nodes.Add(treeNodeRoot);

            // Create a ContextMenuStrip for the tree node
            ContextMenuStrip contextMenuStripRootNode = new ContextMenuStrip
            {
                ShowCheckMargin = true,
                ShowImageMargin = false
            };

            //Create a menu item for the context menu. Set it to invoke OnContextMenuItemClicked when its clicked
            ToolStripMenuItem menuItem = new ToolStripMenuItem("foo", null, onClick: OnContextMenuItemClicked)
            {
                CheckOnClick = true
            };

            //Add the menu item to the contextstrip.
            contextMenuStripRootNode.Items.Add(menuItem);

            //Set the tree node's ContextMenuStrip property to the ContextMenuStrip.
            treeNodeRoot.ContextMenuStrip = contextMenuStripRootNode;
        }

        /// <summary>
        ///  Select the node the user clicked so that the treeview visually looks as the user expects
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TreeViewTableList_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            TreeView t = (TreeView)sender;
            //Force the node that was clicked to be selected
            t.SelectedNode = t.GetNodeAt(e.X, e.Y);
        }

    }
}
0 голосов
/ 15 мая 2018

Обычный шаблон для такого рода вещей - для MyTreeView событие FooClicked.

Это происходит потому, что то, что происходит при нажатии foo, полностью выходит за пределы области внимания MyTreeView. Это проблема Form1, так что пусть Form1 справится с этим. Просто создайте событие и позвольте форме добавить обработчик для него. OnClickMethod все равно будет определено в Form1.

Создание пункта меню

ToolStripMenuItem toolStripMenuItem = 
    new ToolStripMenuItem(text: "foo", image: null, onClick: (s, e2) => OnFooClicked())
{
    CheckOnClick = true
};

(s, e2) => OnFooClicked() - это обработчик событий, который вызывает OnFooClicked(). Если этот синтаксис настолько странен, что мешает вам, дайте мне знать, и я перепишу его, чтобы избежать путаницы.

Определение события в MyTreeView:

public event EventHandler FooClicked;

protected void OnFooClicked()
{
    //  C#7. If this fails to compile with some weird error let me know and I'll 
    //  provide a backwards compatible version. 
    FooClicked?.Invoke(this, EventArgs.Empty);
}

Теперь в Form1 просто подключите обработчик событий для myTreeView1.FooClicked:

void myTreeView1_FooClicked(object sender, EventArgs e)
{
    //  OnClickMethod() is no longer an event handler. It's just a protected 
    //  method that does stuff. I'm 99% sure its sender and e parameters were 
    //  never used. Writing all your logic directly in event handlers is bad 
    //  practice because it leads to weird noise in the code and bizarre, 
    //  inappropriate method names. 
    OnClickMethod();
}

Насколько я понимаю, это требование:

  1. Клики пользователей toolStripMenuItem1
  2. Form1 слышит об этом и что-то делает в ответ.

В прошлом Form1 непосредственно создавала toolStripMenuItem, поэтому она непосредственно предоставляла обработчик щелчков для самого пункта меню.

Теперь Form1 не создает toolStripMenuItem, поэтому он не может предоставить обработчик. Но все, что беспокоит Form1 - это когда кто-то нажимает на этот пункт меню. Таким образом, мы можем сделать это, создав новое событие, которое принадлежит MyTreeView. Теперь, когда нажимается toolStripMenuItem, его событие click все еще вызывается, но новый обработчик для этого события click вызывает другое событие , событие, к которому имеет доступ Form1. Таким образом, Form1 обрабатывает это другое событие. Это другое событие возникает в ответ на то же действие пользователя, что и раньше: пункт меню, по которому щелкают.

Единственное, чего вам не хватает, это добавить обработчик к событию, вызванному myTreeView1 в вашей форме. Это делается на панели свойств элемента управления в конструкторе форм Visual Studio. Там вкладка События. В этом нет ничего волшебного, особенного или секретного. Это событие, как и любое другое событие. Просто обычное событие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...