这篇文章很不错,推荐大伙儿都看看

80酷酷网    80kuku.com

  Tips and Tricks
Contents
Flicker free drawing in controls
Embedding bitmaps in your manifest
Creating type safe collections the quick way
Flicker free drawing in controls
You have just spent several days writing a beautiful looking custom control but are faced with one last problem that is spoiling the whole effect. Whenever the control is redrawn it flickers. This is most obvious when the control is being resized and so redrawn many times in succession.

Solving this problem is very easy with the .NET Framework. If you come from a C++ and GDI background then the solution was to create a memory based bitmap, draw the control onto the bitmap and then blit this to the screen (otherwise known as double buffering). This is such a common requirement that the UserControl class actually implements this functionality for you. All you need to do is include the following two lines of code into your class constructor.


    SetStyle(ControlStyles.DoubleBuffer, true);
    SetStyle(ControlStyles.AllPaintingInWmPaint, true);


The first line will request that double buffering be used whenever the OnBackground or OnPaint methods are called. This will reduce then amount of flicker but may not remove it completely as painting the whole control still results in two separate blitting operations.

The second line of code above is used to ensure that only a single blitting operation occurs when drawing. This occurs when the underlying windows WM_PAINT message is processed. When this happens it will create the memory bitmap, call the OnBackground method, call the OnPaint method and then finally blit then result to the screen.

The only drawback to this technique is the greater use of resources. However, most controls are relatively small in screen size and so this is unlikely to be an issue.

Embedding bitmaps in your manifest
Usually a bitmap will be embedded into your assembly automatically, just by being associated with a property of a Form using the designer. But there are many circumstances where you may want to embed a bitmap (or other resource) without associating it with any particular Form .

You can do this in Visual Studio .NET by right-clicking the project of interest and selecting the 'Add Existing Item' option. Navigate to your bitmap and selecting it will make it appear in your project details. Now select the bitmap and modify the 'Build' property to become 'Embed as resource'. Rebuild your project and then use the ILDAsm.exe utility to examine the manifest for your built assembly. You will now see that the bitmap has been added as a resource.

To extract this resource at runtime is not obvious with only involves three steps.


    // Get the assembly we are built into
    Assembly myAssembly = Assembly.GetAssembly(Type.GetType("MyNamespace.ThisClass"));

    // Get the resource stream containing the embedded resource
    Stream imageStream = myAssembly.GetManifestResourceStream("Example.bmp");

    // Load the bitmap from the stream
    Bitmap pics = new Bitmap(imageStream);


The first line of code is used to get a reference to the assembly this code is built into. In your own code you should substitute the 'MyNamespace.ThisClass' string for the fully qualified name of the class the code is inside.

The second line requests from the assembly a stream that contains the contents of the named resource. This name will need to match the name that appears when using the ILDAsm.exe utility. Visual Studio will create this name as the default namespace appended with the name of the file. If your code generates an exception at this point then double check the name you provide exactly matches that inside the manifest.

The last line of code is obvious and simply uses the Bitmap constructor that takes as input a Stream .

If you prefer to build your projects manually without Visual Studio then you can still use the same technique. Just use the /resource:Example.bmp option in your csc command to cause the bitmap to be embedded. In which case the name in the manifest will exactly match the resource filename rather than be modified by Visual Studio.

Creating type safe collections the quick way
Often when writing your own class you need to expose a collection of items to the caller. Usually the caller will also need to modify that collection at runtime. For example, a TabControl has a collection of TabPage objects that the caller can modify to add and remove pages.

Although creating a collection class is not difficult it can be time consuming. Rather than begin from scratch it is much easier to derive from the framework class CollectionBase. This class will handle the management of the collection for us by using an ArrayList instance internally.

Two tasks however remain. First we need to generate events when the contents of the collection change, so that our class can perform appropriate actions. For example, when a new TabPage is added to a pages collection the TabControl would need to be notified of this so it can change the appearance appropriately. To achieve this we define a new class called CollectionWithEvents as shown below.


public class CollectionWithEvents : CollectionBase
{
    // Declare the event signatures
    public delegate void CollectionClear();
    public delegate void CollectionChange(int index, object value);

    // Collection change events
    public event CollectionClear Clearing;
    public event CollectionClear Cleared;
    public event CollectionChange Inserting;
    public event CollectionChange Inserted;
    public event CollectionChange Removing;
    public event CollectionChange Removed;
    
    // Overrides for generating events
    protected override void OnClear()
    {
        if (Clearing != null) Clearing();
    }        
    
    protected override void OnClearComplete()
    {
        if (Cleared != null) Cleared();
    }    

    protected override void OnInsert(int index, object value)
    {
        if (Inserting != null) Inserting(index, value);
    }

    protected override void OnInsertComplete(int index, object value)
    {
        if (Inserted != null) Inserted(index, value);
    }

    protected override void OnRemove(int index, object value)
    {
        if (Removing != null) Removing(index, value);
    }

    protected override void OnRemoveComplete(int index, object value)
    {
        if (Removed != null) Removed(index, value);
    }
}


Our second task is to ensure that the collection class is type specific. So we derive a type specific class from the CollectionWithEvents base class and expose the set of methods the caller needs to manipulate it. For example, a collection to manipulate objects of type MyType would look like the following.


public class MyTypeCollection : CollectionWithEvents
{
    public int Add(MyType value)
    {
        return base.List.Add(value as object);
    }

    public void Remove(MyType value)
    {
        base.List.Remove(value as object);
    }

    public void Insert(int index, MyType value)
    {
        base.List.Insert(index, value as object);
    }

    public bool Contains(MyType value)
    {
        return base.List.Contains(value as object);
    }

    public MyType this[int index]
    {
        get { return (base.List[index] as MyType); }
    }
}


The advantage of this design is that any additional collections we need involve creating just a single class identical to the one above but with the MyType replaced with whichever new type we desire.

Using the collection is very simple. Here is an example class that creates and exposes a collection for manipulation by the caller. The constructor hooks into some of the events it exposes in order to perform whatever implementation actions is needs.


class Example
{
    protected MyTypeCollection _collection;
    
    class Example()
    {
        // Create the new but empty collection
        _collection = new MyTypeCollection();

        // Hookup to whichever events are of interest
        _tabPages.Cleared += new CollectionWithEvents.CollectionClear(OnClearedPages);
        _tabPages.Inserted += new CollectionWithEvents.CollectionChange(OnInsertedPage);
        _tabPages.Removed += new CollectionWithEvents.CollectionChange(OnRemovedPage);
    }

    public MyTypeCollection MyTypes
    {
        get { return _collection; }
    }

    public MyType this[int index]
    {
        get { return _collection[index]; }
    }

    protected void OnClearedPages()
    {
        // TODO...your actions
    }

    protected void OnInsertedPage(int index, object value)
    {    
        MyType obj = value as MyType;
        // TODO...your actions
    }

    protected void OnRemovedPage(int index, object value)
    {
        MyType obj = value as MyType;
        // TODO...your actions
    }
}


The above example only hooks into the events that occur after the collection has been altered. In practice you will probably also want to hook into those that occur before the collection is modified.

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: