WPF换肤之四:界面设计和代码设计分离

简介: 原文:WPF换肤之四:界面设计和代码设计分离说起WPF来,除了总所周知的图形处理核心的变化外,和Winform比起来,还有一个巨大的变革,那就是真正意义上做到了界面设计和代码设计的分离。这样可以让美工和程序分开进行,而不是糅合在一块,这样做的好处当然也是显而易见的:提高了开发效率。
原文: WPF换肤之四:界面设计和代码设计分离

说起WPF来,除了总所周知的图形处理核心的变化外,和Winform比起来,还有一个巨大的变革,那就是真正意义上做到了界面设计和代码设计的分离。这样可以让美工和程序分开进行,而不是糅合在一块,这样做的好处当然也是显而易见的:提高了开发效率。

原先的设计方式

在我们之前设计的代码中,每当添加一个新的窗体的时候,我总是会在这个新的窗体的XAML文件中加入如下的代码,以便使样式能够应用上去:

View Code
<Window x:Class="WpfApplication1.MsgWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="TestWindow" Height="391" Width="418" WindowStyle="None" AllowsTransparency="True" Background="Transparent" OpacityMask="White" ResizeMode="NoResize" PreviewMouseMove="ResetCursor" WindowStartupLocation="CenterScreen">
    <Grid Background="Transparent">
            <Border BorderThickness="5" BorderBrush="DarkGreen"  CornerRadius="10,10,10,10" MouseMove="DisplayResizeCursor" PreviewMouseDown="Resize" Name="top">
            <Border.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#eee"/>
                </LinearGradientBrush>
            </Border.Background>
            <Grid>
               <!--这里放置UIElement.-->
            </Grid>
            </Border>
    </Grid>
</Window>

然后,在后台中,为了使窗体能够在最大化时不遮蔽任务栏,拖拉窗体边缘能够改变窗口大小,点按窗体可以实现拖拉的时候,在后台加入了如下的代码:

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Diagnostics;
using System.Runtime.InteropServices;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for TestWindow.xaml
    /// </summary>
    public partial class MsgWindow : Window
    {
        private const int WM_SYSCOMMAND = 0x112;
        private HwndSource hs;
        IntPtr retInt = IntPtr.Zero;

        public MsgWindow()
        {
            InitializeComponent();
            this.SourceInitialized += new EventHandler(WSInitialized);
        }

        void WSInitialized(object sender, EventArgs e)
        {
            hs = PresentationSource.FromVisual(this) as HwndSource;
            hs.AddHook(new HwndSourceHook(WndProc));
        }

       public double relativeClip = 10;

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

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

        private void ResizeWindow(ResizeDirection direction)
        {
            SendMessage(hs.Handle, WM_SYSCOMMAND, (IntPtr)(61440 + direction), IntPtr.Zero);
        }

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

        private void Resize(object sender, MouseButtonEventArgs e)
        {
            Border clickedBorder = sender as Border;

            Point pos = Mouse.GetPosition(this);
            double x = pos.X;
            double y = pos.Y;
            double w = this.ActualWidth;
            double h = this.ActualHeight;

            if (x <= relativeClip & y <= relativeClip) // left top
            {
                this.Cursor = Cursors.SizeNWSE;
                ResizeWindow(ResizeDirection.TopLeft);
            }
            if (x >= w - relativeClip & y <= relativeClip) //right top
            {
                this.Cursor = Cursors.SizeNESW;
                ResizeWindow(ResizeDirection.TopRight);
            }

            if (x >= w - relativeClip & y >= h - relativeClip) //bottom right
            {
                this.Cursor = Cursors.SizeNWSE;
                ResizeWindow(ResizeDirection.BottomRight);
            }

            if (x <= relativeClip & y >= h - relativeClip)  // bottom left
            {
                this.Cursor = Cursors.SizeNESW;
                ResizeWindow(ResizeDirection.BottomLeft);
            }

            if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top
            {
                this.Cursor = Cursors.SizeNS;
                ResizeWindow(ResizeDirection.Top);
            }

            if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right
            {
                this.Cursor = Cursors.SizeWE;
                ResizeWindow(ResizeDirection.Right);
            }

            if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom
            {
                this.Cursor = Cursors.SizeNS;
                ResizeWindow(ResizeDirection.Bottom);
            }

            if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left
            {
                this.Cursor = Cursors.SizeWE;
                ResizeWindow(ResizeDirection.Left);
            }
        }

        private void DisplayResizeCursor(object sender, MouseEventArgs e)
        {
            Border clickBorder = sender as Border;

            Point pos = Mouse.GetPosition(this);
            double x = pos.X;
            double y = pos.Y;
            double w= this.ActualWidth;
            double h= this.ActualHeight;

            this.label1.Content = x + "--" + y;

            if (x <= relativeClip & y <= relativeClip) // left top
            {
                this.Cursor = Cursors.SizeNWSE;
            }
            if (x >= w - relativeClip & y <= relativeClip) //right top
            {
                this.Cursor = Cursors.SizeNESW;
            }

            if (x >= w - relativeClip & y >= h - relativeClip) //bottom right
            {
                this.Cursor = Cursors.SizeNWSE;
            }

            if (x <= relativeClip & y >= h - relativeClip)  // bottom left
            {
                this.Cursor = Cursors.SizeNESW;
            }

            if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top
            {
                this.Cursor = Cursors.SizeNS;
            }

            if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right
            {
                this.Cursor = Cursors.SizeWE;
            }

            if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom
            {
                this.Cursor = Cursors.SizeNS;
            }

            if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left
            {
                this.Cursor = Cursors.SizeWE;
            }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = (this.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal);
        }
        #region 这一部分用于最大化时不遮蔽任务栏
        private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
        {

            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

            // Adjust the maximized size and position to fit the work area of the correct monitor
            int MONITOR_DEFAULTTONEAREST = 0x00000002;
            System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

            if (monitor != System.IntPtr.Zero)
            {

                MONITORINFO monitorInfo = new MONITORINFO();
                GetMonitorInfo(monitor, monitorInfo);
                RECT rcWorkArea = monitorInfo.rcWork;
                RECT rcMonitorArea = monitorInfo.rcMonitor;
                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
            }

            Marshal.StructureToPtr(mmi, lParam, true);
        }

        /// <summary>
        /// POINT aka POINTAPI
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            /// <summary>
            /// x coordinate of point.
            /// </summary>
            public int x;
            /// <summary>
            /// y coordinate of point.
            /// </summary>
            public int y;

            /// <summary>
            /// Construct a point of coordinates (x,y).
            /// </summary>
            public POINT(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
        }

        /// <summary>
        /// 窗体大小信息
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct MINMAXINFO
        {
            public POINT ptReserved;
            public POINT ptMaxSize;
            public POINT ptMaxPosition;
            public POINT ptMinTrackSize;
            public POINT ptMaxTrackSize;
        };
        /// <summary> Win32 </summary>
        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct RECT
        {
            /// <summary> Win32 </summary>
            public int left;
            /// <summary> Win32 </summary>
            public int top;
            /// <summary> Win32 </summary>
            public int right;
            /// <summary> Win32 </summary>
            public int bottom;

            /// <summary> Win32 </summary>
            public static readonly RECT Empty = new RECT();

            /// <summary> Win32 </summary>
            public int Width
            {
                get { return Math.Abs(right - left); }  // Abs needed for BIDI OS
            }
            /// <summary> Win32 </summary>
            public int Height
            {
                get { return bottom - top; }
            }

            /// <summary> Win32 </summary>
            public RECT(int left, int top, int right, int bottom)
            {
                this.left = left;
                this.top = top;
                this.right = right;
                this.bottom = bottom;
            }


            /// <summary> Win32 </summary>
            public RECT(RECT rcSrc)
            {
                this.left = rcSrc.left;
                this.top = rcSrc.top;
                this.right = rcSrc.right;
                this.bottom = rcSrc.bottom;
            }

            /// <summary> Win32 </summary>
            public bool IsEmpty
            {
                get
                {
                    // BUGBUG : On Bidi OS (hebrew arabic) left > right
                    return left >= right || top >= bottom;
                }
            }
            /// <summary> Return a user friendly representation of this struct </summary>
            public override string ToString()
            {
                if (this == RECT.Empty) { return "RECT {Empty}"; }
                return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
            }

            /// <summary> Determine if 2 RECT are equal (deep compare) </summary>
            public override bool Equals(object obj)
            {
                if (!(obj is Rect)) { return false; }
                return (this == (RECT)obj);
            }

            /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
            public override int GetHashCode()
            {
                return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
            }


            /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
            public static bool operator ==(RECT rect1, RECT rect2)
            {
                return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
            }

            /// <summary> Determine if 2 RECT are different(deep compare)</summary>
            public static bool operator !=(RECT rect1, RECT rect2)
            {
                return !(rect1 == rect2);
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class MONITORINFO
        {
            /// <summary>
            /// </summary>            
            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

            /// <summary>
            /// </summary>            
            public RECT rcMonitor = new RECT();

            /// <summary>
            /// </summary>            
            public RECT rcWork = new RECT();

            /// <summary>
            /// </summary>            
            public int dwFlags = 0;
        }

        [DllImport("user32")]
        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

        [DllImport("User32")]
        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
        #endregion

        private void MyMacClass_SourceInitialized(object sender, EventArgs e)
        {
            hs = PresentationSource.FromVisual((Visual)sender) as HwndSource;
            hs.AddHook(new HwndSourceHook(WndProc));
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case 0x0024:/* WM_GETMINMAXINFO */
                    WmGetMinMaxInfo(hwnd, lParam);
                    handled = true;
                    break;
                default: break;
            }
            return (System.IntPtr)0;
        }

    }
}

如果按照上面的设计,那么每加入一个新的窗体,都要重复上面的两个步骤的话,加入一个工程中需要加入的新窗体特别多,估计这种操作足以让一个正常人疯掉了。并且假如以后border的颜色要修改,那得修改多少页面啊~~~

改进的设计方式

所以,为了便于设计和维护,实现所谓的UI和代码分析,让我们提出一个假设的方案来:

首先,所有的公共样式放到一个样式文件中,所有新加的窗体都能共享这个公共的样式文件。

其次,所有的公共事件(窗体最大化,最小化,关闭等),都放到一个公共的基类中,所有新加的窗体只要继承该基类,即可继承系统公用的事件操作。

那么,本着这个假设,我们开始来进行。

首先,对于公共样式,我们需要添加一个“资源词典”的页面,用来设计公共样式:

添加完成后,会看到如下XAML:

View Code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

</ResourceDictionary>

下面我们在这个文件中添加样式:

View Code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

                    x:Class="MyOwnerDrawnWindow.Resource_Dictionaries.MyTheme"

                    >

 

    <!-- Border defining the frame of the Window -->


    <Style x:Key="MywindowBorder" TargetType="Border">

        <Setter Property="CornerRadius" Value="10, 10, 10, 10" />

        <Setter Property="BorderBrush" Value="DarkGreen"></Setter>

        <Setter Property="BorderThickness" Value="5" />

        <Setter Property="HorizontalAlignment" Value="Stretch"></Setter>

        <Setter Property="VerticalAlignment" Value="Stretch"></Setter>

        <Setter Property="Background" Value="#ababab"></Setter>

    </Style>

 

    <ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type Window}">

        <Grid>

            <Border x:Name="MyBorder"  Style="{StaticResource MywindowBorder}" >

                <Border.Background>

                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

                        <GradientStop Color="#eee"/>

                    </LinearGradientBrush>

                </Border.Background>

                <!--这一句很重要,主要用于放置界面元素,和asp.net中的masterpage有点像-->

                 <AdornerDecorator>

                    <ContentPresenter />

                </AdornerDecorator>

            </Border>

        </Grid>

    </ControlTemplate>

 

    <!-- My Window Style -->

    <Style x:Key="MyWindowStyle" TargetType="Window">

        <Setter Property="Background" Value="Transparent" />

        <Setter Property="WindowStyle" Value="None" />

        <Setter Property="AllowsTransparency" Value="True" />

        <Setter Property="Template" Value="{StaticResource MyWindowTemplate}" />

    </Style>

</ResourceDictionary>

好了,这就是我们的样式文件,接下来我们需要处理这个样式中的Border事件,让其支持鼠标左键拖拉功能。

新建一个类,命名为MyThemeClass.cs,让其继承自Window基类。在MyThemeClass类中,我们主要处理两个内容,一个是支持鼠标左键拖拉以便改变窗体大小,另一个是使窗体不遮蔽任务栏。

具体代码如下:

View Code
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Media;

using System.Windows.Interop;

using System.Runtime.InteropServices;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace MyOwnerDrawnWindow

{

    public class MyThemeClass:Window

    {

         private const int WM_SYSCOMMAND = 0x112;

        public const int WM_LBUTTONUP = 0x0202;

        private HwndSource hs;

        IntPtr retInt = IntPtr.Zero;

        public double relativeClip = 10;

 

        public MyThemeClass()

        {

            this.Loaded += delegate

            {

                InitializeEvent();

            };

            this.SourceInitialized +=new EventHandler(MyMacClass_SourceInitialized);

        }

 

        private void MyMacClass_SourceInitialized(object sender, EventArgs e)

        {

            hs = PresentationSource.FromVisual((Visual)sender) as HwndSource;

            hs.AddHook(new HwndSourceHook(WndProc));

        }

 

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

        {

            switch (msg)

            {

                case 0x0024:/* WM_GETMINMAXINFO */

                    WmGetMinMaxInfo(hwnd, lParam);

                    handled = true;

                    break;

                default: break;

            }

            return (System.IntPtr)0;

        }

 

        #region 这一部分用于最大化时不遮蔽任务栏

        private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)

        {

 

            MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

 

            // Adjust the maximized size and position to fit the work area of the correct monitor

            int MONITOR_DEFAULTTONEAREST = 0x00000002;

            System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

 

            if (monitor != System.IntPtr.Zero)

            {

 

                MONITORINFO monitorInfo = new MONITORINFO();

                GetMonitorInfo(monitor, monitorInfo);

                RECT rcWorkArea = monitorInfo.rcWork;

                RECT rcMonitorArea = monitorInfo.rcMonitor;

                mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);

                mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);

                mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);

                mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);

            }

 

            Marshal.StructureToPtr(mmi, lParam, true);

        }

 

        /// <summary>

        /// POINT aka POINTAPI

        /// </summary>

        [StructLayout(LayoutKind.Sequential)]

        public struct POINT

        {

            /// <summary>

            /// x coordinate of point.

            /// </summary>

            public int x;

            /// <summary>

            /// y coordinate of point.

            /// </summary>

            public int y;

 

            /// <summary>

            /// Construct a point of coordinates (x,y).

            /// </summary>

            public POINT(int x, int y)

            {

                this.x = x;

                this.y = y;

            }

        }

 

        [StructLayout(LayoutKind.Sequential)]

        public struct MINMAXINFO

        {

            public POINT ptReserved;

            public POINT ptMaxSize;

            public POINT ptMaxPosition;

            public POINT ptMinTrackSize;

            public POINT ptMaxTrackSize;

        };

        [StructLayout(LayoutKind.Sequential, Pack = 0)]

        public struct RECT

        {

            /// <summary> Win32 </summary>

            public int left;

            /// <summary> Win32 </summary>

            public int top;

            /// <summary> Win32 </summary>

            public int right;

            /// <summary> Win32 </summary>

            public int bottom;

 

            /// <summary> Win32 </summary>

            public static readonly RECT Empty = new RECT();

 

            /// <summary> Win32 </summary>

            public int Width

            {

                get { return Math.Abs(right - left); }  // Abs needed for BIDI OS

            }

            /// <summary> Win32 </summary>

            public int Height

            {

                get { return bottom - top; }

            }

 

            /// <summary> Win32 </summary>

            public RECT(int left, int top, int right, int bottom)

            {

                this.left = left;

                this.top = top;

                this.right = right;

                this.bottom = bottom;

            }

 

 

            /// <summary> Win32 </summary>

            public RECT(RECT rcSrc)

            {

                this.left = rcSrc.left;

                this.top = rcSrc.top;

                this.right = rcSrc.right;

                this.bottom = rcSrc.bottom;

            }

        }

 

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

        public class MONITORINFO

        {

            /// <summary>

            /// </summary>           

            public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

 

            /// <summary>

            /// </summary>           

            public RECT rcMonitor = new RECT();

 

            /// <summary>

            /// </summary>           

            public RECT rcWork = new RECT();

 

            /// <summary>

            /// </summary>           

            public int dwFlags = 0;

        }

 

        [DllImport("user32")]

        internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

 

        [DllImport("User32")]

        internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

        #endregion

 

        #region 这一部分是四个边加上四个角

        public enum ResizeDirection

        {

            Left = 1,

            Right = 2,

            Top = 3,

            TopLeft = 4,

            TopRight = 5,

            Bottom = 6,

            BottomLeft = 7,

            BottomRight = 8,

        }

        #endregion

 

        #region 用于改变窗体大小

        [DllImport("user32.dll", CharSet = CharSet.Auto)]

        private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

 

        private void ResizeWindow(ResizeDirection direction)

        {

            SendMessage(hs.Handle, WM_SYSCOMMAND, (IntPtr)(61440 + direction), IntPtr.Zero);

        }

        #endregion

 

        #region 为元素注册事件

        private void InitializeEvent()

        {

            ControlTemplate baseWindowTemplate = (ControlTemplate)App.Current.Resources["MyWindowTemplate"];

            Border borderClip = (Border)baseWindowTemplate.FindName("MyBorder", this);

 

            borderClip.MouseMove += delegate

            {

                DisplayResizeCursor(null,null);

            };

 

            borderClip.PreviewMouseDown += delegate

            {

                Resize(null,null);

            };

 

            borderClip.MouseLeftButtonDown += delegate

            {

                DragMove();

            };

 

            this.PreviewMouseMove += delegate

            {

                ResetCursor(null,null);

            };

        }

        #endregion

 

        #region 重写的DragMove,以便解决利用系统自带的DragMove出现Exception的情况

        public new void DragMove()

        {

            if (this.WindowState == WindowState.Normal)

            {

                SendMessage(hs.Handle, WM_SYSCOMMAND, (IntPtr)0xf012, IntPtr.Zero);

                SendMessage(hs.Handle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);

            }

        }

        #endregion

 

        #region 显示拖拉鼠标形状

        private void DisplayResizeCursor(object sender, MouseEventArgs e)

        {

            Point pos = Mouse.GetPosition(this);

            double x = pos.X;

            double y = pos.Y;

            double w = this.ActualWidth;  //注意这个地方使用ActualWidth,才能够实时显示宽度变化

            double h = this.ActualHeight;

 

            if (x <= relativeClip & y <= relativeClip) // left top

            {

                this.Cursor = Cursors.SizeNWSE;

            }

            if (x >= w - relativeClip & y <= relativeClip) //right top

            {

                this.Cursor = Cursors.SizeNESW;

            }

 

            if (x >= w - relativeClip & y >= h - relativeClip) //bottom right

            {

                this.Cursor = Cursors.SizeNWSE;

            }

 

            if (x <= relativeClip & y >= h - relativeClip)  // bottom left

            {

                this.Cursor = Cursors.SizeNESW;

            }

 

            if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top

            {

                this.Cursor = Cursors.SizeNS;

            }

 

            if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right

            {

                this.Cursor = Cursors.SizeWE;

            }

 

            if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom

            {

                this.Cursor = Cursors.SizeNS;

            }

 

            if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left

            {

                this.Cursor = Cursors.SizeWE;

            }

        }

        #endregion

 

        #region  还原鼠标形状

        private void ResetCursor(object sender, MouseEventArgs e)

        {

            if (Mouse.LeftButton != MouseButtonState.Pressed)

            {

                this.Cursor = Cursors.Arrow;

            }

        }

        #endregion

 

        #region 判断区域,改变窗体大小

        private void Resize(object sender, MouseButtonEventArgs e)

        {

            Point pos = Mouse.GetPosition(this);

            double x = pos.X;

            double y = pos.Y;

            double w = this.ActualWidth;

            double h = this.ActualHeight;

 

            if (x <= relativeClip & y <= relativeClip) // left top

            {

                this.Cursor = Cursors.SizeNWSE;

                ResizeWindow(ResizeDirection.TopLeft);

            }

            if (x >= w - relativeClip & y <= relativeClip) //right top

            {

                this.Cursor = Cursors.SizeNESW;

                ResizeWindow(ResizeDirection.TopRight);

            }

 

            if (x >= w - relativeClip & y >= h - relativeClip) //bottom right

            {

                this.Cursor = Cursors.SizeNWSE;

                ResizeWindow(ResizeDirection.BottomRight);

            }

 

            if (x <= relativeClip & y >= h - relativeClip)  // bottom left

            {

                this.Cursor = Cursors.SizeNESW;

                ResizeWindow(ResizeDirection.BottomLeft);

            }

 

            if ((x >= relativeClip & x <= w - relativeClip) & y <= relativeClip) //top

            {

                this.Cursor = Cursors.SizeNS;

                ResizeWindow(ResizeDirection.Top);

            }

 

            if (x >= w - relativeClip & (y >= relativeClip & y <= h - relativeClip)) //right

            {

                this.Cursor = Cursors.SizeWE;

                ResizeWindow(ResizeDirection.Right);

            }

 

            if ((x >= relativeClip & x <= w - relativeClip) & y > h - relativeClip) //bottom

            {

                this.Cursor = Cursors.SizeNS;

                ResizeWindow(ResizeDirection.Bottom);

            }

 

            if (x <= relativeClip & (y <= h - relativeClip & y >= relativeClip)) //left

            {

                this.Cursor = Cursors.SizeWE;

                ResizeWindow(ResizeDirection.Left);

            }

        }


        #endregion

    }

}

这样,我们的Theme和类就创建好了,那么,在主窗体中,我们该如何应用上去呢?当然,这个不是难事,并且异常简单:

首先,创建一个新的窗体Window1, 它需要继承自MyThemeClass类:

public partial class Window1 : MyThemeClass

然后再XAML中只需要修改两个地方,首先是添加入一个新的引用:

xmlns:src="clr-namespace:MyOwnerDrawnWindow"

添加完这个引用后,把<Window….></Window>修改成<src:MyThemeClass……></ MyThemeClass> 这样做的目的是防止由于继承基类的不同而造成XAML的识别错误。

最后,只需要添加这句Style="{StaticResource MyWindowStyle}"  就行了。 这样就算是添加完成了新的窗体,每一个新添加的窗体都按照这种方式添加即可,是不是简洁了许多? 并且后期维护也简单多了。

View Code
<src:MyThemeClass x:Class="MyOwnerDrawnWindow.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:src="clr-namespace:MyOwnerDrawnWindow"

    Title="Window1" Height="335" Width="706"

    Style="{StaticResource MyWindowStyle}">

    <Grid>

       <!--这里放置你的UIElement-->

    </Grid>

</src:MyThemeClass>

好了,最后一步就是讲Theme文件关联起来,在APP.xaml文件中添加对资源文件的引用即可:

View Code
<Application x:Class="MyOwnerDrawnWindow.App"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    StartupUri="Window1.xaml">

   <Application.Resources>

        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>

                <ResourceDictionary Source="MyTheme.xaml"></ResourceDictionary>

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>

    </Application.Resources>

</Application>

这样运行起来以后,达到的预期的效果。

源码下载

源码下载

 

 

 

目录
相关文章
|
C# 程序员 测试技术
WPF之动态换肤
原文:WPF之动态换肤 如何实现换肤呢,对于复杂的换肤操作,如,更换按钮样式、窗口样式等,我们需要写多个资源字典来表示不同的皮肤,通过动态加载不同的资源字典来实现换肤的效果;对于简单的换肤操作,如更改背景颜色、设置窗体透明度,这种换肤操作,我们就不能使用上面的方法了,这个时候,我们只要在一个全局对象中添加几个属性,如背景颜色、前景颜色、窗体透明度等,然后,再绑定这几个属性就能达到我们想要的效果。
951 0
|
前端开发 测试技术 C#
WPF MVVM UI分离之《交互与数据分离》
原文:WPF MVVM UI分离之《交互与数据分离》 在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架。 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下面的问题: 删除操作,假如需要先执行一部分数据的处理,然后删除界面列表中的子项,之后再执行其它数据的处理。
1060 0
|
前端开发 测试技术 C#
WPF MVVM UI分离之《交互与数据分离》
在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架。 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下面的问题: 删除操作,假如需要先执行一部分数据的处理,然后删除界面列表中的子项,之后再执行其它数据的处理。
1511 0