使用WPF来创建 Metro UI

当我第一次运行Zune时,我为这些美丽的UI所折服。当时就说这肯定不是用WPF做的,因为这些字体是如此的清晰而且UI反映的也非常快速。。而且我从维基百科上也了解到Zune的第一个版本是2006年发布的,而WPF与.NET 3.0却是 2006 年11月发布的。

 
那么问题来了,如果它不是WPF做的,那它是用什么技术做到的呢?为了找到答案,我使用Process Explorer工具来看看Zune是如何启动的,默认情况下,.NET应用程序都是被用黄色高亮显示的。
使用WPF来创建 Metro UI
 

很好,这说明Zune肯定是.net 应用程序了,然后我们可以看到Zune需要如下库
使用WPF来创建 Metro UI

然后用 Reflector一看:
 
如你所见,根名空间是 Microsoft.Iris. 我在Google上搜到这玩意看上去就像某种原始的WPF组件 -- MCML
 
WPF能创造出类似的UI吗?
第一个难点就是就是设定WindowStyle为None。因为这有这有才能让标题栏以及边框不可见
使用WPF来创建 Metro UI
 
那该如何移动窗体呢?
首先添加一个Shape(Rectangle),然后为它订阅PreviewMouseDown事件处理。
01 // Is this a double-click?
02 if (DateTime.Now.Subtract(m_headerLastClicked) <= s_doubleClick)
03 {
04   // Execute the code inside the event handler for the
05   // restore button click passing null for the sender
06   // and null for the event args.
07   HandleRestoreClick(nullnull);
08 }
09  
10 m_headerLastClicked = DateTime.Now;
11  
12 if (Mouse.LeftButton == MouseButtonState.Pressed)
13 {
14   DragMove();
15 }
该如何任意改变窗体大小?
在主窗体的四个角分别添加一个Shape(比如Rectangle)然后为它们都订阅PreviewMouseDown事件处理:
01 Rectangle clickedRectangle = (Rectangle)sender;
02     
03 switch (clickedRectangle.Name)
04 {
05   case "top":
06       Cursor = Cursors.SizeNS;
07       ResizeWindow(ResizeDirection.Top);
08       break;
09   case "bottom":
10       Cursor = Cursors.SizeNS;
11       ResizeWindow(ResizeDirection.Bottom);
12       break;
13   // ...
14 }
 
下面就是用鼠标重新调整窗体大小的代码
01 /// <summary> /// Resizes the window.
02 /// </summary> /// <param name="direction">The direction.
03 private void ResizeWindow(ResizeDirection direction)
04 {
05   NativeMethods.SendMessage(m_hwndSource.Handle, WM_SYSCOMMAND,
06       (IntPtr)(61440 + direction), IntPtr.Zero);
07 }
08  
09 [DllImport("user32.dll", CharSet = CharSet.Auto)]
10 internal static extern IntPtr SendMessage(
11   IntPtr hWnd,
12   UInt32 msg,
13   IntPtr wParam,
14   IntPtr lParam);
如何为窗体添加阴影效果。
实际上有两种做法:
第一种就是试用DWM API。这个方法需要订阅SourceInitialized事件。
01 /// <summary> /// Raises the <see cref="FrameworkElement.Initialized"> event.
02 /// This method is invoked whenever
03 /// <see cref="P:FrameworkElement.IsInitialized"> /// is set to true internally.
04 /// </see></see></summary> /// <param name="e">The <see cref="T:RoutedEventArgs"> /// that contains the event data.
05 protected override void OnInitialized(EventArgs e)
06 {
07   AllowsTransparency    = false;
08   ResizeMode            = ResizeMode.NoResize;
09   Height                = 480;
10   Width                 = 852;
11   WindowStartupLocation = WindowStartupLocation.CenterScreen;
12   WindowStyle           = WindowStyle.None;
13  
14   SourceInitialized    += HandleSourceInitialized;
15  
16   base.OnInitialized(e);
17 }
18     
19 /// <summary> /// Handles the source initialized.
20 /// </summary> /// <param name="sender">The sender.
21 /// <param name="e">The <see cref="System.EventArgs"> /// instance containing the event data.
22 private void HandleSourceInitialized(Object sender, EventArgs e)
23 {
24   m_hwndSource = (HwndSource)PresentationSource.FromVisual(this);
25  
26   // Returns the HwndSource object for the window
27   // which presents WPF content in a Win32 window.
28   HwndSource.FromHwnd(m_hwndSource.Handle).AddHook(
29       new HwndSourceHook(NativeMethods.WindowProc));
30  
31   // http://msdn.microsoft.com/en-us/library/aa969524(VS.85).aspx
32   Int32 DWMWA_NCRENDERING_POLICY = 2;
33   NativeMethods.DwmSetWindowAttribute(
34       m_hwndSource.Handle,
35       DWMWA_NCRENDERING_POLICY,
36       ref DWMWA_NCRENDERING_POLICY,
37       4);
38  
39   // http://msdn.microsoft.com/en-us/library/aa969512(VS.85).aspx
40   NativeMethods.ShowShadowUnderWindow(m_hwndSource.Handle);
41 }</see></see>
无阴影的窗体
使用WPF来创建 Metro UI
有阴影的窗体
使用WPF来创建 Metro UI 
 
 
第二种方法就是使用四个外部的透明窗体来制造了阴影的假象,如下图所示
使用WPF来创建 Metro UI
 
1,用代码的方式创建一个透明的窗体
2,找到Main Window 在屏幕上的坐标,尤其是左上角
3,计算4个透明窗口的坐标
4,当我们移动Main Window时,4个边框透明窗口也需要跟着移动
5,当我们重新设定 Main Window大小时,4个边框透明窗口也要跟着变化大小。
 
说这么多看上去好像很难,来让我们看看实现的代码吧。
 
创建透明窗体的代码
01 /// <summary> /// Initializes the surrounding windows.
02 /// </summary> private void InitializeSurrounds()
03 {
04   // Top.
05   m_wndT = CreateTransparentWindow();
06  
07   // Left.
08   m_wndL = CreateTransparentWindow();
09  
10   // Bottom.
11   m_wndB = CreateTransparentWindow();
12  
13   // Right.
14   m_wndR = CreateTransparentWindow();
15  
16   SetSurroundShadows();
17 }
18     
19 /// <summary> /// Creates an empty window.
20 /// </summary> /// <returns></returns> private static Window CreateTransparentWindow()
21 {
22   Window wnd             = new Window();
23   wnd.AllowsTransparency = true;
24   wnd.ShowInTaskbar      = false;
25   wnd.WindowStyle        = WindowStyle.None;
26   wnd.Background         = null;
27  
28   return wnd;
29 }
30  
31 /// <summary> /// Sets the artificial drop shadow.
32 /// </summary> /// <param name="active">if set to <c>true</c> [active].
33 private void SetSurroundShadows(Boolean active = true)
34 {
35   if (active)
36   {
37       Double cornerRadius = 1.75;
38  
39       m_wndT.Content = GetDecorator(
40           "Images/ACTIVESHADOWTOP.PNG");
41       m_wndL.Content = GetDecorator(
42           "Images/ACTIVESHADOWLEFT.PNG", cornerRadius);
43       m_wndB.Content = GetDecorator(
44           "Images/ACTIVESHADOWBOTTOM.PNG");
45       m_wndR.Content = GetDecorator(
46           "Images/ACTIVESHADOWRIGHT.PNG", cornerRadius);
47   }
48   else
49   {
50       m_wndT.Content = GetDecorator(
51           "Images/INACTIVESHADOWTOP.PNG");
52       m_wndL.Content = GetDecorator(
53           "Images/INACTIVESHADOWLEFT.PNG");
54       m_wndB.Content = GetDecorator(
55           "Images/INACTIVESHADOWBOTTOM.PNG");
56       m_wndR.Content = GetDecorator(
57           "Images/INACTIVESHADOWRIGHT.PNG");
58   }
59 }
60  
61 [DebuggerStepThrough]
62 private Decorator GetDecorator(String imageUri, Double radius = 0)
63 {
64   Border border       = new Border();
65   border.CornerRadius = new CornerRadius(radius);
66   border.Background   = new ImageBrush(
67       new BitmapImage(
68           new Uri(BaseUriHelper.GetBaseUri(this),
69               imageUri)));
70  
71   return border;
72 }
 
计算位置高度的代码  
01 /// <summary> /// Raises the <see cref="FrameworkElement.Initialized"> event.
02 /// This method is invoked whenever
03 /// <see cref="FrameworkElement.IsInitialized"> /// is set to true internally.
04 /// </see></see></summary> /// <param name="e">The <see cref="T:RoutedEventArgs"> /// that contains the event data.
05 protected override void OnInitialized(EventArgs e)
06 {
07   // ...
08  
09   LocationChanged += HandleLocationChanged;
10   SizeChanged     += HandleLocationChanged;
11   StateChanged    += HandleWndStateChanged;
12  
13   InitializeSurrounds();
14   ShowSurrounds();
15  
16   base.OnInitialized(e);
17 }
18     
19 /// <summary> /// Handles the location changed.
20 /// </summary> /// <param name="sender">The sender.
21 /// <param name="e">The <see cref="System.EventArgs"> /// instance containing the event data.
22 private void HandleLocationChanged(Object sender, EventArgs e)
23 {
24   m_wndT.Left   = Left  - c_edgeWndSize;
25   m_wndT.Top    = Top   - m_wndT.Height;
26   m_wndT.Width  = Width + c_edgeWndSize * 2;
27   m_wndT.Height = c_edgeWndSize;
28  
29   m_wndL.Left   = Left - m_wndL.Width;
30   m_wndL.Top    = Top;
31   m_wndL.Width  = c_edgeWndSize;
32   m_wndL.Height = Height;
33  
34   m_wndB.Left   = Left  - c_edgeWndSize;
35   m_wndB.Top    = Top   + Height;
36   m_wndB.Width  = Width + c_edgeWndSize * 2;
37   m_wndB.Height = c_edgeWndSize;
38  
39   m_wndR.Left   = Left + Width;
40   m_wndR.Top    = Top;
41   m_wndR.Width  = c_edgeWndSize;
42   m_wndR.Height = Height;
43 }
44     
45 /// <summary> /// Handles the windows state changed.
46 /// </summary> /// <param name="sender">The sender.
47 /// <param name="e">The <see cref="System.EventArgs"> /// instance containing the event data.
48 private void HandleWndStateChanged(Object sender, EventArgs e)
49 {
50   if (WindowState == WindowState.Normal)
51   {
52       ShowSurrounds();
53   }
54   else
55   {
56       HideSurrounds();
57   }
58 }</see></see></see>

原文链接 OSChina.NET原创翻译
原文地址:https://www.cnblogs.com/gc2013/p/3682687.html