Question

I would like to be able to create a black custom window (with border and controls) like that shipped as part of expression blend, Twirl, or Adobe Lightroom.

Is there a best practices way of creating an owner drawn window?

Platform: C# and WindowsForms (any version)

Was it helpful?

OTHER TIPS

If the custom-chrome tools don't provide you with the look-and-feel that you want, this kind of thing is easy to do yourself in C#. Basically, you create a borderless form (FormBorderStyle = None) and then create all the controls and borders yourself, by placing controls where you need them (a label for the title bar, command buttons for close and minimize etc.) and/or drawing directly on the form's surface using the Graphics object.

You also have to implement code to allow the form to be dragged around by its "fake" title bar (see this answer for a sample of how to do this). You may also have to implement your own resizing mechanism (if you need the forms to be resizable).

Finally, although the custom-form code might be a bit clunky, you can implement it on a single form and then have all the other forms in your application inherit from this form, which makes it a very useful technique for custom-skinning an entire application.

My task was to make active window more noticeable, bright - than others, inactive windows of the app. App has many opened windows, some modal, some modeless - and the MDI parent one.

You can can use something like not-a-border - a frame inside the client area. Here is the code snippet, a part of a base class (can be used directly in a form):

    #region Кастомизированное поведение - рамки, активность и т.д.
    private bool isCurrentlyActive = false;
    private bool childControlsAreHandled = false;
    private Pen activeWindowFramePen, inactiveWindowFramePen;
    private Point[] framePoints;

    private void AddControlPaintHandler(Control ctrl)
    {
        ctrl.Paint += DrawWindowFrame;
        if (ctrl.Controls != null)
        {
            foreach (Control childControl in ctrl.Controls)
            {
                AddControlPaintHandler(childControl);
            }
        }
    }

    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);
        if ((this.childControlsAreHandled == false)
            && (WindowFrameType != Forms.WindowFrameType.NoFrame)
            && (this.MdiParent == null))
        {
            RecalculateWindowFramePoints();
            AddControlPaintHandler(this);
            this.childControlsAreHandled = true;
        }

        this.isCurrentlyActive = true;
        if (InactiveWindowOpacity < 1)
        {
            base.Opacity = 1;
        }
        base.Invalidate(true);
    }

    protected override void OnDeactivate(EventArgs e)
    {
        base.OnDeactivate(e);
        this.isCurrentlyActive = false;
        if (InactiveWindowOpacity < 1)
        {
            base.Opacity = InactiveWindowOpacity;
        }
        base.Invalidate(true);
    }

    protected override void OnResizeEnd(EventArgs e)
    {
        base.OnResizeEnd(e);
        this.framePoints = null;
        RecalculateWindowFramePoints();
        this.Invalidate(true);
    }

    private Pen ActivePen
    {
        get
        {
            if (this.isCurrentlyActive)
            {
                if (this.activeWindowFramePen == null)
                {
                    this.activeWindowFramePen = new Pen(Color.FromArgb((int)(WindowFrameOpacity*255), WindowFrameActiveColor), WindowFrameSize * 2);
                }
                return this.activeWindowFramePen;
            }
            else
            {
                if (this.inactiveWindowFramePen == null)
                {
                    this.inactiveWindowFramePen = new Pen(Color.FromArgb((int)(WindowFrameOpacity*255), WindowFrameInactiveColor), WindowFrameSize * 2);
                }
                return this.inactiveWindowFramePen;
            }
        }
    }

    private Point[] RecalculateWindowFramePoints()
    {
        if ((WindowFrameType == Forms.WindowFrameType.AllSides)
            && (this.framePoints != null)
            && (this.framePoints.Length != 5))
        {
            this.framePoints = null;
        }
        if ((WindowFrameType == Forms.WindowFrameType.LeftLine)
            && (this.framePoints != null)
            && (this.framePoints.Length != 2))
        {
            this.framePoints = null;
        }
        if (this.framePoints == null)
        {
            switch (WindowFrameType)
            {
                case Forms.WindowFrameType.AllSides:
                    this.framePoints = new Point[5]
                    {
                        new Point(this.ClientRectangle.X, this.ClientRectangle.Y),
                        new Point(this.ClientRectangle.X + this.ClientRectangle.Width, this.ClientRectangle.Y),
                        new Point(this.ClientRectangle.X + this.ClientRectangle.Width, this.ClientRectangle.Y + this.ClientRectangle.Height),
                        new Point(this.ClientRectangle.X, this.ClientRectangle.Y + this.ClientRectangle.Height),
                        new Point(this.ClientRectangle.X, this.ClientRectangle.Y)
                    };
                    break;
                case Forms.WindowFrameType.LeftLine:
                    this.framePoints = new Point[2]
                    {
                        new Point(this.ClientRectangle.X, this.ClientRectangle.Y),
                        new Point(this.ClientRectangle.X, this.ClientRectangle.Y + this.ClientRectangle.Height)
                    };
                    break;
            }
        }
        return this.framePoints;
    }

    private void DrawWindowFrame(object sender, PaintEventArgs e)
    {
        if (WindowFrameType == Forms.WindowFrameType.NoFrame)
        {
            return;
        }
        if ((this.framePoints == null) || (this.framePoints.Length == 0))
        {
            return;
        }
        Control ctrl = (Control)(sender);
        // пересчитаем точки в координатах контрола.
        List<Point> pts = new List<Point>();
        foreach (var p in this.framePoints)
        {
            pts.Add(ctrl.PointToClient(this.PointToScreen(p)));
        }
        e.Graphics.DrawLines(ActivePen, pts.ToArray());
    }

    public static int WindowFrameSize = 2;
    public static WindowFrameType WindowFrameType = Forms.WindowFrameType.NoFrame;
    public static Color WindowFrameActiveColor = Color.YellowGreen;
    public static Color WindowFrameInactiveColor = SystemColors.ControlDark;
    public static double InactiveWindowOpacity = 1.0;
    public static double WindowFrameOpacity = 0.3;
    #endregion

Static fields of the class are initialized from application settings form (class) - so, all the forms in the app has the same behavior.

Hope that helps to someone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top