<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [WPF 自定義控件]使用WindowChrome的問(wèn)題

      1. 前言

      上一篇文章介紹了使用WindowChrome自定義Window,實(shí)際使用下來(lái)總有各種各樣的問(wèn)題,這些問(wèn)題大部分都不影響使用,可能正是因?yàn)椴挥绊懯褂盟砸恢睕](méi)得到修復(fù)(也有可能別人根本不覺(jué)得這些是問(wèn)題)。

      這篇文章我總結(jié)了一些實(shí)際遇到的問(wèn)題及其解決方案。

      2. WindowChrome最大化的問(wèn)題

      2.1 影響Chrome尺寸的幾個(gè)值

      上一篇文章提到有幾個(gè)值用于計(jì)算Chrome的尺寸:

      屬性 值(像素) 描述
      SM_CXFRAME/SM_CYFRAME 4 The thickness of the sizing border around the perimeter of a window that can be resized, in pixels. SM_CXSIZEFRAME is the width of the horizontal border, and SM_CYSIZEFRAME is the height of the vertical border.This value is the same as SM_CXFRAME.
      SM_CXPADDEDBORDER 4 The amount of border padding for captioned windows, in pixels.Windows XP/2000: This value is not supported.
      SM_CYCAPTION 23 The height of a caption area, in pixels.

      在有標(biāo)題的標(biāo)準(zhǔn)Window,chrome的頂部尺寸為SM_CYFRAME + SM_CXPADDEDBORDER + SM_CYCAPTION = 31,左右兩邊尺寸為SM_CXFRAME + SM_CXPADDEDBORDER = 8,底部尺寸為SM_CYFRAME + SM_CXPADDEDBORDER = 8。

      具體的計(jì)算方式可以參考Firefox的源碼

        // mCaptionHeight is the default size of the NC area at
        // the top of the window. If the window has a caption,
        // the size is calculated as the sum of:
        //      SM_CYFRAME        - The thickness of the sizing border
        //                          around a resizable window
        //      SM_CXPADDEDBORDER - The amount of border padding
        //                          for captioned windows
        //      SM_CYCAPTION      - The height of the caption area
        //
        // If the window does not have a caption, mCaptionHeight will be equal to
        // `GetSystemMetrics(SM_CYFRAME)`
        mCaptionHeight = GetSystemMetrics(SM_CYFRAME) +
                         (hasCaption ? GetSystemMetrics(SM_CYCAPTION) +
                                           GetSystemMetrics(SM_CXPADDEDBORDER)
                                     : 0);
      
        // mHorResizeMargin is the size of the default NC areas on the
        // left and right sides of our window.  It is calculated as
        // the sum of:
        //      SM_CXFRAME        - The thickness of the sizing border
        //      SM_CXPADDEDBORDER - The amount of border padding
        //                          for captioned windows
        //
        // If the window does not have a caption, mHorResizeMargin will be equal to
        // `GetSystemMetrics(SM_CXFRAME)`
        mHorResizeMargin = GetSystemMetrics(SM_CXFRAME) +
                           (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
      
        // mVertResizeMargin is the size of the default NC area at the
        // bottom of the window. It is calculated as the sum of:
        //      SM_CYFRAME        - The thickness of the sizing border
        //      SM_CXPADDEDBORDER - The amount of border padding
        //                          for captioned windows.
        //
        // If the window does not have a caption, mVertResizeMargin will be equal to
        // `GetSystemMetrics(SM_CYFRAME)`
        mVertResizeMargin = GetSystemMetrics(SM_CYFRAME) +
      (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
      

      在WPF中這幾個(gè)值分別映射到SystemParameters的相關(guān)屬性:

      系統(tǒng)值 SystemParameters屬性
      SM_CXFRAME/SM_CYFRAME WindowResizeBorderThickness 4,4,4,4
      SM_CXPADDEDBORDER 無(wú) 4
      SM_CYCAPTION WindowCaptionHeight 23

      另外還有WindowNonClientFrameThickness,相當(dāng)于WindowResizeBorderThickness的基礎(chǔ)上,Top+=WindowCaptionHeight,值為 4,27,4,4。

      SM_CXPADDEDBORDER在WPF里沒(méi)有對(duì)應(yīng)的值,我寫(xiě)了個(gè)WindowParameters的類(lèi),添加了這個(gè)屬性:

      /// <summary>
      /// returns the border thickness padding around captioned windows,in pixels. Windows XP/2000:  This value is not supported.
      /// </summary>
      public static Thickness PaddedBorderThickness
      {
          [SecurityCritical]
          get
          {
              if (_paddedBorderThickness == null)
              {
                  var paddedBorder = NativeMethods.GetSystemMetrics(SM.CXPADDEDBORDER);
                  var dpi = GetDpi();
                  Size frameSize = new Size(paddedBorder, paddedBorder);
                  Size frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, dpi / 96.0, dpi / 96.0);
                  _paddedBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);
              }
      
              return _paddedBorderThickness.Value;
          }
      }
      

      2.2 WindowChrome的實(shí)際大小和普通Window不同

      先說(shuō)說(shuō)我的環(huán)境,WIndows 10,1920 * 1080 分辨率,100% DPI。

      <WindowChrome.WindowChrome>
          <WindowChrome />
      </WindowChrome.WindowChrome>
      <Window.Style>
          <Style TargetType="{x:Type Window}">
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="{x:Type Window}">
                          <Border>
                              <Grid>
                                  <AdornerDecorator>
                                      <ContentPresenter />
                                  </AdornerDecorator>
                                  <ResizeGrip x:Name="WindowResizeGrip"
                                              HorizontalAlignment="Right"
                                              IsTabStop="false"
                                              Visibility="Collapsed"
                                              VerticalAlignment="Bottom" />
                              </Grid>
                          </Border>
                          <ControlTemplate.Triggers>
                              <MultiTrigger>
                                  <MultiTrigger.Conditions>
                                      <Condition Property="ResizeMode"
                                                 Value="CanResizeWithGrip" />
                                      <Condition Property="WindowState"
                                                 Value="Normal" />
                                  </MultiTrigger.Conditions>
                                  <Setter Property="Visibility"
                                          TargetName="WindowResizeGrip"
                                          Value="Visible" />
                              </MultiTrigger>
                          </ControlTemplate.Triggers>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>
      </Window.Style>
      

      按上一篇文章介紹的方法打開(kāi)一個(gè)使用WindowChrome的Window(大小為800 * 600),在VisualStudio的實(shí)時(shí)可視化樹(shù)可以看到AdornerDecorator的實(shí)際大小和Window的實(shí)際大小都是800 * 600(畢竟邊WindowChrome里的Border、Grid等都沒(méi)設(shè)Margin或Padding)。然后用Inspect觀察它的邊框??梢钥吹絎indow實(shí)際上的范圍沒(méi)什么問(wèn)題。但和標(biāo)準(zhǔn)Window的對(duì)比就可以看出有區(qū)別,我在之前的文章中介紹過(guò)標(biāo)準(zhǔn)Window的實(shí)際范圍和用戶看到的并不一樣。

      上面兩張圖分別是通過(guò)Inspect觀察的標(biāo)準(zhǔn)Window(上圖)和使用WindowChrome的Window(下圖),可以看到標(biāo)準(zhǔn)Window左右下三個(gè)方向有些空白位置,和邊框加起來(lái)是8個(gè)像素。WindowChrome則沒(méi)有這個(gè)問(wèn)題。

      2.3 最大化狀態(tài)下Margin和標(biāo)題高度的問(wèn)題

      WindowChrome最大化時(shí)狀態(tài)如上圖所示,大小也變?yōu)?936 * 1066,這個(gè)大小沒(méi)問(wèn)題,有問(wèn)題的是它不會(huì)計(jì)算好client-area的尺寸,只是簡(jiǎn)單地加大non-client的尺寸,導(dǎo)致client-area的尺寸也成了1936 * 1066。標(biāo)準(zhǔn)Window在最大化時(shí)non-client area的尺寸為1936 * 1066,client-area的尺寸為1920 * 1027。

      2.4 最大化時(shí)chrome尺寸的問(wèn)題

      結(jié)合Window(窗體)的UI元素及行為這篇文章,WindowChrome最大化時(shí)的client-area的尺寸就是Window尺寸(1936 * 1066)減去WindowNonClientFrameThickness(4,27,4,4)再減去PaddedBorderThickness(4,4,4,4)。這樣就準(zhǔn)確地計(jì)算出client-area在最大化狀態(tài)下的尺寸為1920 * 1027。

      在自定義Window的ControlTempalte中我使用Trigger在最大化狀態(tài)下將邊框改為0,然后加上WindowResizeBorderThickness的Padding和PaddedBorderThickness的Margin:

      <Trigger Property="WindowState"
               Value="Maximized">
          <Setter TargetName="MaximizeButton"
                  Property="Visibility"
                  Value="Collapsed" />
          <Setter TargetName="RestoreButton"
                  Property="Visibility"
                  Value="Visible" />
          <Setter TargetName="WindowBorder"
                  Property="BorderThickness"
                  Value="0" />
          <Setter TargetName="WindowBorder"
                  Property="Padding"
                  Value="{x:Static SystemParameters.WindowResizeBorderThickness}" />
          <Setter Property="Margin"
                  TargetName="LayoutRoot"
                  Value="{x:Static local:WindowParameters.PaddedBorderThickness}" />
      </Trigger>
      

      以前我還試過(guò)讓BorderThickness保持為1,Margin改為7,但后來(lái)發(fā)現(xiàn)運(yùn)行在高于100% DPI的環(huán)境下出了問(wèn)題,所以改為綁定到屬性。

      在不同DPI下這幾個(gè)屬性值如下:

      DPI non-client area 尺寸 client area 尺寸 WindowNonClientFrameThickness PaddedBorderThickness
      100 1936 * 1066 1920 * 1027 4,4,4,4 4,4,4,4
      125 1550.4 1536 3.2,3.2,3.2,3.2 4,4,4,4
      150 1294.66666666667 280 3.3333,3.3333,3.3333,3.3333 4,4,4,4
      175 1110.85714285714 1097.14285714286 2.8571428,2.8571428,2.8571428,2.8571428 4,4,4,4
      200 973 960 2.5,2.5,2.5,2.5 4,4,4,4

      可以看到PaddedBorderThickness總是等于4,所以也可以使用不綁定PaddedBorderThickness的方案:

      <Border x:Name="WindowBorder"
              BorderThickness="3"
              BorderBrush="{TemplateBinding BorderBrush}"
              Background="{TemplateBinding Background}"
              >
          <Border.Style>
              <Style TargetType="{x:Type Border}">
                  <Style.Triggers>
                      <DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource TemplatedParent}}" Value="Maximized">
                          <Setter Property="Margin" Value="{x:Static SystemParameters.WindowResizeBorderThickness}"/>
                          <Setter Property="Padding" Value="1"/>
                      </DataTrigger>
                  </Style.Triggers>
              </Style>
          </Border.Style>
      

      但我還是更喜歡PaddedBorderThickness,這是心情上的問(wèn)題(我都寫(xiě)了這么多代碼了,你告訴我直接用4這個(gè)神奇的數(shù)字就好了,我斷然不能接受)。而且有可能將來(lái)Windows的窗體設(shè)計(jì)會(huì)改變,綁定系統(tǒng)的屬性比較保險(xiǎn)。

      最后,其實(shí)應(yīng)該監(jiān)視SystemParameters的StaticPropertyChanged事件然后修改PaddedBorderThickness,因?yàn)閃indowNonClientFrameThickness和WindowResizeBorderThickness會(huì)在系統(tǒng)主題改變時(shí)改變,但不想為了這小概率事件多寫(xiě)代碼就偷懶了。

      3. SizeToContent的問(wèn)題

      SizeToContent屬性用于指示W(wǎng)indow是否自動(dòng)調(diào)整它的大小,但當(dāng)設(shè)置'SizeToContent="WidthAndHeight"'時(shí)就會(huì)出問(wèn)題:

      上圖左面時(shí)一個(gè)沒(méi)內(nèi)容的自定義Window,右邊是一個(gè)沒(méi)內(nèi)容的系統(tǒng)Window,兩個(gè)都設(shè)置了SizeToContent="WidthAndHeight"??梢钥吹阶远xWindowChorme多出了一些黑色的區(qū)域,仔細(xì)觀察這些黑色區(qū)域,發(fā)覺(jué)它的尺寸大概就是non-client area的尺寸,而且內(nèi)容就是WindowChrome原本的內(nèi)容。

      SizeToContent="WidthAndHeight"時(shí)Window需要計(jì)算ClientArea的尺寸然后再確定Window的尺寸,但使用WindowChrome自定義Window時(shí)程序以為整個(gè)ControlTempalte的內(nèi)容都是ClientArea,把它當(dāng)作了ClientArea的尺寸,再加上non-client的尺寸就得出了錯(cuò)誤的Window尺寸。ControleTemplate的內(nèi)容沒(méi)辦法遮住整個(gè)WindowChrome的內(nèi)容,于是就出現(xiàn)了這些黑色的區(qū)域。

      解決方案是在OnSourceInitialized時(shí)簡(jiǎn)單粗暴地要求再計(jì)算一次尺寸:

      protected override void OnSourceInitialized(EventArgs e)
      {
          base.OnSourceInitialized(e);
          if (SizeToContent == SizeToContent.WidthAndHeight && WindowChrome.GetWindowChrome(this) != null)
          {
              InvalidateMeasure();
          }
      }
      

      以前我曾建議在OnContentRendered中執(zhí)行這段代碼,但后來(lái)發(fā)現(xiàn)調(diào)試模式,或者性能比較差的場(chǎng)合會(huì)有些問(wèn)題,所以改為在OnSourceInitialized中執(zhí)行了。

      4. FlashWindow的問(wèn)題

      如果一個(gè)Window設(shè)置了Owner并且以ShowDialog的方式打開(kāi),點(diǎn)擊它的Owner將對(duì)這個(gè)Window調(diào)用FlashWindowEx功能,即閃爍幾下,并且還有提示音。除了這種方式還可以用編程的方式調(diào)用FlashWindow功能。

      WindowChrome提供通知FlashWindow發(fā)生的事件,F(xiàn)lashWindow發(fā)生時(shí)雖然Window看上去在Active/Inactive 狀態(tài)間切換,但I(xiàn)sActive屬性并不會(huì)改變。

      要處理這個(gè)問(wèn)題,可以監(jiān)聽(tīng)WM_NCACTIVATE消息,它通知Window的non-client area是否需要切換Active/Inactive狀態(tài)。

      IntPtr handle = new WindowInteropHelper(this).Handle;
      HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WndProc));
      
      
      protected override void OnActivated(EventArgs e)
      {
          base.OnActivated(e);
          SetValue(IsNonClientActivePropertyKey, true);
      }
      
      protected override void OnDeactivated(EventArgs e)
      {
          base.OnDeactivated(e);
          SetValue(IsNonClientActivePropertyKey, false);
      }
      
      private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
      {
          if (msg == WindowNotifications.WM_NCACTIVATE)
              SetValue(IsNonClientActivePropertyKey, wParam == _trueValue);
      
          return IntPtr.Zero;
      }
      

      需要添加一個(gè)只讀的IsNonClientActive依賴屬性,ControlTemplate通過(guò)Trigger使邊框置灰:

      <Trigger Property="IsNonClientActive"
               Value="False">
          <Setter Property="BorderBrush"
                  Value="#FF6F7785" />
      </Trigger>
      

      5. ResizeBorder的問(wèn)題

      5.1 ResizeBorder尺寸的問(wèn)題

      標(biāo)準(zhǔn)Window可以單擊并拖動(dòng)以調(diào)整窗口大小的區(qū)域?yàn)?像素(可以理解為SM_CXFRAME的4像素加上SM_CXPADDEDBORDER的4像素)。

      WindowChrome實(shí)際大小就是看起來(lái)的大小,默認(rèn)的ResizeBorderThickness是4像素,就是從Chrome的邊框向內(nèi)的4像素范圍,再多就會(huì)影響client-area里各元素的正常使用。

      由于標(biāo)準(zhǔn)Window的課拖動(dòng)區(qū)域幾乎在Window的外側(cè),而且有8個(gè)像素,而WindowChrome只能有4個(gè)像素,所以WindowChrome拖動(dòng)起來(lái)手感沒(méi)那么好。

      5.2 拖動(dòng)邊框產(chǎn)生的性能問(wèn)題

      最后提一下WindowChrome的性能問(wèn)題,正常操作我覺(jué)得應(yīng)該沒(méi)什么問(wèn)題,只有拖動(dòng)左右邊緣尤其是左邊緣改變Window大小的時(shí)候右邊的邊緣會(huì)很不和諧。其實(shí)這個(gè)問(wèn)題不是什么大問(wèn)題,看看這個(gè)空的什么都沒(méi)有的Skype窗體都會(huì)這樣,所以不需要特別在意。

      6. 其它自定義Window的方案

      在Kino.Toolkit.Wpf里我只提供了最簡(jiǎn)單的使用WindowChrome的方案,這個(gè)方案只能創(chuàng)建沒(méi)有圓角的Window,而且不能自定義邊框陰影顏色。如果真的需要更高的自由度可以試試參考其它方案。

      6.1 VisualStudio

      VisualStudio當(dāng)然沒(méi)有開(kāi)源,但并不妨礙我們?nèi)⒖妓脑创a。可以在以下DLL找到Microsoft.VisualStudio.PlatformUI.MainWindow:

      X:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Microsoft.VisualStudio.Shell.UI.Internal.dll

      6.2 FirstFloor.ModernUI

      Modern UI for WPF (MUI),A set of controls and styles converting your WPF application into a great looking Modern UI app.

      6.3 MahApps.Metro

      MahApps.Metro
      ,A framework that allows developers to cobble together a Metro or Modern UI for their own WPF applications with minimal effort.

      6.4 Fluent.Ribbon

      Fluent.Ribbon is a library that implements an Office-like user interface for the Windows Presentation Foundation (WPF).

      6.5 HandyControl

      HandyControll是一套WPF控件庫(kù),它幾乎重寫(xiě)了所有原生樣式,同時(shí)包含50多款額外的控件,還提供了一些好看的Window。

      6.6 Sakuno.UserInterface

      Sakuno.UserInterface,A framework with some powerful tools that allows developers to build a WPF application in modern UI.

      7. 參考

      WindowChrome Class (System.Windows.Shell) Microsoft Docs

      SystemParameters Class (System.Windows) Microsoft Docs

      WPF Windows 概述 _ Microsoft Docs

      GetSystemMetrics function Microsoft Docs

      FlashWindowEx function Microsoft Docs

      Window Class (System.Windows) Microsoft Docs

      Inspect - Windows applications Microsoft Docs

      8. 源碼

      Kino.Toolkit.Wpf_Window at master

      posted @ 2019-06-12 09:03  dino.c  閱讀(10795)  評(píng)論(13)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲欧洲一区二区精品| 日韩高清亚洲日韩精品一区二区| 成人做爰www网站视频| 综合色一色综合久久网| 日韩精品专区在线影观看| 年轻女教师hd中字3| 亚洲人成电影网站 久久影视| 亚洲综合在线亚洲优优色| 最新精品国偷自产在线 | 精品国产粉嫩内射白浆内射双马尾| 少妇人妻综合久久中文字幕| 中文字幕国产精品第一页| 98精品全国免费观看视频| 国产精品青草久久久久福利99| 国产中文字幕精品免费| 免费人妻无码不卡中文字幕系 | 亚洲偷自拍国综合| 欧美人与动牲交a免费| av日韩精品在线播放| 久久精品第九区免费观看| 久久精品夜色噜噜亚洲aa| 亚洲 欧洲 自拍 偷拍 首页| 国产精品午夜福利91| 7878成人国产在线观看| 最近2019免费中文字幕8| 无套内射视频囯产| 色就色偷拍综合一二三区| 国产亚洲av夜间福利香蕉149| 亚洲区中文字幕日韩精品| 一区二区不卡国产精品| 亚洲综合另类小说色区一| 深夜宅男福利免费在线观看| 一区二区三区鲁丝不卡| 凤阳县| 长腿校花无力呻吟娇喘的视频| 综合偷自拍亚洲乱中文字幕 | 色综合激情丁香七月色综合| 久久久久成人片免费观看蜜芽 | 免费a级毛片无码av| 色综合色综合色综合久久| 在线精品自拍亚洲第一区|