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

原文: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>

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

源码下载

源码下载

 

 

 

时间: 2024-11-02 01:10:28

WPF换肤之四:界面设计和代码设计分离的相关文章

WPF换肤设计原理浅析_C#教程

WPF换肤的设计原理,利用资源字典为每种皮肤资源添加不同的样式,在后台切换皮肤资源文件. 截图 上图中,第一张图采用规则样式,第二张图采用不规则样式,截图的时候略有瑕疵. 资源字典 规则样式资源Skin.RegularStyle.xaml <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microso

WPF换肤之一:创建圆角窗体

原文:WPF换肤之一:创建圆角窗体     我们都期望自己的软件能够有一套看上去很吸引人眼球的外衣,使得别人看上去既专业又有美感.这个系列就带领着大家一步一步的讲解如何设计出一套自己的WPF的窗体皮肤,如果文中有任何错误或者不足,还请指出.     WPF是微软大战略中的一个重心所在,学习WPF可谓是一举多得:首先,学习WPF可以让你了解SilverLight的80%:其次,XAML语言可以让你快速的入手WCF和WF:更甚者,就是WPF给予DX渲染核心,抛弃了传统WINFORM以GDI+为主的渲

WPF换肤之八:创建3D浏览效果

原文:WPF换肤之八:创建3D浏览效果 上节中,我们展示了WPF中的异步以及界面线程交互的方式,使得应用程序的显示更加的流畅.这节我们主要讲解如何设计一个具有3D浏览效果的天气信息浏览器. 效果显示 下面我们看截图: 是不是能够感受到一种与众不同的感觉.如果你能够感受到它的与众不同,这也是我本节所要达到的目标. 实现方式 上面的只是一个简单的3D图形,它的产生需要依赖于WPF中的MeshGeometry3D对象,这个对象按照微软官方的解释就是用于生成3D形状的三角形基元,它有三个比较重要的属性:

WPF换肤之二:可拉动的窗体

原文:WPF换肤之二:可拉动的窗体 让我们接着上一章: WPF换肤之一:创建圆角窗体 来继续. 在这一章,我主要是实现对圆角窗体的拖动,改变大小功能. 拖动自绘窗体的步骤 首先,通过上节的设计,我们知道了如何设计一个圆角窗体,通过XAML代码量,我们发现设置这个窗体是多么的简单.但是如何让窗体能够进行Resize呢? 在Winform时代,我们通过WndProc(ref Message m)处理窗体界面消息来实现,那么在WPF中是否也是如此呢? 其实在WPF中,虽说封装比较紧密,但是对于处理界面

WPF换肤之六:酷炫的时区浏览小精灵

原文:WPF换肤之六:酷炫的时区浏览小精灵 由于工作需要,经常要查看到不同地区的 当前时间,以前总是对照着时区表来进行加减运算,现在有了这个小工具以后,感觉省心了不少.下面是软件的截图: 效果图赏析   在界面上,有能够冉冉升起的太阳或者月亮,有缓慢飘动的浮云,有青葱翠绿的花叶, 当然,也有显目的时区显示.如果要是放在WinForm时代,要实现这样的界面,真的是繁琐和复杂,但是在WPF中,利用XAML控制前台界面,利用CodeBehind控制窗口拖动,日月变换等等逻辑,真的是简便而且效果强大.其

WPF换肤之七:异步

原文:WPF换肤之七:异步 在WinForm时代,相信大家都遇到过这种情形,如果在程序设计过程中遇到了耗时的操作,不使用异步会导致程序假死.当然,在WPF中,这种情况也是存在的,所以我们就需要寻找一种解决方法来让程序界面响应和耗时操作异步进行,那么上述假死的情况就不会发生了. 这一节就着重讲解异步以及线程和界面交互. 异步使用方式(APM模式) 在上节中,我们给一个普通的Window窗口做了换肤处理,呈现出了一个非常酷的时区浏览小工具.当然,这一节,我们还是以那个工具为主,为其增加天气预报功能,

仿百度换肤功能的简单实例代码_javascript技巧

效果:(换肤出来一个div,选择你想要的图片,作为网页背景,保存) 要点:cookie保存状态 html代码: <body> <div id="header"> <div id="header_con"> <div class="dbg"><a href="javascript:;" onclick="showImgBox()">换肤</a&

ActiveSkin 4.3软件换肤在VC中的实现

ActiveSkin是一款给软件更换皮肤的ActiveX控件.它很还好的将软件界面设计工作从繁琐程序代码编写中解放出来,使得功能设计者可以专心于功能代码的实现,而把软件界面交给美工人员处理.提高了界面设计的工作效率,是一种很好的软件设计思想. 下面通过三个示例来介绍他的一般使用. 示例一:标准型皮肤SkinForm的对话框工程 在VC环境下建立一个MFC基本对话框工程.在对话框的资源文件里Dlg.rc设计对话框界面中,删去已有的按钮Buttons和标签Lables,并插入ActiveSkin的A

vs2008-VS2008MFC建立对话框,利用Skin++换肤不同步

问题描述 VS2008MFC建立对话框,利用Skin++换肤不同步 利用Skin++对对话框进行动态换肤,界面大部分都改变,但是按钮等其他控件没有改变,只有当鼠标光标移动到按钮控件上面时,按钮控件才能发生对应皮肤的改变 解决方案 重画窗口中所有控件,首先获取所有按钮句柄,然后调用: InvalidateRgn(hwnd,NULL,TRUE);//按钮窗口DC立即失效 UpdateWindow(hwnd);//立即响应WM_PAINT重画窗口 解决方案二: Skin++,没有用过 是不是对应按键没