Проверьте SuspendLayout - PullRequest
       17

Проверьте SuspendLayout

15 голосов
/ 22 апреля 2009

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

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

 // Do Stuff.

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

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

Ответы [ 3 ]

11 голосов
/ 22 апреля 2009

Исходя из ответа верности, у вас есть один вариант:

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

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-a TreeView", что вызовет проблемы.
    • усилия по обслуживанию возможно высоки.
    • Если по какой-то причине древовидная структура решила приостановить себя, это сломается.
    • новая версия среды выполнения вряд ли что-то сломает, вы просто не получите новую функциональность без усилий.
  • Реализация совершенно нового TreeViewEx, который предоставляет это состояние
    • результирующий экземпляр больше не является "is-a 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, и тогда вы сможете проверить значение в любое время.

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

9 голосов
/ 24 марта 2010

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

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

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

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 голосов
/ 22 апреля 2009

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

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

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

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

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