Проверить SuspendLayout

Есть ли способ в С# проверить, приостановлен ли объект? У меня есть TreeView, который мне нужно знать, если он все еще приостановлен.

 myTreeView.BeginUpdate();
 myTreeView.SuspendLayout();

 // Do Stuff.

 myTreeView.EndUpdate();
 myTreeView.ResumeLayout();

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

Ответы

Ответ 1

Следуя ответу от верности, у вас есть один вариант:

Используйте следующий класс

public class SuspendAwareTreeView : TreeView    
{
    public readonly T RealControl;
    private int suspendCount;

    public bool IsSuspended 
    { 
        get { return suspendCount > 0; }
    }

    public Suspendable(T real) { this.RealControl = real; }

    public void SuspendLayout() 
    { 
        this.suspendCount++;
        this.RealControl.SuspendLayout();
    }

    public void ResumeLayout() 
    { 
        this.RealControl.ResumeLayout();
        this.suspendCount--;
    }
}

Затем используйте этот класс для всего, где вам нужно его приостановить.

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

Если это так, вы будете вынуждены пойти с множеством менее приятных решений:

  • Напишите новый элемент управления пользователя, который обертывает TreeView и отменяет все вызовы, но поддерживает приостановленное состояние.
    • полученный экземпляр больше не является "is-TreeView", который вызовет проблемы.
    • Возможно, техническое обслуживание возможно высокое.
    • Если по какой-то причине древовидная конструкция когда-либо решила приостановить себя, это сломается.
    • Новая версия среды выполнения вряд ли сломает что-либо, вы просто не получите новую функциональность без усилий.
  • Внедрить полностью новый TreeViewEx, который предоставляет это состояние
    • полученный экземпляр больше не является "is-TreeView", который вызовет проблемы.
    • техническое обслуживание возможно высокое
    • никогда не может сломаться, поскольку у вас есть полный контроль, может отличаться от оригинала, хотя
    • Новая версия среды выполнения вряд ли что-то сломает, вы просто не получите новых функций без значительных усилий (возможно, в нарушение закона /EULA ).
  • Нарушение инкапсуляции
    • Нет изменений для системы типов, все остальное продолжает работать.
    • Усилия по техническому обслуживанию, потенциально высокие при изменении времени выполнения
    • приложения будут разбиты, если среда выполнения изменится под ними

Для ваших нужд , если и только если вы управляете версиями во время выполнения, это работает полностью (то есть контролируемая корпоративная среда), то следующий злой, но эффективный взлом подходит. До тех пор, пока вы тестируете любое время, которое вы обновляете, он может продолжать работать с минимальными усилиями.

public class ControlInvader
{
  private static readonly System.Reflection.FieldInfo layoutSuspendCount = 
      typeof(Control).GetField("layoutSuspendCount",
          System.Reflection.BindingFlags.Instance | 
          System.Reflection.BindingFlags.NonPublic);

  private readonly Control control;        

  public bool IsSuspended 
  {
    get 
    {
      return 0 != (byte)layoutSuspendCount.GetValue(this.control);
    }
  }

  public Suspendable(Control control) { this.control = control; }     
}

Прикрепите это к своему TreeView, а затем вы сможете проверить значение, когда захотите.

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

Ответ 2

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

void Recursive(Control c)
{
  c.SuspendLayout();
  try
  {
    if (existCondition) return;
    // do stuff
    Recursive(c);
  }
  finally
  {
    c.ResumeLayout(true);
  }
}

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

c.SuspendLayout() // 1st call suspends layout
c.SuspendLayout() // 2nd and subsequent call only increase the count and does nothing.
....
c.ResumeLayout(true) // this decrease the count to 1 and does NOT actually resumes layout.
c.ResumeLayout(true) // this set the count to 0 and REALLY resumes layout.

НТН

Ответ 3

Быстрый просмотр Reflector в методе SuspendLayout из System.Windows.Forms.Control показывает следующее:

public void SuspendLayout()
{
    this.layoutSuspendCount = (byte) (this.layoutSuspendCount + 1);
    if (this.layoutSuspendCount == 1)
    {
        this.OnLayoutSuspended();
    }
}

Так как он не устанавливает никаких общедоступных флагов, а метод OnLayoutSuspended() является внутренним, все равно не обнаруживается, что когда приостановлено управление.

Вы можете создать подкласс дерева с помощью новых методов Suspend и ResumeLayout, но было бы трудно гарантировать, что они будут вызваны при любых обстоятельствах, поскольку базовые методы не являются виртуальными.