重新想象 Windows 8.1 Store Apps (93) - 控件增強: GridView, ListView


[源碼下載]


重新想象 Windows 8.1 Store Apps (93) - 控件增強: GridView, ListView



作者:webabcd


介紹
重新想象 Windows 8.1 Store Apps 之控件增強

  • GridView 和 ListView 每屏顯示的數據量多滾動也流暢
  • GridViewItemPresenter 和 ListViewItemPresenter 更方便更快速地顯示各種狀態
  • 自定義 GridViewItemPresenter 和 ListViewItemPresenter



示例
1、演示 GridView 和 ListView 的新增特性: GridView 和 ListView 每屏顯示的數據量多滾動也流暢
GridViewAndListView/Employee.cs

namespace Windows81.Controls.GridViewAndListView
{
    public class Employee
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public bool IsMale { get; set; }
    }
}

GridViewAndListView/TestData.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Windows81.Controls.GridViewAndListView
{
    public class TestData
    {
        /// <summary>
        /// 返回一個 Employee 數據集合,測試用
        /// </summary>
        public static List<Employee> GetEmployees()
        {
            var employees = new List<Employee>();

            for (int i = 0; i < 10000; i++)
            {
                employees.Add(
                    new Employee
                    {
                        Name = "Name " + i.ToString().PadLeft(4, '0'),
                        Age = new Random(i).Next(20, 60),
                        IsMale = Convert.ToBoolean(i % 2)
                    });
            }

            return employees;
        }
    }
}

GridViewAndListView/IncrementalData.xaml

<Page
    x:Name="pageRoot"
    x:Class="Windows81.Controls.GridViewAndListView.IncrementalData"
    DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows81.Controls.GridViewAndListView"
    xmlns:common="using:Windows81.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">

        <GridView x:Name="gridView" Margin="120 0 0 0"
                  ContainerContentChanging="gridView_ContainerContentChanging">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Height="100" Width="100" Background="Blue">
                        <Rectangle x:Name="placeholderRectangle" Fill="Red" Height="10" Opacity="0" />
                        <TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
                        <TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
                        <TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
        
    </Grid>
</Page>

GridViewAndListView/IncrementalData.xaml.cs

/*
 * 演示 GridView 和 ListView 的新增特性
 * 
 * 當 GridView 或 ListView 的一屏需要顯示的數據量極大時(一屏的 item 多,且每個 item 中的 element 也多),由於每次滾動時需要繪制當前屏的每個 element,這需要占用大量的 ui 資源,所以就會有一些卡頓
 * 為了解決這個問題 win8.1 給出了兩種解決方案
 * 1、設置 GridView 或 ListView 的 ShowsScrollingPlaceholders 屬性為 true(默認值),每次顯示 item 時先會顯示占位符(application 級的背景色塊),然后再繪制內容
 * 2、通過 GridView 或 ListView 的 ContainerContentChanging 事件,分步繪制 item 中的 element
 */

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

namespace Windows81.Controls.GridViewAndListView
{
    public sealed partial class IncrementalData : Page
    {
        public IncrementalData()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            gridView.ItemsSource = TestData.GetEmployees();

            // 默認值是 true,即為了保證流暢,每次顯示 item 時先會顯示占位符(application 級的背景色塊),然后再繪制內容
            // 本例演示 ContainerContentChanging 事件的使用,所以不會用到這個
            gridView.ShowsScrollingPlaceholders = false;
        }

        private void gridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            // 交由我處理吧
            args.Handled = true;

            // 第 1 階段繪制
            // args.Phase.ToString(); // 0

            StackPanel templateRoot = (StackPanel)args.ItemContainer.ContentTemplateRoot;
            Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
            TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");
            TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");
            TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale");

            // 顯示自定義占位符(也可以不用這個,而是直接顯示 item 的背景)
            placeholderRectangle.Opacity = 1;

            // 除了占位符外,所有 item 全部暫時不繪制
            lblName.Opacity = 0;
            lblAge.Opacity = 0;
            lblIsMale.Opacity = 0;

            // 開始下一階段的繪制
            args.RegisterUpdateCallback(ShowName);
        }

        private void ShowName(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            // 第 2 階段繪制
            // args.Phase.ToString(); // 1

            Employee employee = (Employee)args.Item;
            SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
            StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
            TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");

            // 繪制第 2 階段的內容
            lblName.Text = employee.Name;
            lblName.Opacity = 1;

            // 開始下一階段的繪制
            args.RegisterUpdateCallback(ShowAge);
        }

        private void ShowAge(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            // 第 3 階段繪制
            // args.Phase.ToString(); // 2

            Employee employee = (Employee)args.Item;
            SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
            StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
            TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");

            // 繪制第 3 階段的內容
            lblAge.Text = employee.Age.ToString();
            lblAge.Opacity = 1;

            // 開始下一階段的繪制
            args.RegisterUpdateCallback(ShowIsMale);
        }

        private void ShowIsMale(ListViewBase sender, ContainerContentChangingEventArgs args)
        {
            // 第 4 階段繪制
            // args.Phase.ToString(); // 3

            Employee employee = (Employee)args.Item;
            SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
            StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
            Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
            TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale");

            // 繪制第 4 階段的內容
            lblIsMale.Text = employee.IsMale.ToString();
            lblIsMale.Opacity = 1;

            // 隱藏自定義占位符
            placeholderRectangle.Opacity = 0;
        }
    }
}


2、GridViewItemPresenter 和 ListViewItemPresenter - 用於設置控件的各種狀態(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加載完后再顯示,win8.1 的此方式是需要的時候才加載)
GridViewAndListView/ItemPresenter.xaml

<Page
    x:Class="Windows81.Controls.GridViewAndListView.ItemPresenter"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows81.Controls.GridViewAndListView"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <Style x:Key="MyGridViewItemPresenterTemplate" TargetType="GridViewItem">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="GridViewItem">
                        <!--
                            GridViewItemPresenter 和 ListViewItemPresenter - 用於設置控件的各種狀態(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加載完后再顯示,win8.1 的此方式是需要的時候才加載)
                                Margin - item 的 margin
                                SelectionCheckMarkVisualEnabled - 是否顯示選中狀態的標記
                                SelectedBorderThickness - 選中狀態的邊框粗細
                                SelectedBackground - 選中狀態的邊框顏色
                                CheckBrush - 選中狀態的圖標(本例就是那個小對勾)
                                ...... - 還有好多好多,看文檔吧
                        -->
                        <GridViewItemPresenter Margin="10" SelectionCheckMarkVisualEnabled="True" SelectedBorderThickness="3" SelectedBackground="Red" CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Page.Resources>

    <Grid Background="Transparent">
        <GridView x:Name="gridView" Margin="120 0 0 0" 
                  ItemContainerStyle="{StaticResource MyGridViewItemPresenterTemplate}"
                  SelectionMode="Multiple">
            <GridView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Height="100" Width="100" Background="Blue">
                        <TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
                        <TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
                        <TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
                    </StackPanel>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>

GridViewAndListView/ItemPresenter.xaml.cs

/*
 * GridViewItemPresenter 和 ListViewItemPresenter - 用於設置控件的各種狀態(win8.1 新引入),比 win8 的方式更方便,且更快(win8 的方式是全部加載完后再顯示,win8.1 的此方式是需要的時候才加載)
 */

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Windows81.Controls.GridViewAndListView
{
    public sealed partial class ItemPresenter : Page
    {
        public ItemPresenter()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            gridView.ItemsSource = TestData.GetEmployees();
        }
    }
}


3、演示自定義 GridViewItemPresenter 和 ListViewItemPresenter 的使用
GridViewAndListView/MyItemPresenter.cs

/*
 * 自定義 GridViewItemPresenter 和 ListViewItemPresenter
 * 
 * 本例演示如何自定義一個 ContentPresenter 類,以用於 GridView(參見:ItemPresenterCustom.xaml)
 */

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes;

namespace Windows81.Controls.GridViewAndListView
{
    class MyItemPresenter : ContentPresenter
    {
        Grid _contentGrid = null; // item 的 grid(數據模板中的 Grid,也就是說要使用本例的這個 ContentPresenter 則數據模板中必須要用 Grid 做容器)
        Rectangle _pointerOverBorder = null; // 鼠標經過
        Rectangle _focusVisual = null; // 選中

        PointerDownThemeAnimation _pointerDownAnimation = null;
        Storyboard _pointerDownStoryboard = null;

        GridView _parentGridView;

        public MyItemPresenter()
            : base()
        {
            base.Margin = new Thickness(10);
        }

        protected override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            var obj = VisualTreeHelper.GetParent(this);
            while (!(obj is GridView))
            {
                obj = VisualTreeHelper.GetParent(obj);
            }
            _parentGridView = (GridView)obj;

            _contentGrid = (Grid)VisualTreeHelper.GetChild(this, 0);
        }

        protected override bool GoToElementStateCore(string stateName, bool useTransitions)
        {
            base.GoToElementStateCore(stateName, useTransitions);

            switch (stateName)
            {
                // 正常狀態
                case "Normal":
                    HidePointerOverVisuals();
                    HideFocusVisuals();
                    if (useTransitions)
                    {
                        StopPointerDownAnimation();
                    }
                    break;

                // 選中狀態
                case "Focused":
                case "PointerFocused":
                    ShowFocusVisuals();
                    break;

                // 取消選中狀態
                case "Unfocused":
                    HideFocusVisuals();
                    break;

                // 鼠標經過狀態
                case "PointerOver":
                    ShowPointerOverVisuals();
                    if (useTransitions)
                    {
                        StopPointerDownAnimation();
                    }
                    break;

                // 鼠標點擊狀態
                case "Pressed":
                case "PointerOverPressed":
                    if (useTransitions)
                    {
                        StartPointerDownAnimation();
                    }
                    break;

                default: break;
            }

            return true;
        }

        private void StartPointerDownAnimation()
        {
            if (_pointerDownStoryboard == null)
                CreatePointerDownStoryboard();

            _pointerDownStoryboard.Begin();
        }

        private void StopPointerDownAnimation()
        {
            if (_pointerDownStoryboard != null)
                _pointerDownStoryboard.Stop();
        }

        private void ShowFocusVisuals()
        {    
            if (!FocusElementsAreCreated())
                CreateFocusElements();

            _focusVisual.Opacity = 1;
        }

        private void HideFocusVisuals()
        {
            if (FocusElementsAreCreated())
                _focusVisual.Opacity = 0;
        }

        private void ShowPointerOverVisuals()
        { 
            if (!PointerOverElementsAreCreated())
                CreatePointerOverElements();

            _pointerOverBorder.Opacity = 1;
        }

        private void HidePointerOverVisuals()
        {
            if (PointerOverElementsAreCreated())
                _pointerOverBorder.Opacity = 0;
        }

        private void CreatePointerDownStoryboard()
        {
            _pointerDownAnimation = new PointerDownThemeAnimation();
            Storyboard.SetTarget(_pointerDownAnimation, _contentGrid);

            _pointerDownStoryboard = new Storyboard();
            _pointerDownStoryboard.Children.Add(_pointerDownAnimation);
        }

        private void CreatePointerOverElements()
        {
            _pointerOverBorder = new Rectangle();
            _pointerOverBorder.IsHitTestVisible = false;
            _pointerOverBorder.Opacity = 0;
            _pointerOverBorder.Fill = (SolidColorBrush)_parentGridView.Resources["PointerOverBrush"]; // 此資源要預先在 GridView 中定義

            _contentGrid.Children.Insert(_contentGrid.Children.Count, _pointerOverBorder);
        }

        private void CreateFocusElements()
        {
            _focusVisual = new Rectangle();
            _focusVisual.IsHitTestVisible = false;
            _focusVisual.Height = 10;
            _focusVisual.StrokeThickness = 2;
            _focusVisual.VerticalAlignment = VerticalAlignment.Bottom;
            _focusVisual.Fill = (SolidColorBrush)_parentGridView.Resources["FocusBrush"]; // 此資源要預先在 GridView 中定義
            _focusVisual.Stroke = (SolidColorBrush)_parentGridView.Resources["FocusBrush"]; // 此資源要預先在 GridView 中定義

            _contentGrid.Children.Insert(0, _focusVisual);
        }

        private bool FocusElementsAreCreated()
        {
            return _focusVisual != null;
        }

        private bool PointerOverElementsAreCreated()
        {
            return _pointerOverBorder != null;
        }
    }
}

GridViewAndListView/ItemPresenterCustom.xaml

<Page
    x:Class="Windows81.Controls.GridViewAndListView.ItemPresenterCustom"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows81.Controls.GridViewAndListView"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <Style x:Key="MyGridViewItemPresenterTemplate" TargetType="GridViewItem">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="GridViewItem">
                        <!--
                            自定義 GridViewItemPresenter 和 ListViewItemPresenter
                            關於 GridViewItemPresenter 和 ListViewItemPresenter 的說明參見:ItemPresenter.xaml
                        
                            MyItemPresenter 參見 MyItemPresenter.cs
                        -->
                        <local:MyItemPresenter/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Page.Resources>

    <Grid Background="Transparent">
        <GridView x:Name="gridView" Margin="120 0 0 0" 
                  ItemContainerStyle="{StaticResource MyGridViewItemPresenterTemplate}"
                  SelectionMode="Multiple">
            <GridView.Resources>
                <!--MyItemPresenter.cs 中需要用到的資源-->
                <SolidColorBrush x:Name="PointerOverBrush" Color="#50505050"/>
                <SolidColorBrush x:Name="FocusBrush" Color="#ffff0000"/>
            </GridView.Resources>
            <GridView.ItemTemplate>
                <DataTemplate>
                    <Grid Height="100" Width="100" Background="Blue">
                        <TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
                    </Grid>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>
    </Grid>
</Page>

GridViewAndListView/ItemPresenterCustom.xaml.cs

/*
 * 演示自定義 GridViewItemPresenter 和 ListViewItemPresenter 的使用
 * 
 * 關於 GridViewItemPresenter 和 ListViewItemPresenter 的說明參見:ItemPresenter.xaml
 */

using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Windows81.Controls.GridViewAndListView
{
    public sealed partial class ItemPresenterCustom : Page
    {
        public ItemPresenterCustom()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            gridView.ItemsSource = TestData.GetEmployees();
        }
    }
}



OK
[源碼下載]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM