UWP中實現大爆炸效果(二)

来源:https://www.cnblogs.com/blue-fire/archive/2018/05/27/9097918.html
-Advertisement-
Play Games

上一回實現了一個寬度不均勻的Panel,這次我們編寫一個簡單的BigbangView主體。 首先創建一個模板化控制項,刪掉Themes/Generic.xaml中的<Style TargetType="BigbangView">...</Style>段。 然後打開C:\Program Files (x ...


上一回實現了一個寬度不均勻的Panel,這次我們編寫一個簡單的BigbangView主體。

首先創建一個模板化控制項,刪掉Themes/Generic.xaml中的<Style TargetType="BigbangView">...</Style>段。

然後打開C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\(SDK版本)\Generic\generic.xaml,在裡面找到

<Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
...
</Style>
<Style TargetType="ListView">
...
</Style>

這兩段,複製到項目中Themes/Generic.xaml中,將TargetType="ListView"修改為TargetType="BigbangView",添加Setter:

 

<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="IsTabStop" Value="False" />
<Setter Property="TabNavigation" Value="Once" />
<Setter Property="IsSwipeEnabled" Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="ItemContainerTransitions">
    <Setter.Value>
        <TransitionCollection>
            <AddDeleteThemeTransition />
            <ContentThemeTransition />
            <ReorderThemeTransition />
            <EntranceThemeTransition IsStaggeringEnabled="False" />
        </TransitionCollection>
    </Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
    <Setter.Value>
        <Style TargetType="ListViewItem">
        前面複製的ListViewItemExpanded的內容剪貼到這裡
        </Style>
    </Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
    <Setter.Value>
        <ItemsPanelTemplate>
            <control:BigbangPanel >
                <control:BigbangPanel.ChildrenTransitions>
                    <TransitionCollection>
                        <AddDeleteThemeTransition />
                    </TransitionCollection>
                </control:BigbangPanel.ChildrenTransitions>
            </control:BigbangPanel>
        </ItemsPanelTemplate>
    </Setter.Value>
</Setter>
View Code

 

 其中BigbangPanel是咱們上回書寫的面板。

然後打開BigbangView.cs,修改基類:

public sealed class BigbangView : ListView
{
    public BigbangView()
    {
        this.DefaultStyleKey = typeof(BigbangView);
    }
}

  

接下來就是整個過程中最複雜,最枯燥的部分,編寫按下滑動選中。

先打開安卓版的大爆炸(不是錘子的可以拿個安卓手機下載IT之家客戶端看效果),對整個過程進行分析發現,有以下幾種狀態。

1、點擊選中;

2、Panel高度小於控制項高度,也就是ScrollViewer不啟用時,按下向四周滑動可以更改選中狀態;

3、Panel高度大於控制項高度,上下滑動可以滾動ScrollViewer,左右滑動禁用ScrollViewer的滾動並且更改選中狀態 。

這篇文章先實現滑鼠的操作。由於滑鼠在頁面上下滑動並不會觸發ScrollViewer的滾動,所以在此不考慮第三項。

首先修改Style中的Template段,

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="control:BigbangView">
            <Border x:Name="RootBorder" BorderBrush="{TemplateBinding BorderBrush}"
                    Background="{TemplateBinding Background}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                <ScrollViewer x:Name="ScrollViewer"
                    TabNavigation="{TemplateBinding TabNavigation}"
                    HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
                    HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                    IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
                    VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
                    VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
                    IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
                    IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
                    IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
                    ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
                    IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
                    BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
                    AutomationProperties.AccessibilityView="Raw">
                    <Grid x:Name="ItemsGrid" Background="Transparent" ManipulationMode="System">
                        <ItemsPresenter
                            Header="{TemplateBinding Header}"
                            HeaderTemplate="{TemplateBinding HeaderTemplate}"
                            HeaderTransitions="{TemplateBinding HeaderTransitions}"
                            Footer="{TemplateBinding Footer}"
                            FooterTemplate="{TemplateBinding FooterTemplate}"
                            FooterTransitions="{TemplateBinding FooterTransitions}"
                            Padding="{TemplateBinding Padding}"/>
                    </Grid>
                </ScrollViewer>
            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>
View Code

在後臺代碼中重載OnApplyTemplate()(實際應該做空判斷,我偷懶了)

public BigbangView()
{
    this.DefaultStyleKey = typeof(BigbangView);
    this.Loaded += BigbangView_Loaded;
    this.Unloaded += BigbangView_Unloaded;
    this.SelectionChanged += BigbangView_SelectionChanged;

    PointerPressedHandler = new PointerEventHandler(_PointerPressed);
    PointerReleasedHandler = new PointerEventHandler(_PointerReleased);
    PointerMovedHandler = new PointerEventHandler(_PointerMoved);
}

protected override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    RootBorder = GetTemplateChild("RootBorder") as Border;
    ItemsGrid = GetTemplateChild("ItemsGrid") as Grid;
    ScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;

    ScrollViewer.AddHandler(UIElement.PointerPressedEvent, PointerPressedHandler, true);
    ScrollViewer.AddHandler(UIElement.PointerReleasedEvent, PointerReleasedHandler, true);
    ScrollViewer.AddHandler(UIElement.PointerCanceledEvent, PointerReleasedHandler, true);
    ScrollViewer.AddHandler(UIElement.PointerExitedEvent, PointerReleasedHandler, true);
    ScrollViewer.ViewChanging += _ScrollViewer_ViewChanging;
}

  

然後我們需要把每個子元素在Panel中的位置緩存下來,在Panel中添加屬性

private Dictionary<UIElement, Rect> _ChildrenRects;

public Dictionary<UIElement, Rect> ChildrenRects
{
    get
    {
        if (_ChildrenRects == null) _ChildrenRects = new Dictionary<UIElement, Rect>();
        return _ChildrenRects;
    }
    set => _ChildrenRects = value;
}

  

將ArrangeOverride中Children[x].Arrange(new Rect...)修改為如下

var rect = new Rect(x, y, Children[i].DesiredSize.Width, Children[i].DesiredSize.Height);
Children[i].Arrange(rect);
ChildrenRects[Children[i]] = rect;

  

編寫以下幾個輔助的方法:

//從Item獲取在Panel中的佈局信息
private Rect? GetItemRect(object Item)
{
    var itemContainer = base.ContainerFromItem(Item) as UIElement;
    if (itemContainer != null && _Panel != null)
    {
        if (_Panel.ChildrenRects.ContainsKey(itemContainer))
        {
            return _Panel.ChildrenRects[itemContainer];
        }
    }
    return null;
}

//從容器獲取Item和Index
private int GetIndexFromContainer(UIElement Container, out object Item, IList<object> SourceList = null)
{
    Item = ItemFromContainer(Container);
    if (Item != null)
    {
        if (SourceList == null) SourceList = GetSourceList();
        return SourceList.IndexOf(Item);
    }
    return -1;
}

//獲取坐標位置的Item和Index
private int GetIndexFromPosition(Point Position, out object Item)
{
    var sourceList = GetSourceList();

    for (int i = 0; i < sourceList.Count; i++)
    {
        var rect = GetItemRect(sourceList[i]);
        if (!rect.HasValue) break;

        if (rect.Value.Contains(Position))
        {
            Item = sourceList[i];
            return i;
        }
    }
    Item = null;
    return -1;
}

//獲取源列表
private IList<object> GetSourceList()
{
    if (ItemsSource == null) return Items.ToList();
    else
    {
        var tmp = new List<object>();
        foreach (var item in (IEnumerable)ItemsSource)
        {
            tmp.Add(item);
        }
        return tmp;
    }
}

  

然後編寫三個狀態方法:OnSelectionStart初始化各個變數的狀態,獲取開始的點;OnSelecting更新被選中的項,OnSelectionComplate做最後的清理,還原狀態:

int StartIndex = -1;    //本次選擇開始的位置
int EndIndex = -1;    //本次選擇結束的位置
bool? IsFirstItemHadSelected;    //本次選擇是選中後續還是取消選中後續
Point? StartPoint;    //選中開始的坐標
UIElement StartContainer;    //選擇開始時的容器

private void OnSelectionStarted(Point Position)
{
    var tmpIndex = GetIndexFromPosition(Position, out var item);
    if (tmpIndex == -1)
    {
        return;
    }
    if (StartIndex < 0)
    {
        StartIndex = tmpIndex;
        IsFirstItemHadSelected = SelectedItems.Contains(item);
        StartContainer = ContainerFromItem(item) as UIElement;
    }
}

private void OnSelecting(UIElement Container)
{
    if (IsFirstItemHadSelected.HasValue)
    {
        var sourceList = GetSourceList();
        var tmpIndex = GetIndexFromContainer(Container, out var item, sourceList);
        if (tmpIndex == -1) return;
        if (StartIndex < 0)
        {
            StartIndex = tmpIndex;
            return;
        }
        else
        {
            EndIndex = tmpIndex;
            if (EndIndex >= 0)
            {
                for (int i = Math.Min(StartIndex, EndIndex); i <= Math.Max(StartIndex, EndIndex); i++)
                {
                    if (IsFirstItemHadSelected.Value)
                    {
                        if (SelectedItems.Contains(sourceList[i])) SelectedItems.Remove(sourceList[i]);
                    }
                    else
                    {
                        if (!SelectedItems.Contains(sourceList[i])) SelectedItems.Add(sourceList[i]);
                    }

                }
            }

        }
    }

}

private void OnSelectionComplated()
{
    StartIndex = -1;
    EndIndex = -1;
    IsFirstItemHadSelected = null;
    StartPoint = null;
    StartContainer = null;
}

  

然後編寫事件:

private void Container_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    if (IsFirstItemHadSelected.HasValue)
    {
        if (sender is UIElement ele && ele != StartContainer)
        {
            if (StartContainer == null)
            {
                StartContainer = ele;
                StartIndex = GetIndexFromContainer(StartContainer, out var item);
            }
            else
            {
                OnSelecting(ele);
            }
        }
    }
}

private void _PointerPressed(object sender, PointerRoutedEventArgs e)
{
    StartPoint = e.GetCurrentPoint(ItemsGrid).Position;
    OnSelectionStarted(StartPoint.Value);
    IsSwipeEnable = true;
}

private void _PointerReleased(object sender, PointerRoutedEventArgs e)
{
    if (IsSwipeEnable.HasValue)
    {
        OnSelectionComplated();
    }
}

  

至此,BigbangView已經可以響應滑鼠的滑動選擇。

下回預告:BigbangView響應觸摸。


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

-Advertisement-
Play Games
更多相關文章
  • 一.列表及元組 1.首先我們先來看一下列表: 列表是我們最常用的數據類型之一,通過列表可以對數據實現最方便的存儲、修改等操作 創建類表的兩種方式: (1.)l1=[1,2,3,4] (2.)l1=list((1,2,3,4)) (1.)和(2.)是等價的都是創建列表的方式 列表常用的方法: 我們可以 ...
  • 題目: 給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。 設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。 註意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。 示例 1: 輸入: [7,1,5,3,6,4] 輸出: 7 解釋 ...
  • 一:Hash結構集合 Hash結構的特點是無序和唯一,無序即添加元素的順序和輸出元素的順序不一致,唯一是指元素不重覆。那是什麼來保證Hash結構元素唯一的呢? 元素所在類的HashCode()和equals()方法來保證元素的唯一性的,所以自定義的類用Hash結構集合存儲元素時,需要重寫這兩個方法。 ...
  • 登錄百度 先清理瀏覽器緩存,打開Charles,登錄一次百度主頁,抓取到登錄過程。 參數分析 確定了需要分析的參數,從哪個開始分析呢?隨意吧 一般有些參數之間是有關係的,比如token的請求參數里需要gid參數 這裡我就不一一去分析參數間的關係了,直接來了啊 參數 gid 一方面其他參數需要它,另外 ...
  • 資料庫就是存儲數據的倉庫,其本質是一個文件系統,數據按照特定的格式將數據存儲起來,用戶可以對資料庫中的數據進行增加,修改,刪除及查詢操作。 mysql的dos視窗啟動關閉命令:net start mysql和net stop mysql 登錄命令:(1)mysql -u用戶名 -p密碼 (2)mys ...
  • 我根據自己的理解,對原文的精華部分進行了提煉,併在一些難以理解的地方加上了自己的“可能比較準確”的「翻譯」。 Chapter4 設計與聲明 Designs and Declarations 條款18: 讓介面容易被正確使用,不易被誤用 欲開發一個“容易被使用,不容易被誤用”的介面,首先必須考慮客戶可 ...
  • 環境配置:windows ,VS,SQLite(點擊下載),System.Data.SQLite.DLL(點擊下載)。 目錄: 一、新建項目,添加引用 二、創建資料庫 三、創建表 四、插入數據 五、查詢數據 一、新建項目,添加引用 1.在VS中新建一個控制台應用程式,如下圖 2.添加引用 將下載的S ...
  • 關聯刪除通常是一個資料庫術語,用於描述在刪除行時允許自動觸發刪除關聯行的特征;即當主表的數據行被刪除時,自動將關聯表中依賴的數據行進行刪除,或者將外鍵更新為 或預設值。 資料庫關聯刪除行為 我們先來看一看SQL Server中支持的行為。在創建外鍵約束時,可以指定關聯表在主表刪除行時,對依賴的數據如 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...