New UWP Community Toolkit - RangeSelector

来源:https://www.cnblogs.com/shaomeng/archive/2018/04/03/8678687.html
-Advertisement-
Play Games

概述 前面 New UWP Community Toolkit 文章中,我們對 V2.2.0 版本的重要更新做了簡單回顧,其中簡單介紹了 RangeSelector,本篇我們結合代碼詳細講解一下 RangeSelector 相關功能。 RangeSelector 是一種範圍選擇控制項,有兩個滑塊控制項, ...


概述

前面 New UWP Community Toolkit 文章中,我們對 V2.2.0 版本的重要更新做了簡單回顧,其中簡單介紹了 RangeSelector,本篇我們結合代碼詳細講解一下 RangeSelector 相關功能。

RangeSelector 是一種範圍選擇控制項,有兩個滑塊控制項,允許用戶在控制項的取值範圍內選擇一個子區間範圍。在實際應用開發中 RangeSelector 也有著非常廣泛的應用,例如篩選時的價格區間選擇等等。我們來看一下官方示例中的展示:

Source: https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/RangeSelector

Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/rangeselector

Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;

 

開發過程

代碼分析

先來看看 RangeSelector 的結構組成:

  • RangeChangedEventArgs.cs - 範圍改變處理事件傳入的參數類,包含了 oldValue,newValue 和 ChangedRangeProperty(標誌 min 和 max 兩個區間值是否改變)
  • RangeSelector.cs - RangeSelector 的控制項定義和事件處理類
  • RangeSelector.xaml - RangeSelector 的樣式文件

下麵來看一下幾個主要類中的主要代碼實現,因為篇幅關係,我們只摘錄部分關鍵代碼實現:

1.  RangeSelector.xaml

 RangeSelector.xaml 是 RangeSelector 控制項的樣式文件,我們看到 Template 部分,由一個背景 Border OutOfRangeContentContainer,兩個選擇滑塊 MinThumb 和 MaxThumb,以及顯示當前範圍的矩形 ActiveRectangle 組成;再看 VisualStateManager,我們截取了一部分,在 MinPressed 發生時,MinThumb 被高亮顯示,同理其他狀態發生時也會有對應的視覺狀態發生。 

<Style TargetType="controls:RangeSelector">
...
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="controls:RangeSelector">
            <Grid x:Name="ControlGrid" Height="24" >
                <Grid.Resources>
                    <Style x:Key="SliderThumbStyle" TargetType="Thumb"> ... </Style>
                </Grid.Resources>

                <Border x:Name="OutOfRangeContentContainer" Background="Transparent"> ... </Border>
                <Canvas x:Name="ContainerCanvas"
                    Margin="0,0,8,0"
                    Background="Transparent">
                    <Rectangle x:Name="ActiveRectangle" Height="2" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" />
                    <Thumb x:Name="MinThumb" AutomationProperties.Name="Min thumb" IsTabStop="True" Style="{StaticResource SliderThumbStyle}" TabIndex="0" />
                    <Thumb x:Name="MaxThumb" AutomationProperties.Name="Max thumb" IsTabStop="True" Style="{StaticResource SliderThumbStyle}" TabIndex="1" />
                </Canvas>
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal" />
                        <VisualState x:Name="MinPressed">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MinThumb"
                                                            Storyboard.TargetProperty="Background">
                                    <DiscreteObjectKeyFrame KeyTime="0"
                                                        Value="{ThemeResource SystemControlHighlightChromeHighBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <!-- other visual state -->
                        ...
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>
</Style>

2. RangeSelector.cs

我們先看看 RangeSelector 類的組成: 

 

先來看看類中的依賴屬性:

  • Minimum - 控制項允許選擇範圍的最小值,預設是 0.0,修改時觸發 MinimumChangedCallback
  • Maximum - 控制項允許選擇範圍的最大值,預設是 1.0,修改時觸發 MaximumChangedCallback
  • RangeMin - 控制項實際選擇範圍的最小值,預設是 0.0,修改時觸發 RangeMinChangedCallback
  • RangeMax - 控制項實際選擇範圍的最大值,預設是 1.0,修改時觸發 RangeMaxChangedCallback
  • IsTouchOptimized - 觸摸優化的標誌,預設是 false,修改時觸發 IsTouchOptimizedChangedCallback
  • StepFrequency - 每次調整範圍時的步長,預設是 1.0

我們在其中挑出有代表性的方法詳細看一下:

① MinimumChangedCallback(d, e)

允許範圍最小值調整的回調方法,最大值對應的方法功能類似;當最小值調整後的 newValue 大於等於舊的最大值時,對最大值重新設置為 newValue + 0.01;當 newValue 大於等於實際範圍最小值時,把實際最小值設置為 newValue,當 newValue 大於等於實際範圍最大值時,把實際最大值也設置為 newValue;最後如果 newValue 小於 oldValue 時,需要同步滑塊的位置;

private static void MinimumChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var rangeSelector = d as RangeSelector;

    if (rangeSelector == null || !rangeSelector._valuesAssigned)
    {
        return;
    }

    var newValue = (double)e.NewValue;
    var oldValue = (double)e.OldValue;

    if (rangeSelector.Maximum < newValue)
    {
        rangeSelector.Maximum = newValue + Epsilon;
    }

    if (rangeSelector.RangeMin < newValue)
    {
        rangeSelector.RangeMin = newValue;
    }

    if (rangeSelector.RangeMax < newValue)
    {
        rangeSelector.RangeMax = newValue;
    }

    if (newValue < oldValue)
    {
        rangeSelector.SyncThumbs();
    }
}

② RangeMinChangedCallback(d, e)

實際範圍最小值調整的回調方法,最大值對應的方法功能類似;根據步長來對 newValue 做矯正,比如 oldValue = 0.0,newValue = 0.11,步長 0.1,那麼 newValue 會調整為 0.1;然後是對 newValue 超出允許選擇範圍時的邊界處理;最後實際選擇範圍修改時,需要同步調整顯示實際範圍的矩形控制項的狀態;

private static void RangeMinChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var rangeSelector = d as RangeSelector;

    if (rangeSelector == null)
    {
        return;
    }

    rangeSelector._minSet = true;

    if (!rangeSelector._valuesAssigned)
    {
        return;
    }

    var newValue = (double)e.NewValue;
    rangeSelector.RangeMinToStepFrequency();

    if (rangeSelector._valuesAssigned)
    {
        if (newValue < rangeSelector.Minimum)
        {
            rangeSelector.RangeMin = rangeSelector.Minimum;
            return;
        }

        if (newValue > rangeSelector.Maximum)
        {
            rangeSelector.RangeMin = rangeSelector.Maximum;
            return;
        }

        rangeSelector.SyncActiveRectangle();

        if (newValue > rangeSelector.RangeMax)
        {
            rangeSelector.RangeMax = newValue;
        }
    }
    else
    {
        rangeSelector.SyncActiveRectangle();
    }
}

③ IsTouchOptimizedChangedCallback(d, e)

當觸摸優化變化時,控制項也會做出變化,實際處理方法是 ArrangeForTouch();我們看到,在觸摸優化後,滑塊的寬高被設置為 44,對應的範圍顯示也會變大;而在非觸摸優化時,控制項整體會變小,變為滑鼠點擊時的樣式;因為實現了觸摸優化,所以我們可以根據當前設備是否是平板模式,來決定控制項的顯示狀態,非常有用。

private void ArrangeForTouch()
{
    if (_containerCanvas == null)
    {
        return;
    }

    if (IsTouchOptimized)
    {
        ...
        if (_minThumb != null)
        {
            _minThumb.Width = _minThumb.Height = 44;
            _minThumb.Margin = new Thickness(-20, 0, 0, 0);
        }
        ...
    }
    else
    {
        ...
        if (_minThumb != null)
        {
            _minThumb.Width = 8;
            _minThumb.Height = 24;
            _minThumb.Margin = new Thickness(-8, 0, 0, 0);
        }
        ...
    }
}

下麵的幾個方法,主要處理的是 rangeMin 和 rangeMax 兩個滑塊的拖拽事件,我們還是只看 min 對應的處理,max 處理類似:

根據當前滑塊拖拽後的位置,來修改實際範圍最小值;計算方式就是根據允許範圍區間,控制項實際寬度,以及當前位置距離最小值的距離,來計算出比例;

private void MinThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
    _absolutePosition += e.HorizontalChange;

    RangeMin = DragThumb(_minThumb, 0, Canvas.GetLeft(_maxThumb), _absolutePosition);
}

private double DragThumb(Thumb thumb, double min, double max, double nextPos)
{
    nextPos = Math.Max(min, nextPos);
    nextPos = Math.Min(max, nextPos);

    Canvas.SetLeft(thumb, nextPos);

    return Minimum + ((nextPos / _containerCanvas.ActualWidth) * (Maximum - Minimum));
}

而在滑塊拖拽開始和結束時,以及可用狀態變化時,也會觸發對應的 VisualStateManager 的 state 來調整控制項視覺顯示狀態;

 

調用示例

我們定義了一個 RangeSelector 控制項,在左右兩側顯示當前選擇範圍的最小值和最大值,而控制項的可選範圍區間是 0~100,可以看到示例運行圖的顯示:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="50"/>
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Column="0"
                HorizontalAlignment="Left"
                VerticalAlignment="Center"
                Foreground="Black"
                Text="{Binding RangeMin, ElementName=RangeSelectorControl}" />
    <controls:RangeSelector  Grid.Column="1"
                x:Name="RangeSelectorControl"
                Minimum="0"
                Maximum="100"
                StepFrequency="1"/>
    <TextBlock  Grid.Column="2"
                HorizontalAlignment="Right"
                VerticalAlignment="Center"
                Foreground="Black"
                Text="{Binding RangeMax, ElementName=RangeSelectorControl}" />
</Grid>

 

總結

到這裡我們就把 UWP Community Toolkit 中的 RangeSelector 控制項的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個控制項有所幫助,大家也可以在實際應用中,編寫更豐富的控制項樣式,或者更特殊的範圍選擇,比如環形等。歡迎大家多多交流,謝謝!

最後,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490大家可以通過微博關註最新動態。

衷心感謝 UWPCommunityToolkit 的作者們傑出的工作,Thank you so much, UWPCommunityToolkit authors!!!

 


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

-Advertisement-
Play Games
更多相關文章
  • 練習題 python 1、整理函數相關知識點,寫博客 2、寫函數,檢查獲取傳入列表或元組對象的所有奇數位索引對應的元素, 並將其作為新列表返回給調用者。 def odd_index(l): lis = [] for i in range(len(l)): 通過range設定索引範圍比通過元素找索引要 ...
  • 【基本的文件操作】 參數: 1、文件路徑; 2、編碼方式; 3、執行動作;(打開方式)只讀,只寫,追加,讀寫,寫讀! ...
  • 第一次作業 第一作業的主要難度在於判斷字元串的格式。在寫C語言代碼時發現,如果輸出格式錯在哪裡內容非常多,導致C語言雖然沒有仔細debug,但依然寫了好久。java使用正則表達式後可以簡化很多,但輸出錯在哪裡要增加的內容更多,所以只是對錯誤簡單地進行分類輸出。 正則表達式相關的類由Matcher和P ...
  • 創建動態Web工程打war包 ​ 創建動態Web工程打war包 ​ 創建動態Web工程打war包 ​ File→new→Maven Project→勾上create a simple project→然後next> ​ File→new→Maven Project→勾上create a simple ...
  • 對python中集合的理解 集合是一個無序的,不重覆的數據組合,它的主要作用如下: 去重,把一個列表變成集合,就自動去重了 關係測試,測試兩組數據之前的交集、差集、並集等關係 常用操作 ...
  • 在C++里,通過繼承和組合實現了代碼復用,使得開發效率提高,並且能夠通過代碼看到事物的關係 組合比繼承簡單,所以在寫代碼時先考慮能否組合,再來考慮繼承. 組合的特點 將其它類的對象作為當前類的成員使用 比如主機類,擁有 CPU/主板/記憶體/硬碟這4個對象成員,而這4個對象成員並沒有繼承主機類的特性和 ...
  • 由於開發環境要求,或者實際需要。經常會出現python2.7和python3.x共存下的開發環境問題。虛擬環境的搭建可以很好的隔離Projects的開發環境。 1.首先解決python2.7和python3.x的pip問題 由於py2和py3版本都是使用的pip,然而我們在安裝Python3(>=3 ...
  • 你是產品經理,目前正在領導一個團隊開發一個新產品。不幸的是,您的產品的最新版本沒有通過質量檢查。由於每個版本都是基於之前的版本開發的,所以錯誤版本之後的所有版本都是不好的。 假設你有 n 個版本 [1, 2, ..., n],你想找出第一個錯誤的版本,導致下麵所有的錯誤。 你可以通過 bool is ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...