乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      【W(wǎng)PF學(xué)習(xí)】第六十八章 自定義繪圖元素

       路人甲Java 2021-04-01

        上一章分析了WPF元素的內(nèi)部工作元素——允許每個元素插入到WPF布局系統(tǒng)的MeasureOverride()和ArrangeOverride()方法中。本章將進(jìn)一步深入分析和研究元素如何渲染自身。

        大多數(shù)WPF元素通過組合方式創(chuàng)建可視化外觀。換句話說,典型的元素通過其他更基礎(chǔ)的元素進(jìn)行構(gòu)建。例如,使用標(biāo)記定義用戶控件的組合元素,處理標(biāo)記的方式與自定義窗口中的XAML相同。使用控件模板為自定義控件定義可視化樹。并且當(dāng)創(chuàng)建自定義面板時,根本不必定義任何可視化細(xì)節(jié)。組合元素由克難攻堅使用者提供,并添加到Children集合?!?/p>

        當(dāng)然,知道現(xiàn)在才能使用組合。最終,一些類需要負(fù)責(zé)繪制內(nèi)容。在WPF中,這些類位于元素樹的底層。在典型窗口中,是通過單獨(dú)的文本、形狀以及位圖執(zhí)行渲染的,而不是通過高級元素。

      一、OnRender()方法

        為了執(zhí)行自定義渲染,元素必須重寫OnRender()方法,該方法繼承自UIElement基類。OnRender()方法未必不需要替換組合——一些控件使用OnRender()方法繪制可視化細(xì)節(jié)并使用組合在其上疊加其他元素。Border和Panel類是兩個例子,Border類在OnRender()方法中繪制邊框,Panel類在OnRender()方法中繪制背景。Border和Panel類都支持子內(nèi)容,并且這些子內(nèi)容在自定義的繪圖細(xì)節(jié)之上進(jìn)行渲染。

        OnRender()方法接受一個DrawingContext對象,該對象為繪制內(nèi)容提供了了一套很有用的方法。在OnRender()方法中執(zhí)行繪圖的主要區(qū)別是不能顯示地創(chuàng)建和關(guān)閉DrawingContext對象。這是因?yàn)閹讉€不同的OnRender()方法可能使用相同的DrawingContext對象。例如,派生的元素可以執(zhí)行一些自定義繪圖操作并調(diào)用基類中的OnRender()方法來繪制其他內(nèi)容。這種方法是可行的,因?yàn)楫?dāng)開始這一過程時,WPF會自動創(chuàng)建DrawingContext對象,并且當(dāng)不再需要時關(guān)閉對象。

        關(guān)于WPF渲染,最令人驚奇的細(xì)節(jié)是實(shí)際上只需要使用很少的類。大多數(shù)類是通過其他更簡單的類構(gòu)建的,并且對于典型的控件,為了找到實(shí)際重寫OnRender()方法的類,需要進(jìn)入到控件元素樹中非常深的層次。下面是一些重寫OnRender()方法的類:

      •   TextBlock類。無論在何處放置文本,都有TextBlock對象使用OnRender()方法繪制文本。
      •   Image類。Image類重寫OnRender()方法,使用DrawingContext.DrawImage()方法繪制圖形內(nèi)容。
      •   MediaElement類。如果正在使用該類播放視頻文件,該類會重寫OnRender()方法以繪制視頻幀。
      •   各種形狀類。Shape基類重寫了OnRender()方法,通過使用DrawingContext.DrawGeometry()方法,繪制在其內(nèi)部存儲的Geometry對象。根據(jù)Shape類的特定派生類,Geometry對象可以表示橢圓、矩形或更復(fù)雜的由直線和曲線構(gòu)成的路徑。許多元素使用形狀繪制小的可視化細(xì)節(jié)。
      •   各種修飾類。這些類(如ButtonChrome和ListBoxChrome)繪制通用控件的外側(cè)外觀,并在具體制定的內(nèi)部放置內(nèi)容。其他許多繼承自Decorator的類,如Border類,都重寫了OnRender()方法。
      •   各種面板類。盡管面板的內(nèi)容是由其子元素提供的,但是OnRender()方法繪制具有背景色(假設(shè)設(shè)置了Background屬性)的矩形。

        通常,OnRender()方法的實(shí)現(xiàn)看起來很簡單。例如,下面是繼承自Shape類的所有渲染代碼:

      protected override void OnRender(DrawingContext drawingContext)
      {
          this.EnsureRenderedGeometry();
          if(this._renderedGeometry!=Geometry.Empty)
          {
              drawingContext.DrawingGeometry(this.Fill,this.GetPen(),this._renderedGeometry);
          }
      }

        請記住,重寫OnRender()方法不是渲染內(nèi)容并且將其添加到用戶界面的唯一方法。也可以創(chuàng)建DrawingVisual對象,并是喲AddVisualChild()方法為UIElement對象添加該可視化對象。然后可以調(diào)用DrawingVisual.RenderOpen()方法為DrawingVisual對象檢索DrawingContext對象,并使用返回的DrawingContext對象渲染DrawingVisual對象的內(nèi)容。

        在WPF中,一些元素使用這種策略在其他元素內(nèi)容之上顯示一些圖形細(xì)節(jié)。例如,在拖放指示器、錯誤指示器以及焦點(diǎn)框中可以看到這種情況。在所有這些情況中,DrawingVisual類允許元素在其他內(nèi)容之上繪制內(nèi)容,而不是在其他內(nèi)容之下繪制內(nèi)容。但對于大部分情況,是在專門的OnRender()方法中進(jìn)行渲染。

      二、評估自定義繪圖

        當(dāng)創(chuàng)建自定義元素時,可能會選擇重寫OnRender()方法來繪制自定義內(nèi)容??稍诎瑑?nèi)容的元素(最常見的情況是繼承自Decorator的類)中重寫OnRender()方法,從而可以在內(nèi)容周圍添加圖形裝飾。也可以在沒有任何嵌套內(nèi)容的元素中重寫OnRender()方法,從而可以繪制元素的整個可視化外觀。例如,可以創(chuàng)建繪制一些小的圖形細(xì)節(jié)的自定義元素,然后可以通過組合,在其他類中使用自定義元素。WPF中的這方面示例是TickBar元素,該元素為Slider控件繪制刻度標(biāo)記。TickBar元素通過Slider控件的默認(rèn)控件模板(該模板還包括一個Border和一個Track元素,Track元素又包含了兩個RepeatButton控件和一個Thumb元素)嵌入到Slider控件的可視化樹中。

        一個明顯的問題是需要確定何時使用較低級的OnRender()方法,以及何時使用其他類(l例如,繼承自Shape類的元素)的組合來繪制所需的內(nèi)容。為了做出決定,需要評估所需圖形的復(fù)雜程度以及希望提供的交互能力。

        例如,分析一下ButtonChrome類。在ButtonChrome類的WPF實(shí)現(xiàn)中,自定義的渲染代碼考慮了各種屬性,包括RenderDefaulted、RenderMouseOver以及RenderPressed。Button類的默認(rèn)控件模板在適當(dāng)?shù)臅r機(jī)使用觸發(fā)器設(shè)置這些屬性。例如,當(dāng)將鼠標(biāo)移動到按鈕上時,Button類使用觸發(fā)器將ButtonChrome.RenderMouseOver屬性設(shè)置為true。

        無論何時改變RenderDefaulted、RenderMouseOver或RenderPressed屬性,ButtonChrome類都會調(diào)用基本的InvalidateVisual()方法來指示當(dāng)前外觀不在有效。WPF然后調(diào)用ButtonChrome.OnRender()方法來獲取新的圖形表示。

        如果ButtonChrome類使用組合,這種行為就更難實(shí)現(xiàn)。使用合適的元素為ButtonChrome類創(chuàng)建標(biāo)準(zhǔn)外觀很容易,但是當(dāng)按鈕的狀態(tài)發(fā)生變化是,需要做更多的工作來修改外觀。需要動態(tài)改變構(gòu)成ButtonChrome類的嵌套元素,如果外觀變化很大的話,就必須隱藏一個元素并在合適的位置顯示另一個元素。

        大多數(shù)自定義元素不需要自定義渲染。但是當(dāng)屬性發(fā)生變化或執(zhí)行特定操作是,需要渲染復(fù)雜的變化很大的可視化外觀,此時使用自定義的渲染方法可能更加簡單并且更便捷。

      三、自定義繪圖元素

        通過前面對OnRender()方法的介紹,理解其工作原理。下面使用OnRender()方法創(chuàng)建自定義控件。

        下面創(chuàng)建了一個名為CustomDrawnElement的元素,演示了一種簡單的效果。該元素使用RadialGradientBrush畫刷繪制陰影背景,技巧是動態(tài)設(shè)置強(qiáng)調(diào)顯示的漸變起點(diǎn),使用其跟隨鼠標(biāo)。從而當(dāng)用戶在控件上移動鼠標(biāo)時,白色的發(fā)光中心點(diǎn)跟隨鼠標(biāo)移動。

        CustomDrawnElement元素不需要包含任何子內(nèi)容,所以它直接繼承自FrameworkElement類。該元素只提供了一個可以設(shè)置的屬性——漸變的背景色。

      public class CustomDrawnElement:FrameworkElement
          {
              public static DependencyProperty BackgroundColorProperty;
      
              static CustomDrawnElement()
              {
                  FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(Colors.Yellow);
                  metadata.AffectsRender = true;
                  BackgroundColorProperty = DependencyProperty.Register("BackgroundColor",
                      typeof(Color), typeof(CustomDrawnElement), metadata);
              }
      
      
              public Color BackgroundColor
              {
                  get
                  {
                      return (Color)GetValue(BackgroundColorProperty);
                  }
                  set
                  {
                      SetValue(BackgroundColorProperty, value);
                  }
              }
              ...
      }

        BackgroundColor依賴性屬性使用FrameworkPropertyMetadata.AffectRender標(biāo)志明確進(jìn)行了標(biāo)識。因此,無論何時改變了背景色,WPF都自動調(diào)用OnRender()方法。然而,當(dāng)鼠標(biāo)移動到新的位置時,也需要確保調(diào)用OnRender()方法。這是通過在合適的時間調(diào)用InvalidateVisual()方法實(shí)現(xiàn)的。

              . . .
              protected override void OnMouseMove(MouseEventArgs e)
              {
                  base.OnMouseMove(e);
                  this.InvalidateVisual();
              }
      
              protected override void OnMouseLeave(MouseEventArgs e)
              {
                  base.OnMouseLeave(e);
                  this.InvalidateVisual();
              }
             . . .

        剩下的唯一細(xì)節(jié)是渲染代碼。渲染代碼使用DrawingContext.DrawRectangle()方法繪制元素的背景。ActualWidth和ActualHeight屬性只是控件最終的渲染尺寸。

              . . .
              protected override void OnRender(DrawingContext dc)
              {
                  base.OnRender(dc);
      
                  Rect bounds = new Rect(0, 0, base.ActualWidth, base.ActualHeight);
                  dc.DrawRectangle(GetForegroundBrush(), null, bounds);
              }
              . . .

        最后,名為GetForegroundBrush()的私有輔助方法根據(jù)鼠標(biāo)的當(dāng)前位置構(gòu)造正確的RadialGradientBrush畫刷。為了計算中心點(diǎn),需要將鼠標(biāo)在元素上懸停的當(dāng)前位置轉(zhuǎn)換成從0到1的相對位置,這正是RadialGradientBrush畫刷期望的結(jié)果。

              . . .
              private Brush GetForegroundBrush()
              {
                  if (!IsMouseOver)
                  {
                      return new SolidColorBrush(BackgroundColor);
                  }
                  else
                  {
                      RadialGradientBrush brush = new RadialGradientBrush(Colors.White, BackgroundColor);
                      Point absoluteGradientOrigin = Mouse.GetPosition(this);
                      Point relativeGradientOrigin = new Point(
                          absoluteGradientOrigin.X / base.ActualWidth, absoluteGradientOrigin.Y / base.ActualHeight);
      
                      brush.GradientOrigin = relativeGradientOrigin;
                      brush.Center = relativeGradientOrigin;
                      brush.Freeze();
                      return brush;
                  }
              }
              . . .

      四、創(chuàng)建自定義裝飾元素

        作為一條通用規(guī)則,切勿在控件中使用自定義繪圖。如果在控件中使用自定義繪圖,就違反了WPF無外觀空間的承諾。問題是一旦硬編碼一些繪圖邏輯,就會使控件可視化外觀的一部分不能通過控件模板進(jìn)行定制。更好的方法是設(shè)計單獨(dú)的繪制自定義內(nèi)容的元素(如上面示例中的CustomDrawnElement類),然后在控件的默認(rèn)模板內(nèi)部使用自定義元素。

        有必要快速分析一下如何修改上面示例,使其能夠成為控件模板的一部分。在控件模板中,自定義繪圖元素通常扮演兩個角色:

      •   它們繪制一些小的圖形細(xì)節(jié)(例如滾動按鈕上的箭頭)。
      •   它們在另一個元素的周圍提供更詳細(xì)的背景或邊框。

        第二種方法需要自定義裝飾元素,可以通過兩個輕微的改動將CustomDrawnElement類轉(zhuǎn)換成自定義繪圖元素。首先,使該類繼承自Decorator類:

      public class CustomDrawnDecorator:Decorator

        然后重寫OnMeasure()方法,指定需要的尺寸,所有裝飾元素都會考慮它們的子元素,增加裝飾所需要的額外空間,然后返回組合之后的尺寸。CustomDrawnDecorator類不需要任何額外的空間來繪制邊框,相反,使用下面的代碼簡單地使其自定和其內(nèi)容具有相同的尺寸:

      protected override Size MeasureOverride(Size constraint)
              {
                  UIElement child = this.Child;
                  if (child != null)
                  {
                      child.Measure(constraint);
                      return child.DesiredSize;
                  }
                  else
                  {
                      return new Size();
                  }
      
              }

        一旦創(chuàng)建自定義裝飾元素,就可以在自定義控件模板中使用它們。例如,下面的按鈕模板在按鈕內(nèi)容的后面放置了跟隨鼠標(biāo)蹤跡的漸變背景。使用模板綁定確保使用對齊屬性和內(nèi)邊距屬性。

      <ControlTemplate x:Key="ButtonWithCustomChrome">
                  <lib:CustomDrawnDecorator BackgroundColor="LightGreen">
                      <ContentPresenter Margin="{TemplateBinding Padding}"
               HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
               VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
               ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
               Content="{TemplateBinding ContentControl.Content}"
               RecognizesAccessKey="True" />
                  </lib:CustomDrawnDecorator>
              </ControlTemplate>

        現(xiàn)在可以使用這個模板重新樣式化按鈕,使其具有新的外觀。當(dāng)然,為了使自定義裝飾元素更加實(shí)用,當(dāng)單擊鼠標(biāo)按鈕時可能更希望改變它的外觀。使用修改裝飾類屬性的觸發(fā)器可以完成該工作。

        本章示例源碼:CustomDrawnElement.zip

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多