Surface Dial 與 Windows Wheel UWP應用開發

来源:http://www.cnblogs.com/sonic1abc/archive/2017/02/09/6382545.html
-Advertisement-
Play Games

隨著微軟發佈 Surface Studio 在演示視頻中非常搶眼的一個配件就是 Surface Dial,Dial 是Windows輸入設備大家庭中的新成員我們把它歸類為Windows Wheel 類型設備。今天為大家介紹一下如何配合這個神奇設備開發自己的應用。 ...


隨著微軟發佈 Surface Studio 在演示視頻中非常搶眼的一個配件就是 Surface Dial,Dial 是Windows輸入設備大家庭中的新成員我們把它歸類為Windows Wheel 類型設備。今天為大家介紹一下如何配合這個神奇設備開發自己的應用。

Dial 是一個類似於滾輪的設備,可以協助用戶更快的想電腦輸入信息,但是Dial並不是一個專為精準輸入所設計的設備,換句話說如果用戶想點擊屏幕上某一個點我們還是推薦用戶使用手指觸摸或者Surface Pan 或者滑鼠完成這項工作,Dial更適合類似於畫布的旋轉,筆觸的選擇等快速的操作。

Surface Dial操作非常簡單,只有三個操作事件 長按單擊,以及 轉動並且如果Surface Dial 與 Surface Studio 一起使用的話 Surface Dial 的菜單可以使用 Surface Dial的屏幕菜單,就是將 Dial 放在 Surface Studio 屏幕上隨後通過對Surface Dial的操作,圍繞Dial的屏幕周圍會出現一個菜單。

Dial 自身與windows銜接緊密並且內置系統工具例如調整系統音量,大小縮小,以及撤銷從做等功能。此外Dial 與 Windows Ink集成也是非常緊密。如果您的WUP應用已經使用了 InkCanvas 和 InkToolbar 那麼 Dial就會自動和 InkToolbar中的內容相結合例如 調整標尺的角度和調整筆觸的大小等功能。

然而對於我們開發者而言 Surface 也是為我們提供了API(這裡分別有UWP 和 Win32版本,本文著重介紹UWP版本),其實開發起來也是非常簡單。

首先關鍵點 

RadialController 類,通過CreateForCurrentView()靜態方法獲取實例。

RadialControllerMenuItem 類,用來自定義菜單內容。當Dial長按時會彈出自定義菜單。

ButtonClicked 事件,用來捕捉Dial的點擊事件。

RotationChanged 事件,用來捕捉Dial旋轉事件。

ScreenContactStarted 事件,捕捉 Dial 放在了 Surface Studio 上事件,並且可以從回調參數中獲取Dial在屏幕中擺放的位置。

ScreenContactContinued 事件,捕捉 Dial 放在了 Surface Studio 上並且移動了位置,並且可以從回調參數中獲取Dial在屏幕中擺放的位置。

ScreenContactEnded 事件,捕捉 Dial 從 Surface Studio 上移開事件。

ControlLost 事件,捕捉操作的焦點離開。

 

照搬一個MSDN上的demo code比較直觀的展示這幾個事件的用法以及如何判斷Dial在Surface Studio上的位置

Xaml代碼

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
 <RowDefinition Height="Auto"/>
 <RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" 
     Orientation="Horizontal" 
     Grid.Row="0">
 <TextBlock x:Name="Header"
   Text="RadialController customization sample"
   VerticalAlignment="Center"
   Style="{ThemeResource HeaderTextBlockStyle}"
   Margin="10,0,0,0" />
</StackPanel>
<Grid Grid.Row="1" x:Name="RootGrid">
 <Grid.RowDefinitions>
   <RowDefinition Height="*"/>
   <RowDefinition Height="*"/>
 </Grid.RowDefinitions>
 <Grid.ColumnDefinitions>
   <ColumnDefinition Width="*"/>
   <ColumnDefinition Width="*"/>
 </Grid.ColumnDefinitions>
 <Grid x:Name="Grid0"
   Grid.Row="0"
   Grid.Column="0">
   <StackPanel Orientation="Vertical" 
     VerticalAlignment="Center" 
     HorizontalAlignment="Center">
     <!-- Slider for rotational input -->
     <Slider x:Name="RotationSlider0"
       Width="300"
       HorizontalAlignment="Left"/>
     <!-- Switch for button input -->
     <ToggleSwitch x:Name="ButtonToggle0"
         HorizontalAlignment="Left"/>
   </StackPanel>
 </Grid>
 <Grid x:Name="Grid1"
   Grid.Row="0"
   Grid.Column="1">
   <StackPanel Orientation="Vertical" 
     VerticalAlignment="Center" 
     HorizontalAlignment="Center">
     <!-- Slider for rotational input -->
     <Slider x:Name="RotationSlider1"
       Width="300"
       HorizontalAlignment="Left"/>
     <!-- Switch for button input -->
     <ToggleSwitch x:Name="ButtonToggle1"
         HorizontalAlignment="Left"/>
   </StackPanel>
 </Grid>
 <Grid x:Name="Grid2"
   Grid.Row="1"
   Grid.Column="0">
   <StackPanel Orientation="Vertical" 
     VerticalAlignment="Center" 
     HorizontalAlignment="Center">
     <!-- Slider for rotational input -->
     <Slider x:Name="RotationSlider2"
       Width="300"
       HorizontalAlignment="Left"/>
     <!-- Switch for button input -->
     <ToggleSwitch x:Name="ButtonToggle2"
         HorizontalAlignment="Left"/>
   </StackPanel>
 </Grid>
 <Grid x:Name="Grid3"
   Grid.Row="1"
   Grid.Column="1">
   <StackPanel Orientation="Vertical" 
     VerticalAlignment="Center" 
     HorizontalAlignment="Center">
     <!-- Slider for rotational input -->
     <Slider x:Name="RotationSlider3"
       Width="300"
       HorizontalAlignment="Left"/>
     <!-- Switch for button input -->
     <ToggleSwitch x:Name="ButtonToggle3"
         HorizontalAlignment="Left"/>
   </StackPanel>
 </Grid>
</Grid>
</Grid>

code-behind 代碼

Slider ActiveSlider;
ToggleSwitch ActiveSwitch;
Grid ActiveGrid;

public MainPage()
{
  ...

  myController.ScreenContactStarted += 
    MyController_ScreenContactStarted;
  myController.ScreenContactContinued += 
    MyController_ScreenContactContinued;
  myController.ScreenContactEnded += 
    MyController_ScreenContactEnded;
  myController.ControlLost += MyController_ControlLost;

  //Set initial grid for Surface Dial input.
  ActiveGrid = Grid0;
  ActiveSlider = RotationSlider0;
  ActiveSwitch = ButtonToggle0;
}

private void MyController_ScreenContactStarted(RadialController sender, 
  RadialControllerScreenContactStartedEventArgs args)
{
  //find grid at contact location, update visuals, selection
  ActivateGridAtLocation(args.Contact.Position);
}

private void MyController_ScreenContactContinued(RadialController sender, 
  RadialControllerScreenContactContinuedEventArgs args)
{
  //if a new grid is under contact location, update visuals, selection
  if (!VisualTreeHelper.FindElementsInHostCoordinates(
    args.Contact.Position, RootGrid).Contains(ActiveGrid))
  {
    ActiveGrid.Background = new 
      SolidColorBrush(Windows.UI.Colors.White);
    ActivateGridAtLocation(args.Contact.Position);
  }
}

private void MyController_ScreenContactEnded(RadialController sender, object args)
{
  //return grid color to normal when contact leaves screen
  ActiveGrid.Background = new 
  SolidColorBrush(Windows.UI.Colors.White);
}

private void MyController_ControlLost(RadialController sender, object args)
{
  //return grid color to normal when focus lost
  ActiveGrid.Background = new 
    SolidColorBrush(Windows.UI.Colors.White);
}

private void ActivateGridAtLocation(Point Location)
{
  var elementsAtContactLocation = 
    VisualTreeHelper.FindElementsInHostCoordinates(Location, 
      RootGrid);

  foreach (UIElement element in elementsAtContactLocation)
  {
    if (element as Grid == Grid0)
    {
      ActiveSlider = RotationSlider0;
      ActiveSwitch = ButtonToggle0;
      ActiveGrid = Grid0;
      ActiveGrid.Background = new SolidColorBrush( 
        Windows.UI.Colors.LightGoldenrodYellow);
      return;
    }
    else if (element as Grid == Grid1)
    {
      ActiveSlider = RotationSlider1;
      ActiveSwitch = ButtonToggle1;
      ActiveGrid = Grid1;
      ActiveGrid.Background = new SolidColorBrush( 
        Windows.UI.Colors.LightGoldenrodYellow);
      return;
    }
    else if (element as Grid == Grid2)
    {
      ActiveSlider = RotationSlider2;
      ActiveSwitch = ButtonToggle2;
      ActiveGrid = Grid2;
      ActiveGrid.Background = new SolidColorBrush( 
        Windows.UI.Colors.LightGoldenrodYellow);
      return;
    }
    else if (element as Grid == Grid3)
    {
      ActiveSlider = RotationSlider3;
      ActiveSwitch = ButtonToggle3;
      ActiveGrid = Grid3;
      ActiveGrid.Background = new SolidColorBrush( 
        Windows.UI.Colors.LightGoldenrodYellow);
      return;
    }
  }
}

當然還有一個關鍵點如何創建自定義的菜單 使用Controller.Menu.Items.Add()方法添加和使用 Remove()方法刪除自定義菜單。

註意:這裡Surface Dial 菜單可以容納7個選項,如果超過7個那麼Dial 需要有浮動控制項配合選擇,這樣做會影響用戶體驗是不推薦的。

        private void CreateMenuItems()
        {
            menuItems = new List<RadialControllerMenuItem>
            {
                RadialControllerMenuItem.CreateFromKnownIcon("Item0", RadialControllerMenuKnownIcon.InkColor),
                RadialControllerMenuItem.CreateFromKnownIcon("Item1", RadialControllerMenuKnownIcon.NextPreviousTrack),
                RadialControllerMenuItem.CreateFromKnownIcon("Item2", RadialControllerMenuKnownIcon.Volume),
                RadialControllerMenuItem.CreateFromIcon("Item3", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item3.png"))),
                RadialControllerMenuItem.CreateFromIcon("Item4", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item4.png"))),
                RadialControllerMenuItem.CreateFromIcon("Item5", RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/Item5.png")))
            };
            sliders = new List<Slider> { Slider0, Slider1, Slider2, Slider3, Slider4, Slider5 };
            toggles = new List<ToggleSwitch> { Toggle0, Toggle1, Toggle2, Toggle3, Toggle4, Toggle5 };

            for (int i = 0; i < menuItems.Count; ++i)
            {
                RadialControllerMenuItem radialControllerItem = menuItems[i];
                int index = i;

                radialControllerItem.Invoked += (sender, args) => { OnItemInvoked(index); };
            }
        }

        private void OnItemInvoked(int selectedItemIndex)
        {
            activeItemIndex = selectedItemIndex;
        }

        private void AddItem(object sender, RoutedEventArgs e)
        {
            RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender);

            if (!Controller.Menu.Items.Contains(radialControllerMenuItem))
            {
                Controller.Menu.Items.Add(radialControllerMenuItem);
            }
        }

        private void RemoveItem(object sender, RoutedEventArgs e)
        {
            RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender);

            if (Controller.Menu.Items.Contains(radialControllerMenuItem))
            {
                Controller.Menu.Items.Remove(radialControllerMenuItem);
            }
        }

        private void SelectItem(object sender, RoutedEventArgs e)
        {
            RadialControllerMenuItem radialControllerMenuItem = GetRadialControllerMenuItemFromSender(sender);

            if (Controller.Menu.Items.Contains(radialControllerMenuItem))
            {
                Controller.Menu.SelectMenuItem(radialControllerMenuItem);
                PrintSelectedItem();
            }
        }

 菜單選項推薦使用64x64像素的PNG透明圖像即可,但是同時支持到44x44的最小像素值。

註意:黑色邊框是為了在高對比度模式下也可以讓我們的圖標也可以清晰可見。

 

最後附上微軟的示例代碼:RadialControlle

希望上的總結可以幫助到大家, 同時歡迎大家在這裡和我溝通交流或者在新浪微博上 @王博_Nick

 


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

-Advertisement-
Play Games
更多相關文章
  • 5. 完整的自定義依賴屬性 5.1 定義 以上代碼為一個相對完成的依賴屬性例子(還有一些功能比較少用就不寫出了),從這段代碼可以看出,自定義依賴屬性的步驟如下: 1. 註冊依賴屬性並生成依賴屬性標識符。依賴屬性標識符為一個public static readonly DependencyProper ...
  • //關閉模態框 window.parent.$('#myModal').modal('hide'); //修改成功後刷新table表格 window.parent.$('#reportTable').bootstrapTable('refresh'); ...
  • 前臺代碼 <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" > <div class="modal-dialog" st ...
  • 經過幾天學習,寫出了一個簡單的winform應用程式,貼出源碼,以備不時之需。 軟體啟動後的界面如下圖所示: 如圖,該程式由6個label、8個comboBox、8個textBox和4個button組成。右邊4個textBox設置ReadOnly屬性為true。 軟體啟動時,可以讓comboBox顯 ...
  • 1、啥是RESTFul 服務 在我們創建簡單小程式前,先來學習下RESTFul 服務。RESTFul服務就是遵循了 Representational State Transfer(可以參考http://blog.csdn.net/zhruifei/article/details/50633495) ...
  • 1.nuget中添加包EF和MySql.Data.Entity 2.config文件添加如下配置 1.配置entitframework節點(一般安裝EF時自動添加) 2.配置system.data節點(一般安裝MySql.Data.Entity時自動添加) 3.添加連接串節點(以實際情況修改庫名、密 ...
  • 前言 當線程池的線程阻塞時,線程池會創建額外的線程,而創建、銷毀和調度線程所需要相當昂貴的記憶體資源,另外,很多的開發人員看見自己程式的線程沒有做任何有用的事情時習慣創建更多的線程,為了構建可伸縮、響應靈敏的程式,我們在前面介紹了線程也瘋狂 非同步編程。 但是非同步編程同樣也存在著很嚴重的問題,如果兩個不 ...
  • C 互操作的類型基本位於System.Runtime.InteropServices命名空間下,本系列隨筆主要記錄本人在開發過程中使用的到一些類型函數、技巧及工具 計算類型的大小 獲取類型實例的大小 結構體與指針類型互相轉換 C/C++ 數據結構轉C 結構體工具 工具源碼及下載地址: "http:/ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...