【WPF學習】第三十九章 理解形狀

来源:https://www.cnblogs.com/Peter-Luo/archive/2020/02/13/12301870.html

在WPF用戶界面中,繪製2D圖形內容的最簡單方法是使用形狀(shape)——專門用於表示簡單的直線、橢圓、矩形以及多變形的一些類。從技術角度看,形狀就是所謂的繪圖圖元(primitive)。可組合這些基本元素來創建更複雜的圖形。 關於WPF中形狀的重要細節是,它們都繼承自FrameworkEleme ...


  在WPF用戶界面中,繪製2D圖形內容的最簡單方法是使用形狀(shape)——專門用於表示簡單的直線、橢圓、矩形以及多變形的一些類。從技術角度看,形狀就是所謂的繪圖圖元(primitive)。可組合這些基本元素來創建更複雜的圖形。

  關於WPF中形狀的重要細節是,它們都繼承自FrameworkElement類。因此,形狀是元素。這樣會帶來許多重要的結果:

  •   形狀繪製自身。不需要管理無效的情況和繪圖過程。例如,當移動內容、改變視窗尺寸或改變形狀屬性時,不需要手動重新繪製形狀。
  •   使用與其他元素相同的方式組織形狀。換句話說,可在前面學過的任何佈局容器中放置形狀(儘管Canvas明顯是最有用的容器,因為它允許在特定的坐標位置放置形狀,當構建複雜的具有多個部分的圖畫時,這很重要)。
  •   形狀支持與其他元素相同的事件。這意味著為了處理焦點、按下鍵盤、移動滑鼠以及單擊滑鼠等,不必執行任何額外工作。可使用用於其他元素的相同事件集,並同樣支持工具提示、上下文菜單和拖放操作。

一、Shape類

  每個形狀都繼承自抽象類System.Windows.Shapes.Shape。下圖顯示了形狀類的繼承層次。

 圖 WPF形狀類

  正如上面看到的,相對來說,只有很少一部分類繼承自Shape類。Line、Ellipse以及Rectangle都很直觀,Polyline是一系列相互連接的直線,Polygon是由一系列相互連接的直線形成的閉合圖形。最後,Path類功能強大,能將多個基本形狀組合成單獨的元素。

  儘管Shape類自身不能執行任何工作,但它定義了少量的重要屬性。下表列出了這些屬性。

表 Shape類的屬性

 二、矩形和橢圓

  矩形和橢圓是兩個最簡單的形狀。為創建矩形或橢圓,需要設置大家熟悉的Height和Width屬性(這兩個屬性繼承自FrameworkElement類)來定義形狀的尺寸,然後設置Fill或Stroke屬性(或同時設置這兩個屬性)使形狀可見。還可以使用MinHeigth、MinWidth、HorizontalAlignment、VerticalAlignment以及Margin等屬性。

  下麵舉一個簡單示例,該例在StackPanel面板上放置了一個橢圓和一個矩形,效果圖如下所示:

<StackPanel>
            <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="100" Margin="5" HorizontalAlignment="Left"></Ellipse>
            <Rectangle Fill="Yellow" Stroke="Blue" Height="50" Width="100" Margin="5" HorizontalAlignment="Left"></Rectangle>
</StackPanel>

   Ellipse類沒有增加任何屬性。Rectangle類只增加了兩個屬性:RadiusX和RadiusY。如果將這兩個屬性的值設為非零值,就可以創建出美觀的圓形拐角。

  可認為RadiusX和RadiusY屬性是用於填充矩形拐角的橢圓。例如,如果將這兩個屬性都設為10,WPF會使用10個單位寬的圓形邊緣繪製拐角。隨著半徑的增大,矩形拐角的更多部分會被替換。如果增加RadiusY屬性的值,使其大於RadiusX屬性的值,矩形拐角的左邊和右邊會更平緩,而頂部和底邊的邊緣會更尖銳。如果增大RadiusX屬性的值,使其等於矩形寬度,並增加RadiusY屬性的值,使其等於矩形的寬度,矩形最後會變成普通的橢圓。如下圖所示:

<Window x:Class="Drawing.RoundedRectangles"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RoundedRectangles" Height="447.744" Width="300">
    <StackPanel>
        <TextBlock Margin="5,5,0,0">Corner radius of 5.</TextBlock>
        <Rectangle Fill="Yellow" Stroke="Blue" RadiusX="5" RadiusY="5"
               Width="100" Height="60" Margin="5"  HorizontalAlignment="Left">
        </Rectangle>
        <TextBlock Margin="5,5,0,0">Corner radius of 10.</TextBlock>
        <Rectangle Fill="Yellow" Stroke="Blue" RadiusX="10" RadiusY="10"
               Width="100" Height="60" Margin="5"  HorizontalAlignment="Left"></Rectangle>
        <TextBlock Margin="5,5,0,0">Corner radius of 10 (X) and 25 (Y).</TextBlock>
        <Rectangle Fill="Yellow" Stroke="Blue" RadiusX="10" RadiusY="25"
               Width="100" Height="60" Margin="5"  HorizontalAlignment="Left"></Rectangle>
        <TextBlock Margin="5,5,0,0">Corner radius of 100 (X) and 60 (Y).</TextBlock>
        <Rectangle Fill="Yellow" Stroke="Blue" RadiusX="100" RadiusY="60"
               Width="100" Height="60" Margin="5"  HorizontalAlignment="Left"></Rectangle>
    </StackPanel>
</Window>
RoundedRectangles

 三、改變形狀的尺寸和放置形狀

  正如前面所知,贏編碼尺寸通常不是創建用戶界面的理想方法。它們會限制處理動態內容的能力,並會使應用程式本地化到其他語言變得更加困難。

  當繪製形狀時,不再總是關心這些問題。通常,需要更嚴格地控制形狀的位置。然而,在許多情況下仍需要靈活一點設計。Ellipse和Rectangle為了適應可用的空間,都能自動改變自身。

  如果為提供Height和Width屬性,形狀會根據它們的容器來設置自身的尺寸。在上一個示例中,如果刪除Height和Width值(並且不設置MinHeight和MinWidth值),就會導致形狀縮小到看不見,因為StackPanel面板為了適應其內容改變了尺寸。然而,如果強制StackPanel面板的寬度為整個視窗的寬度(通過將HorizontalAlignment屬性設置為Stretch),並將橢圓的HorizontalAlignment屬性設置為Stretch,刪除橢圓的Width屬性值,這時橢圓的寬度就是整個視窗的寬度。

  可使用Grid容器構造更好的示例。如果使用按比例改變行尺寸的行為(預設行為),就可使用下麵更精簡的標記創建填滿視窗的橢圓:

<Grid>
    <Ellipse Fill="Yellow" Stroke="Blue"></Ellipse>
</Grid>

  在上面的標記中,Grid面板填滿了整個視窗。Grid面板包含了一個按比例改變尺寸的行,該行填滿了整個Grid面板。最後,橢圓填滿了整行。

  改變形狀尺寸的行為依賴於Stretch屬性的值(該屬性在Shape類中定義)。預設情況下,該屬性被設置為Fill。如果改變指定明確的尺寸,這一設置會拉伸形狀,使其填滿容器。下表列出了Stretch屬性的所有可能值。

表 Stretch枚舉值

   下圖顯示了Fill、Uniform、UniformToFill枚舉值之間的區別.

<Window x:Class="Drawing.FillModes"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="FillModes" Height="270" Width="477"
    >
    <Grid ShowGridLines="True" Margin="5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>

        <Ellipse Fill="Yellow" Stroke="Blue"></Ellipse>
        <Ellipse Fill="Yellow" Stroke="Blue" Grid.Column="1" Stretch="Uniform"></Ellipse>
        <Ellipse Fill="Yellow" Stroke="Blue" Grid.Column="2" Stretch="UniformToFill "></Ellipse>

        <TextBlock Grid.Row="1" TextAlignment="Center">Fill</TextBlock>
        <TextBlock Grid.Row="1" Grid.Column="1" TextAlignment="Center">Uniform</TextBlock>
        <TextBlock Grid.Row="1" Grid.Column="2" TextAlignment="Center">UniformToFill</TextBlock>
    </Grid>
</Window>
FillModes

 

   通常,將Stretch的值設置為Fill相當於將HorizontalAlignment和VerticalAlignment屬性設置為Stretch。但如果選擇為形狀設置固定的寬度和高度,二者就有區別了。對於這種情況,會簡單地忽略HorizontalAlignment和VerticalAlignment值。而Stretch設置仍然起作用——該屬性決定如何在給定的範圍內改變形狀內容的尺寸。

  到目前位置,已看到如何改變Rectangle和Ellipse形狀的尺寸,但如何準確地將它們放到期望的位置呢?WPF形狀與其他元素使用相同的佈局系統。然而,有些佈局容器是不合適的。例如,通常不希望使用StackPanel、DockPanel以及WrapPanel面板,因為它們都被設計為獨立的元素。Grid面板更靈活一些,因為它允許在同一個單元格中放置任意多個元素(儘管不能在單元格中的不同部分定位矩形和橢圓)。理想容器是Canvas,該容器要求使用Left、Top、Right或Bottom附加屬性,為每個形狀指定坐標。這樣可以完全控制形狀如何相互重疊:

<Canvas>
        <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="100" Canvas.Top="50" Canvas.Left="100"></Ellipse>
        <Rectangle Fill="Yellow" Stroke="Blue" Height="50" Width="100" Canvas.Top="40" Canvas.Left="30"></Rectangle>
    </Canvas>

  如果使用Canvas容器,標簽的順序是很重要的。在上面的示例中,矩形疊加在橢圓之上,因為在標簽列表中首先出現的是橢圓,所以首先繪製橢圓。

   請記住,Canvas容器不在需要占據整個視窗。例如,完全可以創建一個Grid面板,併在該Grid面板的某個單元格中使用Canvas容器,對於在可自由流動的動態用戶界面中鎖定一小部分繪圖邏輯,這是一種非常好的方法。

四、使用Viewbox控制項縮放形狀

  使用Canvas控制項的唯一限制是圖形不能改變自身的尺寸以適應更大或更小的視窗。對於按鈕這非常合理(在這些情況下,按鈕不改變尺寸),但是對於其他類似的圖形內容,情況就未必如此了。

  對於此類情況,WPF提供了簡便的解決方法。如果希望聯合Canvas控制項的精確控制功能和方便的改變尺寸功能,可使用Viewbox元素。

  Viewbox是繼承自Decorator的簡單類。該類只接受一個子元素,並拉伸或縮小子元素以適應可用的空間。當然,這個單一的子元素可以是佈局容器,其中包含大量形狀(或其他元素),這些元素將同步地改變尺寸。然而,Viewbox更長用於矢量圖像而不是普通控制項。

  儘管可在Viewbox元素中放置單個形狀,但這並不能提供任何實際的優點。反而,當需要封裝構成一幅圖畫(drawing)的一組形狀時,Viewbox元素才有用處。通常,將在Viewbox控制項中放置Canvas,併在Canvas面板中放置形狀。

  下麵的示例在Grid控制項的第二行中放置了一個包含Canvas面板的Viewbox元素。Viewbox元素占用改行的整個高度和寬度。該行占用繪製自動改變尺寸的第一行剩餘的所有空間,下麵是標記:

<Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock> The first row of a grid</TextBlock>
        <Viewbox Grid.Row="1" HorizontalAlignment="Left" MaxHeight="500">
            <Canvas Width="200" Height="150">
                <Ellipse Fill="Yellow" Stroke="Blue" Canvas.Left="10"  Canvas.Top="50"
               Width="100" Height="50" HorizontalAlignment="Left"></Ellipse>
                <Rectangle Fill="Yellow" Stroke="Blue" Canvas.Left="30"  Canvas.Top="40"                 
                 Width="100" Height="50" HorizontalAlignment="Left"></Rectangle>
            </Canvas>
        </Viewbox>
    </Grid>

  下圖顯示了當改變視窗尺寸時,Viewbox控制項如何調整自身。第一行沒有變化。然而為填滿額外控制項,第二行進行了擴展。正如看到的,Viewbox控制項中的形狀也根據視窗增大的比例改變了他們的大小。

  

   預設情況下,Viewbox元素按比例地執行縮放,保持它所包含內容的縱橫比。在當前示例中,這意味著即使包含行的形狀發生了變化(變寬或變高),內部形狀也不會變形。相反,Viewbox元素使用適應可用空間內部的最大縮放繫數。然而,可使用Viewbox.Stretch屬性改變該行為。預設情況下,將該屬性設置為Uniform。可將其改變為Fill,Viewbox元素中的內容會在兩個方向上被拉伸以完全適應可用空間,即使可能會破壞原來的繪圖也會如此。還可通過使用StretchDirection屬性獲得更大的控制權。預設情況下,該屬性被設置為Both,但可使用UpOnly值創建只會增長而不會收縮超過其原始尺寸的內容,並且可以使用DownOnly創建只會縮小而不會增長的內容。

  為時Viewbox元素執行其縮放工作,需要能夠確定兩部分信息:(如果不放在Viewbox元素中)內容應當具有的原始尺寸和希望內容具有的新尺寸。

  第二個細節——新尺寸——非常簡單。Viewbox元素根據Stretch屬性,讓其內部的內容使用所有可用空間,這意味著Viewbox元素越大,其內部的內容就越大。

  第一個細節——原始尺寸,不使用Viewbox空間時的尺寸——隱含在定義嵌套內容的方式中。在前面的示例中,Canvas的尺寸被明確設置為200X150單位大小。因此,Viewbox從該開始點縮放圖像。例如,橢圓最初是100單位寬,這意味著它占用Canvas面板一半的繪圖空間。隨著Canvas控制項的增大,Viewbox元素會遵循這些比例,並且橢圓繼續占用一半的可用控制項。

  然而,如果刪除Canvas控制項的Width和Height屬性,分析會發生什麼情況。現在,Canvas控制項的尺寸被設置為0X0單位大小,所以Viewbox控制項不能改變它的尺寸,並且嵌套在其中的內容不會顯示(這與只使用Canvas控制項時的行為不同。因為儘管Canvas控制項的尺寸仍設置為0X0,但只要Canvas.ClipToBounds屬性沒有被設置為true,就仍然允許在Canvas控制項之外的區域繪製形狀。而Viewbox控制項不能容忍這一錯誤)。

  現在分析一下,如果在按比例改變尺寸的Grid面板的單元格中封裝Canvas面板,並且沒有指定Canvas面板的尺寸,情況又會怎樣。如果沒有使用Viewbox元素,該方法可工作得很好——拉伸Canvas面板以填充單元格,並且內部的內容是可見的。但如果將所有內容放在Viewbox元素中,這種方法就會失效。Viewbox控制項不能確定最初尺寸,因此不能響應地改變Grid面板的尺寸。

  可通過直接在能自動改變尺寸的容器(如Grid面板)中放置特定的形狀(如Rectangle和Ellipse)來避免這個問題。然後Viewbox控制項就能評估Grid面板為了適合其內容所需的最小尺寸,並且縮放Grid面板以適應可用空間。然而,在ViewBox元素中獲取真正所希望的尺寸的最簡單方法,是在具有固定尺寸的元素中封裝內容,可以是Canvas面板、按鈕或其他控制項。這樣,固定尺寸就變成了Viewbox控制項進行計算所需要的原始尺寸。以這種方式硬編碼尺寸不會限制佈局的靈活性,因為Viewbox元素根據可用空間和佈局容器按比例改變尺寸。

五、直線

  Line形狀表示連接一個點和另一個點的一條直線。起點和重點由4個屬性設置:X1與Y1(用於第一個點)和X2與Y2(用於第二個點)。例如,下麵是一條從點(0,0)伸展到點(10,100)的直線:

<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100"></Line>

   對於直線,Fill屬性不起作用,必須設置Stroke屬性。

  在直線中使用的坐標是相對於放置直線的矩形區域左上角的坐標。例如,如果在StackPanel面板上放置上面的直線,坐標(0,0)指向在StackPanel面板上放置該矩形區域的位置,這可能是視窗的左上角,也可能不是。如果StackPanel面板的Margin屬性值不為0,或直線在其他元素之後,直線的開始點(0,0)與視窗頂部會有一定的距離。

  然而,在直線中使用負坐標值是非常合理的。實際上,可為直線使用能超出為直線保留的空間的坐標,從而在視窗的其他任意部分繪製直線。對於到目前位置介紹的Rectangle和Ellipse形狀;這是不可能的。然而,這一模型也有缺點,直線不能使用流內容模型。這意味著為直線設置Margin、HorizontalAlignment以及VerticalAlignment屬性是沒有意義的,因為它們沒有任何效果。對於Polyline和Polygon形狀具有相同的限制。

  如果在Canvas面板上放置了Line形狀,那麼仍應用附加的位置屬性(如Top和Left)。它們決定直線的開始位置。換句話說,兩個直線坐標被平移一定的距離。分析下麵的直線:

<Line Stroke="Blue" X1="0" Y1="0" X2="10" Y2="100"
    Canvas.Left="5" Canvas.Top="100"/>

   這條直線從點(0,0)伸展到點(10,100),使用的坐標系統將Canvas控制項上的點(5,100)作為點(0,0)。這相當於下麵不使用Top和Left屬性的直線:

<Line Stroke="Blue" X1="5" Y1="100" X2="15" Y2="200"/>

  當在Canvas面板上放置Line形狀時,是否使用位置屬性由自己決定。通常,可通過選擇好的開始點簡化直線的繪製,還可以使移動部分圖畫變得容易。例如,如果在Canvas面板的特定位置繪製幾條直線和其他形狀,相對於附近的點繪製它們是不錯的主意(通過使用相同的Top和Left坐標)。通過這種方法,可根據需要將整個圖畫移到新的位置。

六、折線

  可以通過Polyline類繪製一系列相互連接的直線。只需要使用Points屬性提供一系列X和Y坐標。從技術角度看,Points屬性需要使用PointCollection對象,但在XAML中使用基於簡單字元串的語法填充該集合。只需要提供點的列表,併在每個坐標之間添加空格或逗號。

  Polyline形狀可能只有兩個點。例如下麵的Polyline形狀,從點(5,100)伸展到點(15,200):

<Polyline Stroke="Blue" Points="5 100 15 200"/>

  為便於閱讀,可在每個X和Y坐標之間使用逗號:

<Polyline Stroke="Blue" Points="5,100 15,200"/>

  下麵是繪製的更複雜Polyline形狀。點不斷右移,併在更高的Y值——比如(50,160),和更低的Y值——比如(70,130)之間擺動:

<Canvas>
    <Polyline Stroke="Blue" StrokeThickness="5" 
        Points="10,150 30,140 50,160 70,130 90,170 110,120 130,180 150,110 170,190 190,100 210,240" >
    </Polyline>
</Canvas>

  下圖顯示了最終繪製的線條。

 

   對於這個示例,通過代碼使用各種相應地自動增加X和Y值的迴圈填充Point集合可能更容易。如果需要創建高度動態的圖形,事實卻是如此——例如,根據從資料庫中提取的數據集改變其外觀的圖標。但是,如果只是希望構建固定的圖形內容,就根本不需要形狀的具體坐標。相反,可使用另一個工具,如Express Design,繪製恰當的圖形,然後到處到XAML。

七、多邊形

  實際上,Polygon和Polyline是相同的。和Polyline類一樣,Polygon類也有包含一系列坐標的Points集合。唯一的區別是:Polygon形狀添加最後一條線段,將最後一個點連接到開始點(如果最後一個點就是第一個點,Polygon類和Polyline類就沒有區別了)。可使用Fill畫刷填充該形狀的內部區域。通過修改上一節的示例,顯示Polygon:

<Canvas>
        <Polygon Stroke="Blue" StrokeThickness="5" Fill="Yellow" 
        Points="10,150 30,140 50,160 70,130 90,170 110,120 130,180 150,110 170,190 190,100 210,240" >
        </Polygon>
</Canvas>

  最終效果圖如下所示:

 

   對於線條從不相交的簡單形狀,填充其內部很容易做到。但有時會遇到更複雜的Polygon形狀,哪些部分屬於內部(並且應當被填充)以及哪些部分屬於外部並不明顯。

  下麵一個示例,該形狀的特點是一條線段和其他多條線段相交,可能希望填充也希望不填充中央的不規則區域。顯然,可通過將該圖像分割成更小的形狀來準確地控制填充區域。但不需要這麼做。

 <Polygon Stroke="Blue" StrokeThickness="1" Fill="Yellow" 
        Points="15,200 68,70 110,200 0,125 135,125" >
</Polygon>

 

   每個Polygon和Polyline形狀都有FillRule屬性,該屬性用於從兩種填充方法中選擇一種來填充區域。預設情況下,FillRule屬性被設置為EventOdd。為了確定是否填充區域,WPF計算為了到達形狀的外部必須穿過的直線的數量。如果是奇數,就填充區域;如果是偶數,就不填充區域。對於上圖顯示的中央區域,為了到達形狀外部就必須經過兩條直線,所以不會填充該區域。

  WPF還遵循NonZero填充規則,該規則更加複雜。本質上,當使用NonZero填充規則時,WPF使用和EventOdd填充規則相同的方法計算穿過的直線的數量,但是會考慮經過的每條直線的防線。如果在經過的直線中,在某個方向上(比如從左項右)直線的數量等於相反方向(從右向左)上直線的數量,就不會填充區域。如果這兩個直線數量的差不為0,就填充區域。對於上個示例,如果將FillRule屬性設置為NonZero,J就會填充內部區域。

<Polygon Stroke="Blue" StrokeThickness="1" Fill="Yellow" 
               FillRule="Nonzero"
         Points="15,200 68,70 110,200 0,125 135,125" >
</Polygon>

 

   有關NonZero規則的複雜問題在於填充設置依賴於形狀的繪製,而不是形狀自身的外觀。

八、直線線帽和直線交點

  當繪製Line和Polyline形狀時,可使用StartLineCap和EndLineCap屬性選擇如何繪製直線的開始端和結束端(這些屬性不影響其他形狀,因為其他形狀都是閉合的)。

  StartLineCap和EndLineCap屬性通常都設為Flat,這意味著直線在它的最後坐標處立即終止。其他選擇包括Round(該設置會平滑地繪製拐角),Triangle(繪製直線的兩條側邊最後交於一點)以及Square(該設置使直線埠具有尖銳邊緣)。這兩個設置都會增加直線的長度——換句話說,它們使直線超出了其他情況下的結束位置。額外的距離是直線寬度的一半。下圖顯示了直線埠處不同線帽之間的區別。

<Window x:Class="Drawing.LineCaps"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LineCaps" Height="333" Width="376">
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Polyline Stroke="Blue" StrokeThickness="15" StrokeEndLineCap="Flat" SnapsToDevicePixels="True"
      Points="10,10 30,0 50,20 90,10 200,10" >
        </Polyline>
        <TextBlock Grid.Column="1">Flat Line Cap</TextBlock>

        <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="1" StrokeEndLineCap="Square" SnapsToDevicePixels="True"
      Points="10,10 30,0 50,20 90,10 200,10" >
        </Polyline>
        <TextBlock Grid.Row="1" Grid.Column="1">Square Line Cap</TextBlock>

        <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="2" StrokeEndLineCap="Round" SnapsToDevicePixels="True"
      Points="10,10 30,0 50,20 90,10 200,10" >
        </Polyline>
        <TextBlock Grid.Row="2" Grid.Column="1">Round Line Cap</TextBlock>

        <Polyline Stroke="Blue" StrokeThickness="15" Grid.Row="3" StrokeEndLineCap="Triangle"  SnapsToDevicePixels="True"
         Points="10,10 30,0 50,20 90,10 200,10" >
        </Polyline>
        <TextBlock Grid.Row="3" Grid.Column="1">Triangle Line Cap</TextBlock>
    </Grid>
</Window>
LineCaps

 

   除Line形狀外,所有形狀都運行使用StrokeLineJoin屬性扭曲它們的拐角,有4中選擇。Miter值(預設值)使用尖銳的邊緣,Bevel值切掉點邊緣,Round值平滑地過渡邊緣,Triangle值顯示尖點。下圖顯示StrokeLineJoin效果圖。

<Window x:Class="Drawing.LineJoins"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LineJoins" Height="431" Width="303"
    >
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Polyline Stroke="Blue" StrokeThickness="14" StrokeLineJoin="Bevel" SnapsToDevicePixels="True"
      Points="10,60 30,10 50,70 90,40" >
        </Polyline>
        <TextBlock Grid.Column="1" VerticalAlignment="Center">Bevel Line Join</TextBlock>

        <Polyline Stroke="Blue" StrokeThickness="14"


	
您的分享是我們最大的動力!

更多相關文章
  • 1,分頁嘛先要有個SQL 程式才能寫下去 先提供下SQL的思路,對於分頁的SQL我之前帖子有介紹,就不一一介紹了 select top pageSize * --顯示數量 from (select row_number() over(order by EG_ID asc) as rownumber, ...
  • 昨天看新聞,說人教社開放了人教版中小學教材電子版的春季教材(下載地址:http://bp.pep.com.cn/jc/ ),就想著給兒子全下載下來以備後用。不過人工下載真是麻煩枯燥,就為了省事,就寫個爬蟲。原本打算用python,回頭想了下,好久沒用C#了,就用C#寫吧。 具體思路和實現步驟如下 1 ...
  • 本例通過Timer的tick()方法觸發TimerCallback委托來開闢新的線程,線程中的具體工作通過一個靜態方法作為參數給TimerCallback委托。 using System; using System.Threading; /* 這是一個關於 timer開啟多線程的一個例子 * 1.T ...
  • 天天宅在家裡,沒什麼事做,錄個教學視頻吧! 發到了視頻網站上去根本沒人看,傷心ing啊! 不知cnblogs上面是否讓我發! https://www.bilibili.com/video/av88668329 加qq群 336090194可以下載源碼! ...
  • 前言 對於服務端,達到高性能、高擴展離不開非同步。對於客戶端,函數執行時間是1毫秒還是100毫秒差別不大,沒必要為這一點點時間煞費苦心。對於非同步,好多人還有誤解,如: 非同步就是多線程;非同步就是如何利用好線程池。非同步不是這麼簡單,否則微軟沒必要在非同步上花費這麼多心思。本文就介紹非同步最新的實現方式:Tas ...
  • 在工作中,會遇到需要多線程處理相應的業務需求,最典型的包括Socket的通信。 多線程處理里,就會考慮到,哪個線程先運行,哪個線程後運行的情況。 這裡我介紹一下,使用ManualResetEvent類來對線程進行阻塞和繼續操作。 它有三個重要的方法:Reset、Set和WaitOne。 1、首先介紹 ...
  • RedHat7安裝NetCore環境併發布網站 1.註冊Microsoft簽名密鑰並添加Microsoft產品提要,每台機器只需註冊一次 執行下麵的命令即可 rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsof ...
  • 學習劉鐵猛老師《C#語言入門詳解》視頻,針對其中重點知識點進行總結。 1、什麼是類型? 類型又稱為數據類型(Data Type),數據類型在數據結構中的定義是一個值的集合以及定義在這個值集上的一組操作。 可以簡單理解為數據在記憶體中存儲的“型號”;小記憶體容納大尺寸數據會丟失精準度,發生錯誤;而大記憶體容 ...
一周排行
  • .NET 走向開源,MIT許可協議。 微軟為了推動.NET開源社區的發展,2014年聯合社區成立了.NET基金會。 一年前 .NET 基金會完成第一次全面改選,2014年 .NET基金會的創始成員中有六位創始人,均非微軟公司員工,隨著微軟的收購動作,Miguel 也成了微軟員工,Migel一直在努力 ...
  • 在這篇文章中,我將帶領大家詳細學習ASP.NET Core 中的Main方法。在這篇文章中,我將向大家詳細介紹下麵幾個問題:ASP.NET Core Main方法的重要性為什麼我們在ASP.NET Core中會有一個Main方法?當你運行一個ASP.NET Core應用程式的時候,背後發生了什麼?為... ...
  • IViewLocationExpander API ExpandViewLocations Razor視圖路徑,視圖引擎會搜索該路徑. PopulateValues 每次調用都會填充路由 項目目錄如下所示 創建區域擴展器,其實我並不需要多區域,我目前只需要達到一個區域中有多個文件夾進行存放我的視圖. ...
  • EF Core 數據變更自動審計設計 Intro 有的時候我們需要知道每個數據表的變更記錄以便做一些數據審計,數據恢復以及數據同步等之類的事情, EF 自帶了對象追蹤,使得我們可以很方便的做一些審計工作,每次變更發生了什麼變化都變得很清晰,於是就基於 EF 封裝了一層數據變更自動審計 使用效果 測試 ...
  • 在上一篇文章abp(net core)+easyui+efcore實現倉儲管理系統——入庫管理之六(四十二)中我們實現了新增入庫單的功能。結合之前的五篇文章,今天我們來測試一下入庫單新增功能。 ...
  • 這篇文章,我們一起學習ASP.NET Core InProcess Hosting.這篇文章主要討論下麵幾個觀點:CreateDefaultBuilder方法執行什麼任務?什麼是ASP.NET Core InProcess Hosting?怎麼使用InProcess hosting Model來托管... ...
  • 關於 Blazor Server Side 的開篇 , Blazor與 C/S , B/S 有什麼不一樣 , Blazor有什麼優缺點? ...
  • VS2019+MVC+EF6 CodeFirst 連接MySQL 1、準備環境(通過NuGet獲取) EntityFramework MySql.Data.Entity 安裝後確認 2、在MVC Model文件夾下添加一個學生類,後面用它通過[數據遷移]在MySQL中創建一個表 3、創建數據上下文 ...
  • 這篇文章,向大家介紹ASP.NET Core中的Kestrel Web伺服器。這篇文章主要討論下麵兩個重要的事情:什麼是Kestrel 伺服器?怎麼使用.NET Core CLI來運行ASP.NET Core應用程式?什麼是Kestrel伺服器? 我們已經知道ASP.NET Core是一個跨平臺的開... ...
  • 前言 回顧之前的兩篇Swagger做Api介面文檔,我們大體上學會瞭如何在net core3.1的項目基礎上,搭建一套自動生產API介面說明文檔的框架。 本來在Swagger的基礎上,前後端開發人員在開發生產期間,可以藉此進行更加便捷的溝通交流。可是總有些時候,遇到一些難纏的,又不講道理,偏偏覺得將 ...