Могу ли я узнать в родительской форме, подписалась ли унаследованная форма на событие Show?

У меня есть следующие формы в моей структуре winforms

  • FormBase (унаследовано от Form)
  • FormBaseList (наследуется от FormBase)
  • FormBaseDetail (наследуется от FormBase)

Теперь каждая форма в приложении наследуется от одной из трех вышеперечисленных.
Например, FormCustomerList будет унаследована от FormBaseList.

Теперь в FormBaseList присутствует событие FormBaseList_Shown (по двойному клику по нему в окне свойств в VS)

Что я хотел бы знать в коде из FormBaseList_Show, так это наличие события FormCustomerList_Show (опять же, дважды щелкнув его в окне свойств).

Это вообще возможно?

Так зачем мне это?
Потому что некоторые изменения в фреймворке требуют, чтобы формы больше не использовали событие Shown, а использовали специальное событие.
Я хотел бы поймать и показать предупреждение разработчику, если он добавит Show в форму, и если это действительно необходимо, он может установить свойство, которое будет скрывать это предупреждение.
Это предупреждение не нужно показывать во время разработки, во время выполнения будет достаточно. Но если это возможно во время разработки, это будет бонусом.
Так можно ли это сделать и есть ли лучший способ сделать это?

Я надеюсь, что это объяснение понятно

ИЗМЕНИТЬ

Идея состоит в том, что когда разработчик использует событие Show, он должен получить предупреждение (либо во время разработки, либо во время выполнения). Если он чувствует, что ему действительно нужен метод Show, он должен иметь возможность отключить предупреждение для этой конкретной формы.


person GuidoG    schedule 02.05.2019    source источник
comment
stackoverflow.com/a/3856171/17034   -  person Hans Passant    schedule 02.05.2019
comment
Я разместил два решения. Одно решение для времени выполнения (включая два разных варианта) и одно решение для времени разработки. Поскольку решения совершенно разные и включают в себя несколько вариантов, я решил опубликовать их в двух отдельных ответах.   -  person Reza Aghaei    schedule 03.05.2019


Ответы (3)


Чтобы создать исключение или показать окно сообщения во время выполнения, у вас есть следующие варианты:

  • Затените событие Shown и в части add создайте исключение (если не установлен флаг пропуска).
  • Используя отражение, найдите список обработчиков событий для события Shown и проверьте, есть ли обработчик, прикрепленный к событию.

В обоих решениях можно использовать логическое свойство для переопределения поведения в производных формах.

Вариант 1 – затенение события Shown и добавление кода в add

Вы можете скрыть событие Shown, а в средстве доступа add добавить код для отображения окна сообщения или исключения, если к событию добавлен обработчик.

В следующем примере я добавил свойство ThrowExceptionOnSubscribingShownEvent к базовой форме, которая по умолчанию имеет значение true, что означает, что оно выдает исключение при подписке на событие Shown.

public bool ThorwExceptionOnSubscribingShownEvent { get; set; } = true;

public new event EventHandler Shown
{
    add
    {
        if (ThorwExceptionOnSubscribingShownEvent)
            throw new InvalidOperationException("Shown event is deprecated.");

        base.Shown += value;
    }
    remove
    {
        base.Shown -= value;
    }
}

Вариант 2. Поиск списка обработчиков событий для события Shown

Как вариант для времени выполнения, вы можете переопределить метод OnShown и, используя отражение, получить поле EVENT_SHOWN и, используя его, получить список обработчиков событий Shown события. Затем вы можете проверить, не пуст ли список обработчиков событий, выдать исключение.

В следующем примере я добавил свойство ThrowExceptionOnSubscribingShownEvent к базовой форме, которое по умолчанию имеет значение true, что означает, что оно выдает исключение при подписке на событие Shown. Вы можете установить его на false, когда это необходимо в производных формах:

public partial class BaseForm : Form
{
    public BaseForm()
    {
        InitializeComponent();
    }

    public bool ThrowExceptionOnSubscribingShownEvent { get; set; } = true;
    protected override void OnShown(EventArgs e)
    {
        if (!DesignMode)
        {
            var EVENT_SHOWN = typeof(Form).GetField("EVENT_SHOWN",
                BindingFlags.NonPublic | BindingFlags.Static)
                .GetValue(null);
            var handlers = Events[EVENT_SHOWN]?.GetInvocationList();
            if (ThrowExceptionOnSubscribingShownEvent && handlers?.Length > 0)
                throw new InvalidOperationException("Shown event is deprecated.");
        }
        base.OnShown(e);
    }
}
person Reza Aghaei    schedule 03.05.2019

Вы должны скрыть событие Shown и объявить его устаревшим следующим образом:

[Obsolete("Shown event is deprecated.")]
public new event EventHandler Shown
{
    add { base.Shown += value; }
    remove { base.Shown -= value; }
}

Вы пометили его как устаревший, и он будет отображать предупреждение в окне Список ошибок во время компиляции при сборке решения.

Кроме того, событие будет продолжать работать, как и ожидалось, подписавшись на исходное событие Shown базы.

Чтобы отключить предупреждение, добавьте следующую строку кода вверху файла Designer.cs формы, подписавшейся на событие:

#pragma warning disable CS0618 // Type or member is obsolete

и добавьте эту строку внизу:

#pragma warning restore CS0618 // Type or member is obsolete

Примечание. В других файлах, кроме файла Designer.cs, достаточно окружить только подписку на обработчик событий с помощью #pragma. Но для Designer.cs вы не можете окружить подписку на обработчик событий, потому что, изменяя что-либо в Designer, содержимое InitializeComponent и блок кода, определяющий переменные-члены, будут сгенерированы автоматически, и все ваши ручные изменения в Designer.cs будут потерял. Но если вы поместите #pragma вверху и внизу файла, это безопасно и не будет удалено из Designer.cs.

person Reza Aghaei    schedule 03.05.2019
comment
хорошо, это может сработать, но для этого нужно отредактировать Designer.cs, которого я хотел бы избежать - person GuidoG; 03.05.2019
comment
Это работает, я уже пробовал это. Это изменение в файле конструктора является постоянным. Вам не нужно беспокоиться об этом. - person Reza Aghaei; 03.05.2019
comment
Я не беспокоюсь о том, что изменение в файле конструктора будет постоянным, я тоже время от времени меняю там код. Я надеялся на простой способ в своей структуре, например, установить пользовательское свойство в форме, которое скрыло бы предупреждение. - person GuidoG; 03.05.2019
comment
Пользовательское свойство — это решение во время выполнения. - person Reza Aghaei; 03.05.2019
comment
Да, я сказал это в своем вопросе, что решения во время выполнения также будет достаточно. - person GuidoG; 03.05.2019
comment
Кроме того, если пользовательское свойство находится в базовой форме, я думаю, что предупреждение может появиться во время разработки. Я обнаружил, что много кода выполняется во время разработки только потому, что он находится в базовой форме, а не полностью, конечно. - person GuidoG; 03.05.2019
comment
Я вижу, Ганс поделился ссылкой, которая может оказаться полезной для решения во время выполнения. Есть ли причина, по которой вы не используете это? - person Reza Aghaei; 03.05.2019
comment
Ну, это включает в себя использование отражения, и в ссылке также объясняется, что Microsoft не хочет, чтобы мы делали это таким образом. Так что я предпочитаю не делать это так - person GuidoG; 03.05.2019
comment
Тогда, я думаю, мне придется сделать выбор между вашим методом или методом Ганса. Спасибо за ваше время и усилия - person GuidoG; 03.05.2019

Вероятно, это возможно с использованием отражения, но это было бы довольно грязно. Лучшей тактикой было бы скрыть событие Shown следующим образом:

        [Obsolete("Don't use this event, use my custom one")]
        public new event EventHandler Shown;

Тогда все, что пытается использовать это событие, будет генерировать предупреждения компилятора.

person Bryce Wagner    schedule 02.05.2019
comment
Ну, это работает, но теперь я не могу скрыть предупреждение в тех немногих формах, которым по какой-то причине разрешено событие Show. - person GuidoG; 02.05.2019
comment
Обратите внимание, что это создает новое событие, которое ни с чем не связано, поэтому те, которые должны регистрироваться для Shown, больше не будут вызываться. Это означает, что вам нужно создать другое событие для тех, к которым можно подключиться, и вызвать это событие из защищенного переопределения метода OnShown(EventArg e). - person Bryce Wagner; 03.05.2019
comment
но в некоторых случаях разработчик не может перезаписать и скрыть сообщение. Это не то, что мне нужно, извините - person GuidoG; 03.05.2019