日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

UWP Composition API - RadialMenu

發(fā)布時(shí)間:2025/3/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UWP Composition API - RadialMenu 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
原文:UWP Composition API - RadialMenu

用Windows 8.1的童鞋應(yīng)該知道OneNote里面有一個(gè)RadialMenu。如下圖,下圖是WIn10應(yīng)用Drawboard PDF的RadialMenu,Win8.1的機(jī)器不好找了。哈哈,由于整個(gè)文章比較長(zhǎng),大家可以放《給我一首歌的時(shí)間》 邊聽(tīng)邊看。<滑稽>

從設(shè)計(jì)到開(kāi)發(fā)包括修復(fù)一些bug,大概用了不連續(xù)的2個(gè)月,想看源代碼的童鞋可以先到 RadialMenu?查看效果和代碼。

先放上項(xiàng)目里面的最終效果

下面說(shuō)下整個(gè)的過(guò)程

1.布局

首先,可以看到這個(gè)控件一個(gè)圓盤(pán)形狀的東東,在點(diǎn)擊子菜單的時(shí)候,菜單變化,并且?guī)в衐uangduangduang的特效。

先來(lái)看看RadialMenu的ControlTemplate

<ControlTemplate TargetType="local:RadialMenu"><!--<Popup x:Name="Popup" IsLightDismissEnabled="False" IsOpen="{TemplateBinding IsOpen}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">--><Grid x:Name="Root" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"><Grid x:Name="ContentGrid"><Ellipse Fill="{TemplateBinding Background}" StrokeThickness="{TemplateBinding ExpandAreaThickness}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/><local:RadialMenuItemsPresenter x:Name="CurrentItemPresenter" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"ItemsSource="{Binding CurrentItem.Items,RelativeSource={RelativeSource TemplatedParent}}" ><local:RadialMenuItemsPresenter.ItemsPanel><ItemsPanelTemplate><local:RadialMenuPanel/></ItemsPanelTemplate></local:RadialMenuItemsPresenter.ItemsPanel></local:RadialMenuItemsPresenter></Grid><local:RadialMenuNavigationButton x:Name="NavigationButton" Width="{TemplateBinding RadialMenuNavigationButtonSize}" Height="{TemplateBinding RadialMenuNavigationButtonSize}" VerticalAlignment="Center" HorizontalAlignment="Center"/></Grid><!--</Popup>--></ControlTemplate> View Code

一共三個(gè)東西,

1)Ellipse -整個(gè)RadialMenu的外形以及背景

2)RadialMenuItemsPresenter-用來(lái)展示各個(gè)菜單

3)RadialMenuNavigationButton-中間那個(gè)導(dǎo)航的按鈕

1,3應(yīng)該比較好理解,大家看看代碼就能明白。

我著重講下2,這個(gè)也是開(kāi)發(fā)一個(gè)自定義控件比較重要的操作,就是知道怎么按自己的想法去布局。

RadialMenuItemsPresenter 繼承ItemsControl,它的ItemsPanel是RadialMenuPanel。

<local:RadialMenuItemsPresenter x:Name="CurrentItemPresenter"ItemsSource="{Binding CurrentItem.Items,RelativeSource={RelativeSource TemplatedParent}}" ><local:RadialMenuItemsPresenter.ItemsPanel><ItemsPanelTemplate><local:RadialMenuPanel/></ItemsPanelTemplate></local:RadialMenuItemsPresenter.ItemsPanel></local:RadialMenuItemsPresenter>

RadialMenuPanel 就是我們最重要控制怎么展示RadialMenuItemsPresenter 的Items。

重寫(xiě)過(guò)的Panel 同學(xué)一定知道MeasureOverride ,ArrangeOverride 這2個(gè)東西。如果不清楚的一定要去看看葡萄城控件技術(shù)團(tuán)隊(duì)的2篇文章?Measure,Arrange??催^(guò)之后你將對(duì)這2個(gè)方法有新的了解,對(duì)控件的布局更加清晰。

下面請(qǐng)看我優(yōu)(丑)美(陋)的手畫(huà)圖

?

A菜單將它Arrange到圖中位置,B菜單也放到同樣的位置,但是給它做一定的旋轉(zhuǎn)。按做這種原理,把全部的菜單都Arrange到控件的中上位置,并且給他們都做一定的旋轉(zhuǎn),這樣就組成了整個(gè)的圓弧,這個(gè)遞增的角度就是 360°除以菜單的個(gè)數(shù)。

具體的代碼,請(qǐng)看ArrangeChildren方法。

?

2.每個(gè)菜單的設(shè)計(jì)

RadialMenuItem -最基礎(chǔ)的顯示文字,圖片等等

RadialColorMenuItem-用于顯示顏色

RadialNumericMenuItem-用于顯示數(shù)字,它的子集是RadialNumericMenuChildrenItem,是一個(gè)內(nèi)部的類(lèi)。

下來(lái)還是從我優(yōu)(丑)美(陋)的手畫(huà)圖開(kāi)始介紹結(jié)構(gòu)吧

藍(lán)色的線(xiàn)是整個(gè)item的最外邊,黃色是展開(kāi)子菜單的按鈕的中線(xiàn)。

從藍(lán)色到紅色部分就是整個(gè)展開(kāi)子菜單按鈕。?就是圖中的藍(lán)色的那個(gè)部分。

那么我們?nèi)绾蝸?lái)做出這種效果呢。這里我們要用到Path。

可以看到我們只需要把Path的StorkeThickness設(shè)置為展開(kāi)按鈕的寬,而Path的位置為我們手繪圖中的黃色那條線(xiàn)。寬為藍(lán)色到紅色線(xiàn)之間。就可以了。代碼中具體在RadialMenuPanel.cs 中的169行。就是整個(gè)圓的半徑減去展開(kāi)區(qū)域的寬度的一半。

var expandAreaRadius = radius - Menu.ExpandAreaThickness / 2.0;

這個(gè)半徑就是ArcSegment的Size,那么StartPoint和ArcSegment的Point怎么計(jì)算呢。

再來(lái)上我的手繪圖,這次真是手繪的了。。

如上圖,知道了半徑,可以看到里面有個(gè)直角三角形,一個(gè)邊是r,一個(gè)邊是x,一個(gè)邊是y,由于我們可以算出每個(gè)item占的角度,那么這個(gè)直角三角形的角就等于item 角度的一半,使用勾股定理(話(huà)說(shuō)最近項(xiàng)目里面還用到很多幾何知識(shí),還好沒(méi)有把知識(shí)還老師)可以算出x,y。

那么StartPoint= (item的寬度-x,item的高度-y)。因?yàn)榱硪粋€(gè)點(diǎn)是對(duì)稱(chēng)的。。不能算出Point=(item的寬度+x,item的高度-y)

那么我們就可以畫(huà)出這個(gè)圓弧了。。

同理RadialColorMenuItem ,那個(gè)色塊跟擴(kuò)展子菜單按鈕部分一樣。在代碼中具體在RadialMenuPanel.cs 中的175-177行

var colorElementStrokeThickness = radius - Menu.ExpandAreaThickness - Menu.SelectedElementThickness - navigationButtonSize * 0.5;var colorElementRadius = radius - Menu.ExpandAreaThickness - Menu.SelectedElementThickness - colorElementStrokeThickness / 2.0;

這里說(shuō)明下,色塊跟擴(kuò)展子菜單按鈕部分之間還有一個(gè)選中效果的色塊。

RadialNumericMenuItem?

它的子集是RadialNumericMenuChildrenItem

RadialNumericMenuChildrenItem ?的選中效果和鼠標(biāo)懸停效果是2條Line,代碼中具體在RadialMenuPanel.cs 中的180-197行

3.動(dòng)畫(huà)效果

這篇是Composition API,當(dāng)然要用Composition API來(lái)做動(dòng)畫(huà)呢。。哈哈。

先來(lái)說(shuō)展開(kāi)/收縮的動(dòng)畫(huà)

_contentGridVisual = ElementCompositionPreview.GetElementVisual(_contentGrid);_compositor = _contentGridVisual.Compositor;rotationAnimation = _compositor.CreateScalarKeyFrameAnimation();scaleAnimation = _compositor.CreateVector3KeyFrameAnimation();var easing = _compositor.CreateLinearEasingFunction();_contentGrid.SizeChanged += (s, e) =>{_contentGridVisual.CenterPoint = new Vector3((float)_contentGrid.ActualWidth / 2.0f, (float)_contentGrid.ActualHeight / 2.0f, 0);};scaleAnimation.InsertKeyFrame(0.0f, new Vector3() { X = 0.0f, Y = 0.0f, Z = 0.0f });scaleAnimation.InsertKeyFrame(1.0f, new Vector3() { X = 1.0f, Y = 1.0f, Z = 0.0f }, easing);rotationAnimation.InsertKeyFrame(0.0f, -90.0f);rotationAnimation.InsertKeyFrame(1.0f, 0.0f, easing);

可以看到我準(zhǔn)備2個(gè)動(dòng)畫(huà),一個(gè)是旋轉(zhuǎn)一個(gè)縮小放大,而他們的作用題是RadialMenu的中ContentGrid(不包括中心的那個(gè)導(dǎo)航按鈕)

在10586版本之上,動(dòng)畫(huà)有Direction這個(gè)屬性,可以反轉(zhuǎn)動(dòng)畫(huà)效果,也就是說(shuō),我這里寫(xiě)2個(gè)動(dòng)畫(huà),就可以做到展開(kāi)/收縮

有沒(méi)有覺(jué)得很簡(jiǎn)單。。為啥這里特別提10586呢。因?yàn)楹竺嬗锌印?/p>

?

再說(shuō)說(shuō)切換菜單效果,就是點(diǎn)了某個(gè)子菜單的展開(kāi)按鈕,切到它對(duì)應(yīng)的菜單去的效果。其實(shí)更簡(jiǎn)單,直接用上面的scaleAnimation就可以。不一樣的是我們使用了下面的代碼來(lái)讓菜單先收,再開(kāi)。

var batch = _compositor.GetCommitBatch(CompositionBatchTypes.Animation);batch.Completed += (s, e) =>{SetCurrentItemIn(currentItem);scaleAnimation.Duration = TimeSpan.FromSeconds(0.1);if (!lowerThan14393){scaleAnimation.Direction = AnimationDirection.Normal;}_contentGridVisual.StartAnimation(nameof(_contentGridVisual.Scale), scaleAnimation);};scaleAnimation.Duration = TimeSpan.FromSeconds(0.07);scaleAnimation.Direction = AnimationDirection.Reverse;_contentGridVisual.StartAnimation(nameof(_contentGridVisual.Scale), scaleAnimation);

CompositionCommitBatch 有一個(gè)completed事件,這個(gè)就是動(dòng)畫(huà)結(jié)束時(shí)候觸發(fā)的事件。

另外發(fā)現(xiàn)CompositionScopedBatch 也completed事件。

先看看官方介紹CompositionCommitBatch,?CompositionScopedBatch

public void BatchAnimations() {// Create a Scoped batch to capture animation completion events_batch = _compositor.CreateScopedBatch(CompositionBatchTypes.Animation);// Executing the Offset animation and aggregating completion event ApplyOffsetAnimation(_greenSquare);// Suspending to exclude the following Rotation animation from the batch _batch.Suspend();// Executing the Rotation animation ApplyRotationAnimation(_greenSquare);// Resuming the batch to collect additional animations _batch.Resume();// Executing the Opacity animation and aggregating completion event ApplyOpacityAnimation(_greenSquare);// Batch is ended and no objects can be added _batch.End();// Method triggered when batch completion event fires_batch.Completed += OnBatchCompleted; }

可以看到CompositionScopedBatch 多了3個(gè)方法Resume,Suspend,End,也就是說(shuō)你可以暫停動(dòng)畫(huà),并且在這個(gè)時(shí)間段加入新的動(dòng)畫(huà)到這個(gè)group里面,然后啟動(dòng),最后每個(gè)動(dòng)畫(huà)完成的時(shí)候都會(huì)觸發(fā)complete。

最后是整個(gè)RadialMenu的交互。就是移動(dòng)動(dòng)畫(huà)。代碼太簡(jiǎn)單了。

_radialMenuVisual = ElementCompositionPreview.GetElementVisual(this);_radialMenuVisual.Offset = Offset;

移動(dòng)的時(shí)候處理_radialMenuVisual的Offset就可以了。。哈哈。(注意我沒(méi)有把RadialIMenu的元素放在一個(gè)Popup里面,原因是需要滿(mǎn)足,點(diǎn)擊其他地方的時(shí)候不要關(guān)閉RadialMenu。如果Popup的IsLightDismissEnabled 的屬性設(shè)置為flase的話(huà),其他地方點(diǎn)擊又不會(huì)有事件觸發(fā)?,F(xiàn)在的方案是把這個(gè)控件總是放在最高的level 層)

因?yàn)樵O(shè)置了控件的ManipulationMode

ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY | ManipulationModes.TranslateInertia;

那么我們可以在OnManipulationDelta 方法中去改變Offset。達(dá)到移動(dòng)整個(gè)控件的目的

protected override void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e){UpdateOffset(e.Delta.Translation.X, e.Delta.Translation.Y, e.IsInertial);}

這里當(dāng)有慣性的時(shí)候我做了反彈效果,具體的代碼在RadilaMenu的BounceOffset 方法中。

?

不要以為這樣控件就搞定了,坑坑坑。在等著你。。

第一個(gè)地方:

之前講了在10586上面。沒(méi)有反轉(zhuǎn)動(dòng)畫(huà)這個(gè)屬性,而且我發(fā)現(xiàn)10586上面動(dòng)畫(huà)簡(jiǎn)直是垃圾。各種掉幀,各種失效,但是項(xiàng)目里面要支持10586,那我們?cè)趺崔k呢,嗯。別忘記了我們還有Storyboard。我們需要4個(gè)動(dòng)畫(huà)

//use animation for lower than 14393 Storyboard expand;Storyboard collapse;Storyboard open;Storyboard close;

準(zhǔn)備動(dòng)畫(huà)代碼如下,說(shuō)實(shí)話(huà),確實(shí)有點(diǎn)麻煩難懂,這就是我為啥一直推薦使用Composition API的其中一個(gè)原因吧。

_contentGrid.RenderTransformOrigin = new Point(0.5, 0.5);_contentGrid.RenderTransform = new CompositeTransform();#region expandexpand = new Storyboard();var duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleX)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 0 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = 1, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });expand.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleY)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 0 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = 1, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });expand.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.Rotation)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = -90 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = 0, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });expand.Children.Add(duk);#endregion#region collapsecollapse = new Storyboard();duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleX)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 1 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = 0, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });collapse.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleY)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 1 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = 0, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });collapse.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.Rotation)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 0 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2)), Value = -90, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });collapse.Children.Add(duk);#endregion#region Openopen = new Storyboard();duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleX)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 0 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.1)), Value = 1, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });open.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleY)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 0 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.1)), Value = 1, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });open.Children.Add(duk);#endregion#region Closeclose = new Storyboard();duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleX)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 1 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.07)), Value = 0, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });close.Children.Add(duk);duk = new DoubleAnimationUsingKeyFrames();Storyboard.SetTarget(duk, _contentGrid);Storyboard.SetTargetProperty(duk, "(UIElement.RenderTransform).(CompositeTransform.ScaleY)");duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)), Value = 1 });duk.KeyFrames.Add(new EasingDoubleKeyFrame() { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.07)), Value = 0, EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut } });close.Children.Add(duk);#endregion View Code

第二個(gè)地方:

為了實(shí)現(xiàn)RadialNumericMenuChildrenItem 第一個(gè)和最后一個(gè)Item缺掉一個(gè)口的效果,如下圖

我為這個(gè)Path做了一個(gè)Clip。問(wèn)題在于15063版本上面Path的Clip是針對(duì)它實(shí)際圖形位置,而不是Path實(shí)際的大小(從之前手繪圖看錯(cuò),實(shí)際大小是那個(gè)外面的矩形,而且它繪畫(huà)的圖像是小于它的)。所以做了下面的特殊處理

var Build16229 = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 5);// var Build15063 = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 4);//var Build14393 = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3);//var Build10586 = ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 2);bool is15063 = Build15063 && !Build16229;if (!is15063){if (k == 0){radialNumericMenuChildrenItem.ColorElement.Clip = new RectangleGeometry() { Rect = new Rect(sectorRect.Width / 2.0, 0, sectorRect.Width / 2.0, sectorRect.Height) };}else{radialNumericMenuChildrenItem.ColorElement.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, sectorRect.Width / 2.0, sectorRect.Height) };}}else{if (k == 0){radialNumericMenuChildrenItem.ColorElement.Clip = new RectangleGeometry() { Rect = new Rect((colorElement.EndPoint.X - colorElement.StartPoint.X) / 2.0, 0, colorElement.EndPoint.X - colorElement.StartPoint.X, colorElement.Size.Height) };}else{radialNumericMenuChildrenItem.ColorElement.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, (colorElement.EndPoint.X - colorElement.StartPoint.X) / 2.0, colorElement.Size.Height) };}} View Code

?

第三個(gè)地方:

RadialNumericMenuItem 的Items之前設(shè)計(jì)是使用New關(guān)鍵字直接重寫(xiě)了子類(lèi)的Items(RadialMenuItemCollection),為ObservableCollection<double>

在10586 ?release 模式上面RadialMenuPanel 的Children.Count會(huì)為一個(gè)奇怪的負(fù)數(shù),而不是0.

修改方案之后 增加了一個(gè)ObservableCollection<double> NumericItems 屬性來(lái)設(shè)置數(shù)

并且使用?[ContentProperty(Name = "NumericItems")] 屬性標(biāo)簽覆蓋子類(lèi),

但是哇在最新的SDK上面又有問(wèn)題了。。debug模式下編譯能過(guò)。但是運(yùn)行起來(lái)ContentProperty 沒(méi)有生效不通過(guò)。

最后也沒(méi)啥辦法了。。那就是在xaml里面老實(shí)實(shí) 寫(xiě)NumericItems等于啥吧。。( ╯□╰ )

?

第四個(gè)地方

居然是virtual 的Offset 在Page的 NavigationCacheMode 等于enable 或者request的時(shí)候。有大大大的問(wèn)題啊。微軟來(lái)背鍋啊。

問(wèn)題是這樣的。Radilamenu放在一個(gè)Page?NavigationCacheMode =Enable的頁(yè)面上。。然后跳轉(zhuǎn)到其他頁(yè)面再跳轉(zhuǎn)回來(lái)的時(shí)候。

Virtual.Offset 的還是原來(lái)的值。而且整個(gè)RadialMenu看起來(lái)也像在原來(lái)的位置。。但是其實(shí)你用鼠標(biāo)或者觸摸的時(shí)候。發(fā)現(xiàn)RadialMenu觸發(fā)事件的位置居然是在左上角。就是說(shuō)是Offset為0的時(shí)候那個(gè)區(qū)域。。不管用了什么辦法。還是無(wú)解。

最后只能放棄使用Virtual.Offset ,然后改為控制控件Popup的HorizontalOffset和VerticalOffset來(lái)移動(dòng)。

?

第五個(gè)地方:

之前Popup放ControlTemplate里面,這會(huì)導(dǎo)致RadilamMenu只是在你放那個(gè)父容器那一層是最上層。而一般都需要RadialMenu在整個(gè)應(yīng)用的最上層。

辦法只有一個(gè)不要將Popup放進(jìn)去virtual tree,而且是在代碼中new 一個(gè)出來(lái),這樣微軟會(huì)把這個(gè)Popup的Child放進(jìn)PopupRoot,這個(gè)東東是整個(gè)應(yīng)用的最上層,如下圖。

PopupRoot為應(yīng)用的最上層

RootScrollViewer-ScrollContentPresenter-Border-Frame-ContentPresenter 這里就是平時(shí)大家熟悉的MainPage

不知道咋哪里查看這個(gè)的。請(qǐng)打開(kāi)VS,運(yùn)行UWP程序,看到中間那坨黑色的地方嗎,點(diǎn)第一個(gè)VS的左邊就會(huì)出現(xiàn)Live Vistual Tree,通過(guò)這個(gè)你能查看到結(jié)構(gòu),某個(gè)控件的當(dāng)前屬性,或者設(shè)置它的屬性,反正很叼了。學(xué)習(xí)控件的孩子一定要搞懂哈。

注意不要將RadilaMenu直接放進(jìn)Virtual tree,因?yàn)镽adialMenu是準(zhǔn)備放在new 的Popup里面的,大家都知道一個(gè)UIElement不能賦值給2個(gè)Parent的。

那么告訴大家一個(gè)技巧??梢杂肁ttachedProperty將RadialMenu加入到Xaml但是不放進(jìn)Virtual tree。這樣我們就能在Xaml中開(kāi)心的寫(xiě)了。

如Sample 中RadialMenuBase 類(lèi)的AttachedMenu屬性。

<radialMenu:RadialMenuBase.AttachedMenu><radialMenu:RadialMenu x:Name="radialMenu" Offset="100,100" SectorCount="8" IsExpanded="False"><radialMenu:RadialMenuItem Content="Color" ToolTip="Color"><radialMenu:RadialColorMenuItem Color="Red"/></radialMenu:RadialMenuItem><radialMenu:RadialNumericMenuItem x:Name="radialNumericMenuItem" Value="6"><radialMenu:RadialNumericMenuItem.Content><TextBlock><Run Text="Fontsize"/><Run Text="("/><Run Text="{Binding Value,ElementName=radialNumericMenuItem}"/><Run Text=")"/></TextBlock></radialMenu:RadialNumericMenuItem.Content><radialMenu:RadialNumericMenuItem.NumericItems><x:Double>1</x:Double><x:Double>2</x:Double><x:Double>3</x:Double><x:Double>4</x:Double><x:Double>5</x:Double><x:Double>6</x:Double><x:Double>7</x:Double><x:Double>8</x:Double><x:Double>9</x:Double><x:Double>10</x:Double><x:Double>11</x:Double><x:Double>12</x:Double><x:Double>13</x:Double><x:Double>14</x:Double><x:Double>15</x:Double><x:Double>16</x:Double></radialMenu:RadialNumericMenuItem.NumericItems></radialMenu:RadialNumericMenuItem><radialMenu:RadialMenuItem Content="Disabled" IsEnabled="True"><radialMenu:RadialMenuItem Content="test"/></radialMenu:RadialMenuItem></radialMenu:RadialMenu></radialMenu:RadialMenuBase.AttachedMenu>

最后最后,這個(gè)控件里面用到一些屬性標(biāo)簽 比如

[ContentProperty(Name = "NumericItems")]

[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]

這些東西跟Design Time有很大大的關(guān)系,就是你平時(shí)寫(xiě)在Xaml里面的東東。要是有不清楚的。可以留言或者私信我,歡迎投雞蛋。

最后想說(shuō)。要做好一個(gè)控件這些是遠(yuǎn)遠(yuǎn)不夠的,還會(huì)涉及控件的 designtime,各種邏輯,各種版本適配,各種擴(kuò)展,一個(gè)好的控件絕對(duì)等同一個(gè)復(fù)雜的項(xiàng)目。我喜歡做控件。希望給大家使用降低學(xué)習(xí)成本。

謝謝堅(jiān)持看完的童鞋,整個(gè)控件研發(fā)的過(guò)程遠(yuǎn)遠(yuǎn)不止這篇文章,歡迎提問(wèn)。

老規(guī)矩 開(kāi)源有益:RadialMenu?

想在10240上面使用這個(gè)控件的童鞋,可以把關(guān)于Composition API的代碼都注釋掉。使用StoryBoard來(lái)做動(dòng)畫(huà)就ok了。(是不是感覺(jué)我強(qiáng)行為Composition API 打Call,哈哈哈)

?

總結(jié)

以上是生活随笔為你收集整理的UWP Composition API - RadialMenu的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。