You see, you don’t always need code behind.

By now you should be aware that I’m a big fan of attached behaviors. In this post, I’m going to demonstrate a simple technique to add resize and close functionality to window buttons when you want to custom draw your window chrome without having to add code behind the window. This is going to be a quick post, because it’s just so darned easy.

Note: Originally I was using Application.Current.MainWindow to retrieve the window as I only ever apply this trick to the application main window. Mike Strobel suggested using Window.GetWindow instead to retrieve the logical window for the button, just in case it was in a separate window. I’ve adjusted the code sample here to demonstrate this as it makes this more reusable. Thanks Mike.

namespace AttachedTitleButtonsSample
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;

    /// <summary>
    /// Attach this behaviour to a button to enable a button to change the window state without
    /// having to write any code behind the view.
    /// </summary>
    public partial class TitleButtonBehavior : Behavior<Button>
    {
        /// <summary>
        /// The tile button action to apply.
        /// </summary>
        public enum TitleButtonAction
        {
            /// <summary>
            /// Close the application
            /// </summary>
            Close,
            /// <summary>
            /// Maximize the application
            /// </summary>
            Maximize,
            /// <summary>
            /// Minimize the application
            /// </summary>
            Minimize,
            /// <summary>
            /// Reset the application to normal
            /// </summary>
            Normal
        }

        /// <summary>
        /// Gets or sets the button behavior.
        /// </summary>
        public TitleButtonAction ButtonBehavior { get; set; }

        /// <summary>
        /// Add the click handler when this is attached.
        /// </summary>
        protected override void OnAttached()
        {
            this.AssociatedObject.Click += AssociatedObject_Click;
            base.OnAttached();
        }

        /// <summary>
        /// Remove the click handler when this is detached.
        /// </summary>
        protected override void OnDetaching()
        {
            this.AssociatedObject.Click -= AssociatedObject_Click;
            base.OnDetaching();
        }

        /// <summary>
        /// Change the window state when the button is clicked.
        /// </summary>
        void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            Window window = Window.GetWindow(AssociatedObject);
            switch (ButtonBehavior)
            {
                case TitleButtonAction.Close:
                    window.Close();
                    break;
                case TitleButtonAction.Maximize:
                    window.WindowState = WindowState.Maximized;
                    break;
                case TitleButtonAction.Minimize:
                    window.WindowState = WindowState.Minimized;
                    break;
                case TitleButtonAction.Normal:
                    window.WindowState = WindowState.Normal;
                    break;
            }
        }
    }
}

Basically, all you need to do is create an attached behavior that hooks up to the Click event of the button and sets the size based on the appropriate value from the enumeration.

Sample application
I’ve attached a sample application that demonstrates this technique in action. As always, when you download the sample, you’ll need to rename it from a doc to a zip file.

AttachedTitleButtonsSampleZip

6 thoughts on “You see, you don’t always need code behind.

  1. How about resolving the window using Window.GetWindow(this.AssociatedObject) so it works on the ancestor window instead of only with the application’s main window? 🙂

    1. peteohanlon

      Blimey Mike. That was quick. I’ve only just posted this.

      Anyway, you could do that – I’ve never needed to do this on anything other than the application’s main window.

  2. Ed

    On line 55 your adding the click handler again

    this.AssociatedObject.Click += AssociatedObject_Click;

    instead of removing it

    this.AssociatedObject.Click -= AssociatedObject_Click;

  3. Al

    This information is great but I need to take the log4net viewing a step further. Instead of sending the raw messages to a textbox, I need to implement a mechanism in WPF of buffering these messages and displaying them in another object show the most recent message at the top of the list. Then I would like to be able to “freeze” the display so that the user can scroll through the messages, and then “unfreeze” it so that any new messages that where bufferred can not be displayed. I’m not sure how to “capture” the messages into a collection that in this example are going directly to a textbox. Do you have any suggestions?

Leave a comment