Click here to Skip to main content
15,893,588 members
Articles / Desktop Programming / Windows Forms

An Alpha Channel Composited Windows Form with Designer Support

Rate me:
Please Sign up or sign in to vote.
4.96/5 (124 votes)
5 Oct 2009CPOL8 min read 324.1K   48.8K   279  
An alpha channel composited form for image based Window frames
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using AlphaForm;

#region notes

// This example shows how to use two instances of AlphaForm with one being the main
// window and the second being an animated drawer that slides in and out underneath
// the main form. The code here could be adapted to N slide out drawers as the logic
// for managing the animation and z-order issues is the same. It addresses the 
// following behaviours:
// 
// 1. The main form can be top-most or not as in either case the drawer window's 
//    ownership is set to the main form which keeps their z-order in sync.
// 2. A close event to either the main form or drawer window closes both.
// 3. Location changed event handlers enable dragging of both the main from and 
//    drawer window in tandem.

#endregion

namespace SlidingPanes
{
  public partial class Form1 : Form
  {

    public SlideOut m_drawer;
    Point m_drawerLocOld;
    Point m_mainLocOld;
    bool m_drawerOpen;
    bool m_lockLoc;
    
    
    public Form1()
    {
      InitializeComponent();
      m_drawer = new SlideOut();
      m_drawer.ShowInTaskbar = false;

      m_drawerOpen = false;
      m_lockLoc = false;
  	  
      LocationChanged += new EventHandler(MainFormLocationChanged);
      m_drawer.LocationChanged += new EventHandler(DrawerLocationChanged);

    }

    protected override void OnShown(EventArgs e)
    {
      alphaFormTransformer1.Fade(FadeType.FadeIn, false, false, 500);
      base.OnShown(e);
    }
    
    protected override void OnClosing(CancelEventArgs e)
    {
      // We want to close both the drawer window and the main form if either
      // receives a close event. So we use the Owner property as a flag to 
      // manage this. Also see OnClosing in SlideOut.cs.
      if (m_drawer.Owner != null)
        {
        m_drawer.Owner = null;
        m_drawer.Close(); 
        }
      alphaFormTransformer1.Fade(FadeType.FadeOut, true, false, 300);
      base.OnClosing(e);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      // It's going to fade in, so we must set opacity to 0.
      alphaFormTransformer1.TransformForm(0);
    }


    public void DrawerButton_Click(object sender, EventArgs e)
    {
      int ledge;

      // See docs in AlphaForm on this class
      Impulse imp = new Impulse(7.0);

      int frames;
      int duration;

      bool SaveTop = TopMost;

      // Suppress location changed event handlers for drawer and main form
      m_lockLoc = true;

      if (m_drawerOpen)
      {
        duration = 400;
        
        // owner must be null to guarantee drawer window will appear under
        // the main form's layered window.
        m_drawer.Owner = null;

        // Force main form to topmost so that drawer is underneath
        TopMost = true;

        ledge = m_drawer.Left;

        int op = 0;
        int uop = 0;

        // 30-40 fps is a reasonable median starting value (it will
        // be adjusted, on-the-fly, up or down to give 
        // the greatest frame rate for the requested duration)
        // The only risk to setting an initially high fps is on slower
        // graphics displays where this routine will make a larger
        // reduction in frame rate to meet the desired duration. Such
        // on-the-fly frame rate adjustments may be perceived as jitteryness.
        // The inverse is also true - setting it initially too low will force
        // a fast graphics system to make large increases to the frame rate.
        // (Convergence in either case is very fast though :-))
        
        frames = Math.Max((40 * duration) / 1000, 1);
        int inc = Math.Max(m_drawer.Width / frames, 1);
        int frameDur = duration / frames;

        // 0.92 figured by trial & error - because the frame of the drawer
        // window image includes some empty space, and I want it to slide in/out
        // right to the edge. If I wasn't so lazy, I'd crop the image in 
        // Photoshop instead of doing this :-) However this illustrates an
        // important point. If you need to rely on some fraction of the drawer
        // window width or height as an animation parameter, don't use an
        // absolute pixel value as that won't scale for different
        // DPI screens.
        int slideW = (int)(0.92 * m_drawer.Width);

        while (uop != slideW)
        {
          uop = (int)Math.Round(imp.Evaluate(op / (double)slideW) * (double)slideW);

          if (uop > slideW)
            uop = slideW;
          DateTime sf = DateTime.Now;
          m_drawer.SetDesktopLocation(ledge - uop, m_drawer.Location.Y);
          TimeSpan ts = DateTime.Now - sf;
          int wait = frameDur - ts.Milliseconds;

          m_drawer.Update(); // force contents to update

          // We either wait or speed up to maintain requested duration
          if (wait > 0)
          {
              System.Threading.Thread.Sleep(wait);
              if (wait <= frameDur/2 && inc > 1)
              {
                  inc = Math.Max(1, inc / 2);
                  frameDur /= 2;
              }
          }
          else if (Math.Abs(wait) >= frameDur)
          {
              inc *= 2;
              frameDur *= 2;
          }

          op += inc;
        }

        m_drawerOpen = false;

        // Hide the drawer window (it's closed - behind the main form now).
        // Note: We need to hide the composite window which includes the layered window.
        // The Visible property hides the base class property in SlideOut, and a utility function 
        // is called to set the property for both the form and layered window (see
        // SlideOut.cs).
        m_drawer.Visible = false;
        
        DrawerButton.Text = "Open Drawer ->";

      }
      else
      {
        // Setting TopMost on this form won't guarantee the drawer window will be shown 
        // underneath, and we want it to appear behind the main form when made visible.
        // An easy way to achieve this is to just show the drawer window off-screen first, display, then 
        // set TopMost on the main form to ensure the drawer is behind.
        m_drawer.Location = new Point(-1000000, -1000000);
        
        // Note: We need to show the composite window which includes the layered window.
        // The Visible property hides the base class property in SlideOut, and a utility function 
        // is called to set the property for both the form and layered window (see
        // SlideOut.cs).
        m_drawer.Visible = true;
        
        TopMost = true;
        
        // The drawer window is behind (z-order) and off-screen now, so move it into position
        // for animating.
        m_drawer.Location = new Point(Location.X + Width - m_drawer.Width, Location.Y + (Height - m_drawer.Height) / 2);

        ledge = m_drawer.Left;
        duration = 600; // arbitrary

        int op = 0;
        int uop = 0;
        
        // 30-40 fps is a reasonable median starting value (it will
        // be adjusted, on-the-fly, up or down to give 
        // the greatest frame rate for the requested duration)
        // The only risk to setting an initially high fps is on slower
        // graphics displays where this routine will make a larger
        // reduction in frame rate to meet the desired duration. Such
        // on-the-fly frame rate adjustments may be perceived as jitteryness.
        // The inverse is also true - setting it initially too low will force
        // a fast graphics system to make large increases to the frame rate.
        // (Convergence in either case is very fast though :-))
        
        frames = Math.Max((40 * duration) / 1000, 1);
        int inc = Math.Max(m_drawer.Width / frames, 1);
        int frameDur = duration / frames;
        
        // See note above on where this 0.92 comes from.
        int slideW = (int) (0.92 * m_drawer.Width);

        while (uop != slideW)
        {
          uop = (int)Math.Round(imp.Evaluate(op / (double)slideW) * (double)slideW);

          if (uop > slideW)
            uop = slideW;
          DateTime sf = DateTime.Now;
          m_drawer.SetDesktopLocation(ledge + uop, m_drawer.Location.Y);
          TimeSpan ts = DateTime.Now - sf;
          int wait = frameDur - ts.Milliseconds;

          m_drawer.Update(); // force contents to update

          // We either wait or speed up to maintain requested duration
          if (wait > 0)
          {
            System.Threading.Thread.Sleep(wait);

            if (wait <= frameDur / 2 && inc > 1)
            {
              inc = Math.Max(1, inc / 2);
              frameDur /= 2;
            }
          }
          else if (Math.Abs(wait) >= frameDur)
          {
            inc *= 2;
            frameDur *= 2;
          }

          op += inc;
        }

        // Once drawer is displayed, we set ownership to main form so that
        // the two are in sync with respect to activation and close events.
        m_drawer.Owner = this;
        m_drawerLocOld = m_drawer.Location;
        m_drawerOpen = true;
        DrawerButton.Text = "<-- Close Drawer";
      }

      TopMost = SaveTop;
      m_lockLoc = false;
    }

    // These location change event handlers enable synchroneous dragging of
    // the main form and the drawer window
    void DrawerLocationChanged(Object sender, EventArgs e)
    {
      bool saveLock = m_lockLoc;
      if (!m_lockLoc)
      {
        m_lockLoc = true;
        SetDesktopLocation(Location.X + m_drawer.Location.X - m_drawerLocOld.X, Location.Y + m_drawer.Location.Y - m_drawerLocOld.Y);
        m_mainLocOld = Location;
      }
      m_drawerLocOld = m_drawer.Location;
      m_lockLoc = saveLock;
    }

    void MainFormLocationChanged(Object sender, EventArgs e)
    {
      bool saveLock = m_lockLoc;
      if (!m_lockLoc)
      {
        m_lockLoc = true;
        m_drawer.SetDesktopLocation(m_drawer.Location.X + Location.X - m_mainLocOld.X, m_drawer.Location.Y + Location.Y - m_mainLocOld.Y);
        m_drawerLocOld = m_drawer.Location;
      }
      m_mainLocOld = Location;
      m_lockLoc = saveLock;
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Close();
    }

 
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
Jeff Anderson has been a computer graphics software developer for over twenty years.

He is a co-founder of Braid Art Labs and a developer of Braid's GroBoto 3D software. Jeff also dabbles in shareware with an expanded version of the AlphaForm control and other programs here.

Comments and Discussions