成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

自定義Shape-創(chuàng)新互聯(lián)

1. 前言

這篇文章介紹了繼承并自定義Shape的方法,不過(guò),恐怕,事實(shí)上,100個(gè)xaml的程序員99個(gè)都不會(huì)用到。寫(xiě)出來(lái)是因?yàn)榉凑紝W(xué)了,當(dāng)作寫(xiě)個(gè)筆記。

創(chuàng)新互聯(lián)建站成立與2013年,先為北海街道等服務(wù)建站,北海街道等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為北海街道企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。

通過(guò)這篇文章,你可以學(xué)到如下知識(shí)點(diǎn):

  • 自定義Shape。

  • DeferRefresh模式。

  • InvalidateArrange的應(yīng)用。

2. 從Path派生

UWP中的Shape大部分都是密封類--除了Path。所以要自定義Shape只能從Path派生。Template10給出了這個(gè)例子:RingSegment 。

從這個(gè)類中可以看到,自定義Shape只需要簡(jiǎn)單地在每個(gè)自定義屬性的屬性值改變時(shí)或SizeChanged時(shí)調(diào)用private void UpdatePath()為Path.Data賦值就完成了,很簡(jiǎn)單吧。

自定義Shape

RingSegment.StartAngle = 30;
RingSegment.EndAngle = 330;
RingSegment.Radius = 50;
RingSegment.InnerRadius = 30;

3. BeginUpdate、EndUpdate與DeferRefresh

這段代碼會(huì)產(chǎn)生一個(gè)問(wèn)題:每更改一個(gè)屬性的值后都會(huì)調(diào)用UpdatePath(),那不就會(huì)重復(fù)調(diào)用四次?

事實(shí)上真的會(huì),顯然這個(gè)類的作者也考慮過(guò)這個(gè)問(wèn)題,所以提供了public void BeginUpdate()public void EndUpdate()函數(shù)。

/// <summary>/// Suspends path updates until EndUpdate is called;/// </summary>public void BeginUpdate(){
    _isUpdating = true;
}/// <summary>/// Resumes immediate path updates every time a component property value changes. Updates the path./// </summary>public void EndUpdate(){
    _isUpdating = false;    UpdatePath();
}

使用這兩個(gè)方法重新寫(xiě)上面那段代碼,就是這樣:

try{
    RingSegment.BeginUpdate();
    RingSegment.StartAngle = 30;
    RingSegment.EndAngle = 330;
    RingSegment.Radius = 100;
    RingSegment.InnerRadius = 80;
}finally{
    RingSegment.EndUpdate();
}

這樣就保證了只有在調(diào)用EndUpdate()時(shí)才執(zhí)行UpdatePath(),而且只執(zhí)行一次。

在WPF中,DeferRefresh是一種更成熟的方案。相信很多開(kāi)發(fā)者在用DataGrid時(shí)多多少少有用過(guò)(主要是通過(guò)CollectionView或CollectionViewSource)。典型的實(shí)現(xiàn)方式可以參考DataSourceProvider。在UWPCommunityToolkit中也通過(guò)AdvancedCollectionView實(shí)現(xiàn)了這種方式。

在RingSegment中添加實(shí)現(xiàn)如下:

private int _deferLevel;public virtual IDisposable DeferRefresh(){
    ++_deferLevel;    return new DeferHelper(this);
}private void EndDefer(){
    Debug.Assert(_deferLevel > 0);
    --_deferLevel;    if (_deferLevel == 0)
    {        UpdatePath();
    }
}private class DeferHelper : IDisposable{    public DeferHelper(RingSegment source)    {
        _source = source;
    }    private RingSegment _source;    public void Dispose()    {
        GC.SuppressFinalize(this);        if (_source != null)
        {
            _source.EndDefer();
            _source = null;
        }
    }
}

使用如下:

using (RingSegment.DeferRefresh())
{
    RingSegment.StartAngle = 30;
    RingSegment.EndAngle = 330;
    RingSegment.Radius = 100;
    RingSegment.InnerRadius = 80;
}

使用DeferRefresh模式有兩個(gè)好處:

  • 調(diào)用代碼比較簡(jiǎn)單

  • 通過(guò)_deferLevel判斷是否需要UpdatePath(),這樣即使多次調(diào)用DeferRefresh()也只會(huì)執(zhí)行一次UpdatePath()。譬如以下的調(diào)用方式:

using (RingSegment.DeferRefresh())
{
    RingSegment.StartAngle = 30;
    RingSegment.EndAngle = 330;
    RingSegment.Radius = 50;
    RingSegment.InnerRadius = 30;    using (RingSegment.DeferRefresh())
    {
        RingSegment.Radius = 51;
        RingSegment.InnerRadius = 31;
    }
}

也許你會(huì)覺(jué)得一般人不會(huì)寫(xiě)得這么復(fù)雜,但在復(fù)雜的場(chǎng)景DeferRefresh模式是有存在意義的。假設(shè)現(xiàn)在要更新一個(gè)復(fù)雜的UI,這個(gè)UI由很多個(gè)代碼模塊驅(qū)動(dòng),但不清楚其它地方有沒(méi)有對(duì)需要更新的UI調(diào)用過(guò)DeferRefresh(),而創(chuàng)建一個(gè)DeferHelper 的消耗比起更新一次復(fù)雜UI的消耗低太多,所以執(zhí)行一次DeferRefresh()是個(gè)很合理的選擇。

看到++_deferLevel這句代碼條件反射就會(huì)考慮到線程安全問(wèn)題,但其實(shí)是過(guò)慮了。UWP要求操作UI的代碼都只能在UI線程中執(zhí)行,所以理論上來(lái)說(shuō)所有UIElement及它的所有操作都是線程安全的。

4. InvalidateArrange

每次更改屬性都要調(diào)用DeferRefresh顯然不是一個(gè)聰明的做法,而且在XAML中也不可能做到。另一種延遲執(zhí)行的機(jī)制是利用CoreDispatcher的public IAsyncAction RunAsync(CoreDispatcherPriority priority, DispatchedHandler agileCallback)函數(shù)異步地執(zhí)行工作項(xiàng)。要詳細(xì)解釋RunAsync可能需要一整篇文章的篇幅,簡(jiǎn)單來(lái)說(shuō)RunAsync的作用就是將工作項(xiàng)發(fā)送到一個(gè)隊(duì)列,UI線程有空的時(shí)候會(huì)從這個(gè)隊(duì)列獲取工作項(xiàng)并執(zhí)行。InvalidateArrange就是利用這種機(jī)制的典型例子。MSDN上對(duì)InvalidateArrange的解釋是:

使 UIElement 的排列狀態(tài)(布局)無(wú)效。失效后,UIElement 將以異步方式更新其布局。

將InvalidateArrange的邏輯簡(jiǎn)化后大概如下:

protected bool ArrangeDirty { get; set; }public void InvalidateArrange(){    if (ArrangeDirty == true)        return;

    ArrangeDirty = true;
    Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        ArrangeDirty = false;        lock (this)
        {            //Measure
            //Arrange
        }
    });
}

調(diào)用InvalidateArrange后將ArrangeDirty標(biāo)記為True,然后異步執(zhí)行Measure及Arrange代碼進(jìn)行布局。多次調(diào)用InvalidateArrange會(huì)檢查ArrangeDirty的狀態(tài)以免重復(fù)執(zhí)行。利用InvalidateArrange,我們可以在RingSegment的自定義屬性值改變事件中調(diào)用InvalidateArrange,異步地觸發(fā)LayoutUpdated并在其中改變Path.Data。
修改后的代碼如下:

private bool _realizeGeometryScheduled;private Size _orginalSize;private Direction _orginalDirection;private void OnStartAngleChanged(double oldStartAngle, double newStartAngle){    InvalidateGeometry();
}private void OnEndAngleChanged(double oldEndAngle, double newEndAngle){    InvalidateGeometry();
}private void OnRadiusChanged(double oldRadius, double newRadius){    this.Width = this.Height = 2 * Radius;    InvalidateGeometry();
}private void OnInnerRadiusChanged(double oldInnerRadius, double newInnerRadius){    if (newInnerRadius < 0)
    {        throw new ArgumentException("InnerRadius can't be a negative value.", "InnerRadius");
    }    InvalidateGeometry();
}private void OnCenterChanged(Point? oldCenter, Point? newCenter){    InvalidateGeometry();
}protected override Size ArrangeOverride(Size finalSize){    if (_realizeGeometryScheduled == false && _orginalSize != finalSize)
    {
        _realizeGeometryScheduled = true;
        LayoutUpdated += OnTriangleLayoutUpdated;
        _orginalSize = finalSize;
    }    base.ArrangeOverride(finalSize);    return finalSize;
}protected override Size MeasureOverride(Size availableSize){     return new Size(base.StrokeThickness, base.StrokeThickness);
}public void InvalidateGeometry(){    InvalidateArrange();    if (_realizeGeometryScheduled == false )
    {
        _realizeGeometryScheduled = true;
        LayoutUpdated += OnTriangleLayoutUpdated;
    }
}private void OnTriangleLayoutUpdated(object sender, object e){
    _realizeGeometryScheduled = false;
    LayoutUpdated -= OnTriangleLayoutUpdated;    RealizeGeometry();
}private void RealizeGeometry(){    //other code here

    Data = pathGeometry;
}

這些代碼參考了ExpressionSDK的Silverlight版本。ExpressionSDK提供了一些Shape可以用作參考。(安裝Blend后通??梢栽谶@個(gè)位置找到它:C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\Silverlight\v5.0\Libraries\Microsoft.Expression.Drawing.dll)由于比起WPF,Silverlight更接近UWP,所以Silverlight的很多代碼及經(jīng)驗(yàn)更有參考價(jià)值,遇到難題不妨找些Silverlight代碼來(lái)作參考。

自定義Shape

InvalidateArrange屬于比較核心的API,文檔中也充斥著“通常不建議“、”通常是不必要的”、“慎重地使用它”等字句,所以平時(shí)使用最好要謹(jǐn)慎。如果不是性能十分敏感的場(chǎng)合還是建議使用Template10的方式實(shí)現(xiàn)。

5. 使用TemplatedControl實(shí)現(xiàn)

除了從Path派生,自定義Shape的功能也可以用TemplatedControl實(shí)現(xiàn),一般來(lái)說(shuō)這種方式應(yīng)該是最簡(jiǎn)單最通用的方式。下面的代碼使用TemplatedControl實(shí)現(xiàn)了一個(gè)三角形:

[TemplatePart(Name = PathElementName,Type =typeof(Path))]
[StyleTypedProperty(Property = nameof(PathElementStyle), StyleTargetType =typeof(Path))]public class TriangleControl : Control
    {        private const string PathElementName = "PathElement";    

        public TriangleControl()        {            this.DefaultStyleKey = typeof(TriangleControl);            this.SizeChanged += OnTriangleControlSizeChanged;
        }     

        /// <summary>
        ///     標(biāo)識(shí) Direction 依賴屬性。
        /// </summary>
        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(Direction), typeof(TriangleControl), new PropertyMetadata(Direction.Up, OnDirectionChanged));        /// <summary>
        ///     獲取或設(shè)置Direction的值
        /// </summary>
        public Direction Direction
        {            get { return (Direction)GetValue(DirectionProperty); }            set { SetValue(DirectionProperty, value); }
        }        private static void OnDirectionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)        {            var target = obj as TriangleControl;            var oldValue = (Direction)args.OldValue;            var newValue = (Direction)args.NewValue;            if (oldValue != newValue)
                target.OnDirectionChanged(oldValue, newValue);
        }        protected virtual void OnDirectionChanged(Direction oldValue, Direction newValue)        {            UpdateShape();
        }        /// <summary>
        /// 獲取或設(shè)置PathElementStyle的值
        /// </summary>  
        public Style PathElementStyle
        {            get { return (Style)GetValue(PathElementStyleProperty); }            set { SetValue(PathElementStyleProperty, value); }
        }        /// <summary>
        /// 標(biāo)識(shí) PathElementStyle 依賴屬性。
        /// </summary>
        public static readonly DependencyProperty PathElementStyleProperty =
            DependencyProperty.Register("PathElementStyle", typeof(Style), typeof(TriangleControl), new PropertyMetadata(null));        private Path _pathElement;        public override void OnApplyTemplate()        {            base.OnApplyTemplate();
            _pathElement = GetTemplateChild("PathElement") as Path;
        }        private void OnTriangleControlSizeChanged(object sender, SizeChangedEventArgs e)        {            UpdateShape();
        }        private void UpdateShape()        {            var geometry = new PathGeometry();            var figure = new PathFigure { IsClosed = true };
            geometry.Figures.Add(figure);            switch (Direction)
            {                case Direction.Left:
                    figure.StartPoint = new Point(ActualWidth, 0);                    var segment = new LineSegment { Point = new Point(ActualWidth, ActualHeight) };
                    figure.Segments.Add(segment);
                    segment = new LineSegment { Point = new Point(0, ActualHeight / 2) };
                    figure.Segments.Add(segment);                    break;                case Direction.Up:
                    figure.StartPoint = new Point(0, ActualHeight);
                    segment = new LineSegment { Point = new Point(ActualWidth / 2, 0) };
                    figure.Segments.Add(segment);
                    segment = new LineSegment { Point = new Point(ActualWidth, ActualHeight) };
                    figure.Segments.Add(segment);                    break;                case Direction.Right:
                    figure.StartPoint = new Point(0, 0);
                    segment = new LineSegment { Point = new Point(ActualWidth, ActualHeight / 2) };
                    figure.Segments.Add(segment);
                    segment = new LineSegment { Point = new Point(0, ActualHeight) };
                    figure.Segments.Add(segment);                    break;                case Direction.Down:
                    figure.StartPoint = new Point(0, 0);
                    segment = new LineSegment { Point = new Point(ActualWidth, 0) };
                    figure.Segments.Add(segment);
                    segment = new LineSegment { Point = new Point(ActualWidth / 2, ActualHeight) };
                    figure.Segments.Add(segment);                    break;
            }
            _pathElement.Data = geometry;
        }
    }
<Style TargetType="Path"       x:Key="PathElementStyle">
    <Setter Property="Stroke"            Value="RoyalBlue" />
    <Setter Property="StrokeThickness"            Value="10" />
    <Setter Property="Stretch"            Value="Fill" /></Style><Style TargetType="local:TriangleControl">
    <Setter Property="PathElementStyle"            Value="{StaticResource PathElementStyle}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:TriangleControl">
                <Border Background="{TemplateBinding Background}"                        BorderBrush="{TemplateBinding BorderBrush}"                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Path x:Name="PathElement"                          Style="{TemplateBinding PathElementStyle}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter></Style>

自定義Shape

這種方式的好處是容易實(shí)現(xiàn),而且兼容WPF和UWP。缺點(diǎn)是只能通過(guò)PathElementStyle修改Path的外觀,畢竟它不是Shape,而且增加了VisualTree的層次,不適合于性能敏感的場(chǎng)合。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

當(dāng)前標(biāo)題:自定義Shape-創(chuàng)新互聯(lián)
分享地址:http://jinyejixie.com/article10/hgogo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、網(wǎng)站制作、微信小程序標(biāo)簽優(yōu)化、外貿(mào)建站網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設(shè)
呼玛县| 甘孜县| 庄河市| 合川市| 东莞市| 通城县| 桂阳县| 沛县| 东莞市| 庄河市| 沛县| 克拉玛依市| 阿尔山市| 皮山县| 繁昌县| 中宁县| 杨浦区| 龙川县| 扎鲁特旗| 丹寨县| 永仁县| 林甸县| 阳西县| 荥经县| 米易县| 镇原县| 涞源县| 肃南| 平凉市| 新巴尔虎左旗| 永福县| 邵阳市| 会同县| 连江县| 阜城县| 鲜城| 工布江达县| 阿坝县| 枞阳县| 教育| 理塘县|