使用WPF來創建 Metro UI

openkk 12年前發布 | 65K 次閱讀 Metro .NET開發

使用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事件處理。
// 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();
}


該如何任意改變窗體大小?
在主窗體的四個角分別添加一個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;
  // ...
}


下面就是用鼠標重新調整窗體大小的代碼
///  /// Resizes the window.
///  /// The direction.
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);


如何為窗體添加陰影效果。
實際上有兩種做法:
第一種就是試用DWM API。這個方法需要訂閱SourceInitialized事件。
///  /// Raises the  event.
/// This method is invoked whenever
///  /// is set to true internally.
///  /// The  /// that contains the event data.
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);
}

///  /// Handles the source initialized.
///  /// The sender.
/// The  /// instance containing the event data.
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);
}

無陰影的窗體
使用WPF來創建 Metro UI
有陰影的窗體
使用WPF來創建 Metro UI


第二種方法就是使用四個外部的透明窗體來制造了陰影的假象,如下圖所示
使用WPF來創建 Metro UI

1,用代碼的方式創建一個透明的窗體
2,找到Main Window 在屏幕上的坐標,尤其是左上角
3,計算4個透明窗口的坐標
4,當我們移動Main Window時,4個邊框透明窗口也需要跟著移動
5,當我們重新設定 Main Window大小時,4個邊框透明窗口也要跟著變化大小。

說這么多看上去好像很難,來讓我們看看實現的代碼吧。

創建透明窗體的代碼
///  /// Initializes the surrounding windows.
///  private void InitializeSurrounds()
{
  // Top.
  m_wndT = CreateTransparentWindow();

  // Left.
  m_wndL = CreateTransparentWindow();

  // Bottom.
  m_wndB = CreateTransparentWindow();

  // Right.
  m_wndR = CreateTransparentWindow();

  SetSurroundShadows();
}

///  /// Creates an empty window.
///  ///  private static Window CreateTransparentWindow()
{
  Window wnd             = new Window();
  wnd.AllowsTransparency = true;
  wnd.ShowInTaskbar      = false;
  wnd.WindowStyle        = WindowStyle.None;
  wnd.Background         = null;

  return wnd;
}

///  /// Sets the artificial drop shadow.
///  /// if set to true [active].
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;
}


計算位置高度的代碼 
///  /// Raises the  event.
/// This method is invoked whenever
///  /// is set to true internally.
///  /// The  /// that contains the event data.
protected override void OnInitialized(EventArgs e)
{
  // ...

  LocationChanged += HandleLocationChanged;
  SizeChanged     += HandleLocationChanged;
  StateChanged    += HandleWndStateChanged;

  InitializeSurrounds();
  ShowSurrounds();

  base.OnInitialized(e);
}

///  /// Handles the location changed.
///  /// The sender.
/// The  /// instance containing the event data.
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;
}

///  /// Handles the windows state changed.
///  /// The sender.
/// The  /// instance containing the event data.
private void HandleWndStateChanged(Object sender, EventArgs e)
{
  if (WindowState == WindowState.Normal)
  {
      ShowSurrounds();
  }
  else
  {
      HideSurrounds();
  }
}


原文鏈接 , OSChina.NET原創翻譯
 本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!