检查SuspendLayout

时间:2009-04-22 14:24:14

标签: c# .net windows winforms

C#中是否有办法检查对象是否挂起?我有一个TreeView,我需要知道它是否仍然挂起。

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

 // Do Stuff.

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

因为我在递归函数中有这个代码,所以我想知道TreeView是否已被暂停。

3 个答案:

答案 0 :(得分:11)

根据verminity的回答你有一个选择:

使用以下课程

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,然后您可以随时检查该值。

重申这是脆弱的,并且完全不适用于未严格控制底层运行时版本的环境,以及您可以在可能的重大努力上解决这一问题。 你最好包括一个静态初始化程序,它检查字段是否实际存在且是正确的类型,如果没有则中止。

答案 1 :(得分:9)

嗯,这是一种迟到的答案,但在内部,控件正在跟踪计数,并且只会在大多数简历声明中恢复。那你为什么一开始就关心它,你只需确保你调用暂停并在finally块中恢复它:

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.

HTH

答案 2 :(得分:3)

在System.Windows.Forms.Control的SuspendLayout方法中快速查看Reflector,显示以下内容:

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

由于它没有设置任何公共标志,并且OnLayoutSuspended()方法是内部的,所以无论如何都无法找出控件何时被暂停。

您可以使用新的Suspend和ResumeLayout方法创建树视图的子类,但由于基本方法不是虚拟的,因此很难保证在所有情况下都会调用它们。