1. 程式人生 > >使用WPF來建立 Metro UI程式

使用WPF來建立 Metro UI程式

原文:使用WPF來建立 Metro UI程式

這個是我以前網上看到的一篇文章,原文地址是:Building a Metro UI with WPF,這篇文章一步步的介紹瞭如何實現一個Metro樣式的視窗,並且效果非常好。今天看到OSChina上有這篇文章的譯文:使用WPF來建立 Metro UI,翻譯得非常好,這裡推薦一下。

 值得一提的是,除了這種一步步來的方式外,現在Codeplex中也有不少比較好的metro風格的wpf框架,可以幫助我們快速實現Metro樣式的Wpf程式。

 

親們用過 Zune 嘛? 應該吧,可是 4.7.1404.0 版本才是我的第一次Zune體驗,因為這個版本有非常顯著的變化: 支援Windows Phone 7 並 整合了 Windows Live Essentials 2011.

當我第一次執行它的時候,我被他的介面深深地震撼到了,“不!這一定不是WPF!一定不是!”。 再細細的看下去, 文字是那麼的清晰可見, 介面響應是如此的酣暢淋漓! 我順便去看了一下維基百科, 原來Zune的早期版本早在2006年就已經發布。與此同時,WPF 正要隨著 .NET 3.0 一起釋出 (釋出日期是2006年11月)。

roger37 roger37 翻譯於 2012/12/26 17:47  頂
2      


當我第一次執行Zune時,我為這些美麗的UI所折服。當時就說這肯定不是用WPF做的,因為這些字型是如此的清晰而且UI反映的也非常快速。。而且我從維基百科上也瞭解到Zune的第一個版本是2006年釋出的,而WPF與.NET 3.0卻是 2006 年11月釋出的。

  那麼問題來了,如果它不是WPF做的,那它是用什麼技術做到的呢?為了找到答案,我使用Process Explorer工具來看看Zune是如何啟動的,預設情況下,.NET應用程式都是被用黃色高亮顯示的。  

很好,這說明Zune肯定是.net 應用程式了,然後我們可以看到Zune需要如下庫

然後用 Reflector一看: https://github.com/moodmosaic/moodmosaic.github.com/raw/master/images/BuildingaMetroUIwithWPFimage5.png   如你所見,根名空間是 Microsoft.Iris. 我在Google上搜到這玩意看上去就像某種原始的WPF元件 -- MCML     王振威 王振威 翻譯於 2012/11/27 17:12  頂 2       WPF能創造出類似的UI嗎? 第一個難點就是就是設定WindowStyle為None。因為這有這有才能讓標題欄以及邊框不可見   王振威 王振威 翻譯於 2012/11/27 17:13  頂 2       那該如何移動窗體呢? 首先新增一個Shape(Rectangle),然後為它訂閱PreviewMouseDown事件處理。
// Is this a double-click?
if (DateTime.Now.Subtract(m_headerLastClicked) <= s_doubleClick)
{
  // Execute the code inside the event handler for the
  // restore button click passing null for the sender
  // and null for the event args.
  HandleRestoreClick(null, null); } m_headerLastClicked = DateTime.Now; if (Mouse.LeftButton == MouseButtonState.Pressed) { DragMove(); }

 

王振威 王振威 翻譯於 2012/11/27 17:12  頂 2       該如何任意改變窗體大小? 在主窗體的四個角分別新增一個Shape(比如Rectangle)然後為它們都訂閱PreviewMouseDown事件處理:
Rectangle clickedRectangle = (Rectangle)sender;
   
switch (clickedRectangle.Name)
{
  case "top":
      Cursor = Cursors.SizeNS;
      ResizeWindow(ResizeDirection.Top);
      break;
  case "bottom": Cursor = Cursors.SizeNS; ResizeWindow(ResizeDirection.Bottom); break; // ... }

 

  下面就是用滑鼠重新調整窗體大小的程式碼
/// <summary>
/// Resizes the window.
/// </summary> /// <param name="direction">The direction.</param> private void ResizeWindow(ResizeDirection direction) { NativeMethods.SendMessage(m_hwndSource.Handle, WM_SYSCOMMAND, (IntPtr)(61440 + direction), IntPtr.Zero); } [DllImport("user32.dll", CharSet = CharSet.Auto)] internal static extern IntPtr SendMessage( IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam);

 

王振威 王振威 翻譯於 2012/11/27 17:13  頂 2       如何為窗體新增陰影效果。 實際上有兩種做法: 第一種就是試用DWM API。這個方法需要訂閱SourceInitialized事件。
/// <summary>
/// Raises the <see cref="FrameworkElement.Initialized"/> event. /// This method is invoked whenever /// <see cref="P:FrameworkElement.IsInitialized"/> /// is set to true internally. /// </summary> /// <param name="e">The <see cref="T:RoutedEventArgs"/> /// that contains the event data.</param> protected override void OnInitialized(EventArgs e) { AllowsTransparency = false; ResizeMode = ResizeMode.NoResize; Height = 480; Width = 852; WindowStartupLocation = WindowStartupLocation.CenterScreen; WindowStyle = WindowStyle.None; SourceInitialized += HandleSourceInitialized; base.OnInitialized(e); } /// <summary> /// Handles the source initialized. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.EventArgs"/> /// instance containing the event data.</param> private void HandleSourceInitialized(Object sender, EventArgs e) { m_hwndSource = (HwndSource)PresentationSource.FromVisual(this); // Returns the HwndSource object for the window // which presents WPF content in a Win32 window. HwndSource.FromHwnd(m_hwndSource.Handle).AddHook( new HwndSourceHook(NativeMethods.WindowProc)); // http://msdn.microsoft.com/en-us/library/aa969524(VS.85).aspx Int32 DWMWA_NCRENDERING_POLICY = 2; NativeMethods.DwmSetWindowAttribute( m_hwndSource.Handle, DWMWA_NCRENDERING_POLICY, ref DWMWA_NCRENDERING_POLICY, 4); // http://msdn.microsoft.com/en-us/library/aa969512(VS.85).aspx NativeMethods.ShowShadowUnderWindow(m_hwndSource.Handle); }

 

無陰影的窗體 有陰影的窗體     王振威 王振威 翻譯於 2012/11/27 17:17  頂 2         第二種方法就是使用四個外部的透明窗體來製造了陰影的假象,如下圖所示   1,用程式碼的方式建立一個透明的窗體 2,找到Main Window 在螢幕上的座標,尤其是左上角 3,計算4個透明視窗的座標 4,當我們移動Main Window時,4個邊框透明視窗也需要跟著移動 5,當我們重新設定 Main Window大小時,4個邊框透明視窗也要跟著變化大小。   說這麼多看上去好像很難,來讓我們看看實現的程式碼吧。   建立透明窗體的程式碼
/// <summary>
/// Initializes the surrounding windows.
/// </summary> private void InitializeSurrounds() { // Top. m_wndT = CreateTransparentWindow(); // Left. m_wndL = CreateTransparentWindow(); // Bottom. m_wndB = CreateTransparentWindow(); // Right. m_wndR = CreateTransparentWindow(); SetSurroundShadows(); } /// <summary> /// Creates an empty window. /// </summary> /// <returns></returns> private static Window CreateTransparentWindow() { Window wnd = new Window(); wnd.AllowsTransparency = true; wnd.ShowInTaskbar = false; wnd.WindowStyle = WindowStyle.None; wnd.Background = null; return wnd; } /// <summary> /// Sets the artificial drop shadow. /// </summary> /// <param name="active">if set to <c>true</c> [active].</param> private void SetSurroundShadows(Boolean active = true) { if (active) { Double cornerRadius = 1.75; m_wndT.Content = GetDecorator( "Images/ACTIVESHADOWTOP.PNG"); m_wndL.Content = GetDecorator( "Images/ACTIVESHADOWLEFT.PNG", cornerRadius); m_wndB.Content = GetDecorator( "Images/ACTIVESHADOWBOTTOM.PNG"); m_wndR.Content = GetDecorator( "Images/ACTIVESHADOWRIGHT.PNG", cornerRadius); } else { m_wndT.Content = GetDecorator( "Images/INACTIVESHADOWTOP.PNG"); m_wndL.Content = GetDecorator( "Images/INACTIVESHADOWLEFT.PNG"); m_wndB.Content = GetDecorator( "Images/INACTIVESHADOWBOTTOM.PNG"); m_wndR.Content = GetDecorator( "Images/INACTIVESHADOWRIGHT.PNG"); } } [DebuggerStepThrough] private Decorator GetDecorator(String imageUri, Double radius = 0) { Border border = new Border(); border.CornerRadius = new CornerRadius(radius); border.Background = new ImageBrush( new BitmapImage( new Uri(BaseUriHelper.GetBaseUri(this), imageUri))); return border; }

 

  計算位置高度的程式碼  
/// <summary>
/// Raises the <see cref="FrameworkElement.Initialized"/> event. /// This method is invoked whenever /// <see cref="FrameworkElement.IsInitialized"/> /// is set to true internally. /// </summary> /// <param name="e">The <see cref="T:RoutedEventArgs"/> /// that contains the event data.</param> protected override void OnInitialized(EventArgs e) { // ... LocationChanged += HandleLocationChanged; SizeChanged += HandleLocationChanged; StateChanged += HandleWndStateChanged; InitializeSurrounds(); ShowSurrounds(); base.OnInitialized(e); } /// <summary> /// Handles the location changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.EventArgs"/> /// instance containing the event data.</param> private void HandleLocationChanged(Object sender, EventArgs e) { m_wndT.Left = Left - c_edgeWndSize; m_wndT.Top = Top - m_wndT.Height; m_wndT.Width = Width + c_edgeWndSize * 2; m_wndT.Height = c_edgeWndSize; m_wndL.Left = Left - m_wndL.Width; m_wndL.Top = Top; m_wndL.Width = c_edgeWndSize; m_wndL.Height = Height; m_wndB.Left = Left - c_edgeWndSize; m_wndB.Top = Top + Height; m_wndB.Width = Width + c_edgeWndSize * 2; m_wndB.Height = c_edgeWndSize; m_wndR.Left = Left + Width; m_wndR.Top = Top; m_wndR.Width = c_edgeWndSize; m_wndR.Height = Height; } /// <summary> /// Handles the windows state changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.EventArgs"/> /// instance containing the event data.</param> private void HandleWndStateChanged(Object sender, EventArgs e) { if (WindowState == WindowState.Normal) { ShowSurrounds(); } else { HideSurrounds(); } }