Featured Post

Organize and rename photos by EXIF data with PowerShell

This PowerShell script organizes and renames all photos in a selected folder using EXIF data. It will also create thumbnails of the images i...

Monday, November 11, 2013

(WPF) Creating a Custom Window

Here is what you will end up with, looks like a window doesn't it? But you can customize every aspect of it.



Create a new project of type Class Library named CustomControls.

CustomWindow.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace CustomControls
{
    public partial class CustomWindow : Window
    {
        #region Click events

        protected void MinimizeClick(object sender, RoutedEventArgs e)
        {
            WindowState = WindowState.Minimized;
        }

        protected void RestoreClick(object sender, RoutedEventArgs e)
        {
            WindowState = (WindowState == WindowState.Normal) ? WindowState.Maximized : WindowState.Normal;
        }

        protected virtual void CloseClick(object sender, RoutedEventArgs e)
        {
            Close();
        }

        #endregion

        static CustomWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow),
                new FrameworkPropertyMetadata(typeof(CustomWindow)));
        }

        public CustomWindow()
            : base()
        {
            PreviewMouseMove += OnPreviewMouseMove;
        }

        private HwndSource _hwndSource;

        protected override void OnInitialized(EventArgs e)
        {
            SourceInitialized += OnSourceInitialized;
            base.OnInitialized(e);
        }

        private void OnSourceInitialized(object sender, EventArgs e)
        {
            _hwndSource = (HwndSource)PresentationSource.FromVisual(this);
        }

        public override void OnApplyTemplate()
        {

            Rectangle moveRectangle = GetTemplateChild("moveRectangle") as Rectangle;
            if (moveRectangle != null)
                moveRectangle.PreviewMouseDown += moveRectangle_PreviewMouseDown;

            Button minimizeButton = GetTemplateChild("minimizeButton") as Button;
            if (minimizeButton != null)
                minimizeButton.Click += MinimizeClick;

            Button restoreButton = GetTemplateChild("restoreButton") as Button;
            if (restoreButton != null)
                restoreButton.Click += RestoreClick;

            Button closeButton = GetTemplateChild("closeButton") as Button;
            if (closeButton != null)
                closeButton.Click += CloseClick;

            Grid resizeGrid = GetTemplateChild("resizeGrid") as Grid;
            if (resizeGrid != null)
            {
                foreach (UIElement element in resizeGrid.Children)
                {
                    Rectangle resizeRectangle = element as Rectangle;
                    if (resizeRectangle != null)
                    {
                        resizeRectangle.PreviewMouseDown += ResizeRectangle_PreviewMouseDown;
                        resizeRectangle.MouseMove += ResizeRectangle_MouseMove;
                    }
                }
            }

            base.OnApplyTemplate();
        }

        protected void ResizeRectangle_MouseMove(Object sender, MouseEventArgs e)
        {
            Rectangle rectangle = sender as Rectangle;
            switch (rectangle.Name)
            {
                case "top":
                    Cursor = Cursors.SizeNS;
                    break;
                case "bottom":
                    Cursor = Cursors.SizeNS;
                    break;
                case "left":
                    Cursor = Cursors.SizeWE;
                    break;
                case "right":
                    Cursor = Cursors.SizeWE;
                    break;
                case "topLeft":
                    Cursor = Cursors.SizeNWSE;
                    break;
                case "topRight":
                    Cursor = Cursors.SizeNESW;
                    break;
                case "bottomLeft":
                    Cursor = Cursors.SizeNESW;
                    break;
                case "bottomRight":
                    Cursor = Cursors.SizeNWSE;
                    break;
                default:
                    break;
            }
        }

        private void moveRectangle_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (Mouse.LeftButton == MouseButtonState.Pressed)
                DragMove();
        }

        protected void OnPreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (Mouse.LeftButton != MouseButtonState.Pressed)
                Cursor = Cursors.Arrow;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

        protected void ResizeRectangle_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            Rectangle rectangle = sender as Rectangle;
            switch (rectangle.Name)
            {
                case "top":
                    Cursor = Cursors.SizeNS;
                    ResizeWindow(ResizeDirection.Top);
                    break;
                case "bottom":
                    Cursor = Cursors.SizeNS;
                    ResizeWindow(ResizeDirection.Bottom);
                    break;
                case "left":
                    Cursor = Cursors.SizeWE;
                    ResizeWindow(ResizeDirection.Left);
                    break;
                case "right":
                    Cursor = Cursors.SizeWE;
                    ResizeWindow(ResizeDirection.Right);
                    break;
                case "topLeft":
                    Cursor = Cursors.SizeNWSE;
                    ResizeWindow(ResizeDirection.TopLeft);
                    break;
                case "topRight":
                    Cursor = Cursors.SizeNESW;
                    ResizeWindow(ResizeDirection.TopRight);
                    break;
                case "bottomLeft":
                    Cursor = Cursors.SizeNESW;
                    ResizeWindow(ResizeDirection.BottomLeft);
                    break;
                case "bottomRight":
                    Cursor = Cursors.SizeNWSE;
                    ResizeWindow(ResizeDirection.BottomRight);
                    break;
                default:
                    break;
            }
        }

        private void ResizeWindow(ResizeDirection direction)
        {
            SendMessage(_hwndSource.Handle, 0x112, (IntPtr)(61440 + direction), IntPtr.Zero);
        }

        private enum ResizeDirection
        {
            Left = 1,
            Right = 2,
            Top = 3,
            TopLeft = 4,
            TopRight = 5,
            Bottom = 6,
            BottomLeft = 7,
            BottomRight = 8,
        }
    }
}

Themes\Generic.xaml

<resourcedictionary xmlns:local="clr-namespace:CustomControls" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

    <!-- Window Button style -->
    <style targettype="{x:Type Button}" x:key="WindowButtonStyle">
        <setter Property="Background" Value="Transparent"/>
        <setter Property="Foreground" Value="{DynamicResource GreyLightBrush}" />
        <setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
        <setter Property="FontFamily" Value="Webdings"/>
        <setter Property="FontSize" Value="13.333" />
        <setter Property="Margin" Value="1,1,1,0"/>
        <setter Property="Template">
            <Setter.Value>
                <controltemplate TargetType="{x:Type ButtonBase}">
                    <border x:Name="Chrome"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            Background="{TemplateBinding Background}"
                            Margin="0"
                            SnapsToDevicePixels="True">
                        <ContentPresenter
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                Content="{TemplateBinding Content}"
                                ContentStringFormat="{TemplateBinding ContentStringFormat}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                Margin="{TemplateBinding Padding}"
                                RecognizesAccessKey="True"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <trigger Property="IsMouseOver" Value="True">
                <setter Property="Foreground" Value="{DynamicResource WhiteBrush}" />
            </Trigger>
        </Style.Triggers>
    </style>

    <!-- Window style -->
    <style targettype="{x:Type local:CustomWindow}">
        <setter Property="WindowStyle" Value="None"/>
        <setter Property="ResizeMode" Value="NoResize"/>
        <setter Property="Foreground" Value="{DynamicResource ForegroundBrush}" />
        <setter Property="Background" Value="{DynamicResource BackgroundBrush}"/>
        <setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
        <setter Property="BorderThickness" Value="1"/>        
        <setter Property="Template">
            <Setter.Value>
                <controltemplate TargetType="{x:Type local:CustomWindow}">
                    <border BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <grid>
                            <grid>
                                <Grid.RowDefinitions>
                                    <rowdefinition Height="Auto"/>
                                    <rowdefinition />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <columndefinition />
                                    <columndefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <rectangle x:Name="moveRectangle" Fill="Transparent" Grid.Row="0" Grid.Column="0"/>
                                <textblock x:Name="windowTitle" Margin="10 0"
                                           FontSize="16"
                                           Grid.Column="0" Grid.Row="0" Panel.ZIndex="-1"
                                           Foreground="{DynamicResource GreyLightBrush}"
                                           Text="{TemplateBinding Title}"/>
                                <stackpanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal">
                                    <button x:Name="minimizeButton" Style="{StaticResource WindowButtonStyle}"
                                            Content="0" />
                                    <button x:Name="restoreButton" Style="{StaticResource WindowButtonStyle}"
                                            Content="1" />
                                    <button x:Name="closeButton" Style="{StaticResource WindowButtonStyle}"
                                            Content="r" />
                                </StackPanel>
                                <grid Background="{TemplateBinding Background}"
                                           Grid.Row="1" Grid.ColumnSpan="2" Margin="5,5,5,5">
                                    <adornerdecorator>
                                        <ContentPresenter/>
                                    </AdornerDecorator>
                                </Grid>
                            </Grid>

                            <grid x:Name="resizeGrid">
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    VerticalAlignment="Top"
                                    Height="5"
                                    x:Name="top"
                                    Margin="5,0,5,0" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    x:Name="bottom"
                                    Height="5"
                                    VerticalAlignment="Bottom"
                                    Margin="5,0,5,0" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    HorizontalAlignment="Left"
                                    Margin="0,5,0,5"
                                    Width="5"
                                    x:Name="left"/>
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    Margin="0,5,0,5"
                                    Width="5"
                                    HorizontalAlignment="Right"
                                    x:Name="right" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    HorizontalAlignment="Left"
                                    VerticalAlignment="Bottom"
                                    Width="5"
                                    Height="5"
                                    x:Name="bottomLeft" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    VerticalAlignment="Bottom"
                                    Height="5"
                                    Width="5"
                                    HorizontalAlignment="Right"
                                    x:Name="bottomRight" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    HorizontalAlignment="Right"
                                    Width="5"
                                    Height="5"
                                    VerticalAlignment="Top"
                                    x:Name="topRight" />
                                <Rectangle
                                    Stroke="{x:Null}"
                                    Fill="Transparent"
                                    HorizontalAlignment="Left"
                                    Width="6"
                                    VerticalAlignment="Top"
                                    Height="5"
                                    x:Name="topLeft" />
                            </Grid>

                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </style>
</resourcedictionary>

Now you can include this custom controls project as a reference in whichever project you want to use the custom window. After adding it as a reference, here is how to implement it, very simple!

MainWindow.xaml

<control:customwindow height="350" title="MainWindow" width="525"
        x:class="YourApp.MainWindow"
        xmlns:control="clr-namespace:CustomControls;assembly=CustomControls"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <grid>
        
    </grid>
</control:customwindow>


And in the code behind we inherit from our custom class:

MainWindow.xaml.cs
public partial class MainWindow : CustomControls.CustomWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }
}


No comments:

Post a Comment