winfwu

导航

WPF SurfaceListbox 循环事件

截图:

(1)生成项目

  1. 打开vs2010,创建一个surface Application工程。
  2. 进入ContinuousList作为项目名称
  3. 在SurfaceWindow1.xaml文件,添加一个数据绑定SurfaceListBox控制网格控件模板的一部分。
  4. 为SurfaceListBox创建一个模板(Template)。当您完成操作之后,SurfaceWindow1.xaml文件的应用程序应该包含以下代码。

<s:SurfaceWindow x:Class="ContinuousList.SurfaceWindow1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:s="http://schemas.microsoft.com/surface/2008"

    Title="ContinuousList"

    xmlns:l="clr-namespace:ContinuousList"

    >

  <Grid>

     

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="322"/>

            <ColumnDefinition Width="*" />

        </Grid.ColumnDefinitions>

     

        <s:SurfaceListBox Name="MainSurfaceListBox">

            <s:SurfaceListBox.ItemTemplate>

                <DataTemplate>

                    <Image Source="{Binding}" Width="270"/>

                </DataTemplate>

            </s:SurfaceListBox.ItemTemplate>

            <s:SurfaceListBox.Template>

                <ControlTemplate>

                    <s:SurfaceScrollViewer

                        VerticalScrollBarVisibility="Hidden"

                        HorizontalScrollBarVisibility="Disabled"

                        CanContentScroll="true">

                        <l:LoopPanelVertical IsItemsHost="True"/>

                    </s:SurfaceScrollViewer>

                </ControlTemplate>

            </s:SurfaceListBox.Template>

        </s:SurfaceListBox>

 

    </Grid>

</s:SurfaceWindow>

 

        在控制模板的SurfaceListBox控制,一个SurfaceScrollViewer控制被定义。这个HorizontalScrollBarVisibility属性设置为“禁用”禁用水平滚动的控制。这个VerticalScrollBarVisibility属性设置为“隐藏”,使垂直运动不显示滚动条本身。因此,垂直平移是唯一的方法操纵内容。

        这个SurfaceScrollViewer定义使用一个自定义类,叫做LoopPanelVertical。这类源于面板并实现了ISurfaceScrollInfo接口提供定制的循环功能。这个类的进一步讨论在“创建LoopPanelVertical类”一节。

 

(2)填充列表

SurfaceListBox填充的控制通过使用数据绑定。早期的XAML代码建立绑定。指定的源绑定、修改SurfaceWindow1.xaml。cs文件如下:

#region OnInitialized
protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
 
    // Query the registry to find out where the sample photos are stored.
    const string shellKey =
        @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\Shell Folders";
 
    string imagesPath =
        (string)Microsoft.Win32.Registry.GetValue(shellKey, "CommonPictures", null) + @"\Sample Pictures";
 
    try
    {
        // Set the ItemsSource property.
        MainSurfaceListBox.ItemsSource =
            System.IO.Directory.GetFiles(imagesPath, "*.jpg");
    }
    catch (System.IO.DirectoryNotFoundException)
    {
        // Handle exceptions as needed.
    }
}
#endregion

(3)创建LoopPanelVertical类

LoopPanelVertical的类实现了ISurfaceScrollInfo接口启用连续循环的内容SurfaceScrollViewer控制。LoopPanelVertical的实现分为以下几部分。

#region AllCode
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Controls.Primitives;
 
namespace ContinuousList
{
    public partial class LoopPanelVertical : Panel, ISurfaceScrollInfo
    {
        private ScrollViewer owner;
        private Size extent = new Size(0, 0);
        private Size viewport = new Size(0, 0);
        private Point viewportOffset = new Point();
        private bool viewportPositionDirty;
        private bool verticalScrollAllowed;
        private int firstItem;
        private double firstItemOffset;
        private double previousOffset = double.NaN;
        private double totalContentHeight;
 
        // Constructor
        public LoopPanelVertical()
        {
            this.RenderTransform = new TranslateTransform();
        }
 
        #region IScrollInfo Properties
        // Gets or sets a value that determines if the content can be scrolled horizontally.
        public bool CanHorizontallyScroll
        {
            get
            {
                return false;
            }
            set
            {
                if (value)
                {
                    throw new NotSupportedException();
                }
            }
        }
 
        // Gets or sets a value that determines if the content can be scrolled vertically.
        public bool CanVerticallyScroll
        {
            get
            {
                return verticalScrollAllowed;
            }
            set
            {
                verticalScrollAllowed = value;
            }
        }
 
        // Gets a value that describes the height of the area.
        public double ExtentHeight
        {
            get { return extent.Height; }
        }
 
        // Gets a value that describes the width of the area.
        public double ExtentWidth
        {
            get { return extent.Width; }
        }
 
        /// Gets a value that describes the vertical offset of the view box.
        public double VerticalOffset
        {
            get { return viewportOffset.Y; }
        }
 
 
        // Gets a value that describes the horizontal offset of the view box.
        public double HorizontalOffset
        {
            get { return viewportOffset.X; }
        }
 
        // Gets a value that describes the height of the viewport.
        public double ViewportHeight
        {
            get { return viewport.Height; }
        }
 
        // Gets a value that describes the width of the viewport.
        public double ViewportWidth
        {
            get { return viewport.Width; }
        }
 
        // Gets or sets the owner.
        public ScrollViewer ScrollOwner
        {
            get
            {
                return this.owner;
            }
            set
            {
                this.owner = value;
            }
        }
        #endregion
 
        #region IScrollInfo Positioning Methods
        // Scroll content up.
        public void LineUp()
        {
            ScrollContent(-1);
        }
 
        // Scroll content down.
        public void LineDown()
        {
            ScrollContent(1);
        }
 
        public void LineLeft()
        {
            // Not implemented. Cannot scroll horizontally.
        }
 
        public void LineRight()
        {
            // Not implemented. Cannot scroll horizontally.
        }
 
        // Scrolls the content up by ten lines.
        public void MouseWheelUp()
        {
            ScrollContent(10);
        }
 
        // Scrolls the content down by ten lines. 
        public void MouseWheelDown()
        {
            ScrollContent(-10);
        }
 
        public void MouseWheelLeft()
        {
            // Not implemented. Cannot scroll horizontally.
        }
 
        public void MouseWheelRight()
        {
            // Not implemented. Cannot scroll horizontally. 
        }
 
        public void PageUp()
        {
            ScrollContent(-viewport.Height);
        }
 
        public void PageDown()
        {
            ScrollContent(viewport.Height);
        }
 
        public void PageLeft()
        {
            // Not implemented. Cannot scroll horizontally.
        }
 
        public void PageRight()
        {
            // Not implemented. Cannot scroll horizontally.
        }
        #endregion
 
        #region IScrollInfo Offset Methods
        public void SetVerticalOffset(double newOffset)
        {
            if (CanVerticallyScroll)
            {
                // Make sure the offset is set for the current inputs.
                if (double.IsNaN(previousOffset))
                {
                    previousOffset = newOffset;
                }
 
                // Calculate the movement delta.
                double difference = previousOffset - newOffset;
 
                // Scroll the content.
                ScrollContent(difference);
 
                // Update the offset for next time.
                previousOffset = newOffset;
            }
        }
 
        public void SetHorizontalOffset(double newOffset)
        {
            // Not implemented. Cannot scroll horizontally.
        }
        #endregion
 
        #region IScrollInfo MakeVisible Method
        public Rect MakeVisible(Visual visual, Rect rectangle)
        {
            int itemIndex = Children.IndexOf((UIElement)visual);
            int index = firstItem;
            double itemOffset = firstItemOffset;
 
            // Get the offset for the item that should be visible.
            while (index != itemIndex)
            {
                itemOffset += Children[index].DesiredSize.Height;
                index++;
                if (index >= Children.Count)
                {
                    index = 0;
                }
            }
 
            // If the item is not fully in view on the top, 
            // adjust the offset to bring it into view.
            if (itemOffset < viewportOffset.Y)
            {
                ScrollContent(viewportOffset.Y - itemOffset);
            }
 
            // If the item is not fully in view on the bottom, 
            // adjust the offset to bring it into view.
            if (itemOffset + rectangle.Height > viewportOffset.Y + ViewportHeight)
            {
                ScrollContent((viewportOffset.Y + ViewportHeight) - 
                    (itemOffset + rectangle.Height));
            }
 
            return rectangle;
        }
        #endregion
 
        #region ISurfaceScrollInfo Members
        public Vector ConvertFromViewportUnits(Point origin, Vector offset)
        {
            return offset;
        }
 
        public Vector ConvertToViewportUnits(Point origin, Vector offset)
        {
            return offset;
        }
 
        public Vector ConvertToViewportUnitsForFlick(Point origin, Vector offset)
        {
            return offset;
        }
        #endregion
 
        #region Overridden Layout Methods
        protected override Size MeasureOverride(Size availableSize)
        {
            // If there's no children, take all the space.
            if (this.InternalChildren.Count == 0)
            {
                return availableSize;
            }
 
            // Measure each of the child items. Keep a running sum of heights.
            // Used later to measure the viewport and the extent.
            this.totalContentHeight = 0;
            foreach (UIElement child in this.InternalChildren)
            {
                child.Measure(availableSize);
                this.totalContentHeight += child.DesiredSize.Height;
            }
 
            // The viewport should be as large as it can be.
            this.viewport = availableSize;
 
            // Remeasuring could invalidate the current viewport position. Mark it as dirty.
            this.viewportPositionDirty = true;
 
            return availableSize;
        }
        
        protected override Size ArrangeOverride(Size finalSize)
        {
            // If there's no children, take all the space.
            if (this.InternalChildren.Count == 0)
            {
                return finalSize;
            }
 
            // Arrange the viewport relative to the extent.
            if (this.viewportPositionDirty)
            {
                PositionViewportAndExtent();
            }
 
            double nextDrawingPosition = firstItemOffset;
            Rect arrangeInMe;
 
            // From first item to end
            for (int i = firstItem; i < InternalChildren.Count; i++)
            {
                UIElement item = InternalChildren[i];
                arrangeInMe = 
                    new Rect(0, nextDrawingPosition, 
                        item.DesiredSize.Width, 
                        item.DesiredSize.Height);
                item.Arrange(arrangeInMe);
                nextDrawingPosition += item.DesiredSize.Height;
            }
 
            // From child zero to firstItem - 1
            for (int i = 0; i < firstItem; i++)
            {
                UIElement item = InternalChildren[i];
                arrangeInMe = 
                    new Rect(0, nextDrawingPosition, 
                        item.DesiredSize.Width, 
                        item.DesiredSize.Height);
                item.Arrange(arrangeInMe);
                nextDrawingPosition += item.DesiredSize.Height;
            }
 
            return finalSize;
        }
        #endregion
 
        #region Viewport and Extent Methods
        private void PositionViewportAndExtent()
        {
            // If the items all fit in the viewport at the same time, 
            // disable scrolling and center the items.
            if (totalContentHeight < viewport.Height)
            {
                extent = new Size(viewport.Width, totalContentHeight);
                SetViewport(0);
                firstItemOffset = (viewport.Height - totalContentHeight) / 2;
                CanVerticallyScroll = false;
            }
 
            // Otherwise, extend the extent past both ends of the viewport 
            // so there will be plenty of space in which to scroll the items.
            else
            {
                // Make sure the extent is plenty large. 
                extent = new Size(viewport.Width, Math.Max(1000000, totalContentHeight * 15));
                SetViewport((extent.Height - viewport.Height) / 2);
                firstItemOffset = (extent.Height - totalContentHeight) / 2;
                CanVerticallyScroll = true;
            }
 
            viewportPositionDirty = false;
 
            // Because the offset was changed, the current scroll info isn't valid anymore.
            if (owner != null)
            {
                owner.InvalidateScrollInfo();
            }
        }
        
        private void SetViewport(double newOffset)
        {
            // Validate the input.
            if (newOffset < 0 || viewport.Height >= extent.Height)
            {
                newOffset = 0;
            }
 
            if (newOffset + viewport.Height >= extent.Height)
            {
                newOffset = extent.Height - viewport.Height;
            }
 
            // Value is validated, so use it.
            viewportOffset.Y = newOffset;
 
            // Adjust the transform to display based on the new offset.
            ((TranslateTransform)this.RenderTransform).Y = -newOffset;
 
            // Balance the content around the viewport.
            double firstItemBottom = 
                firstItemOffset + InternalChildren[firstItem].DesiredSize.Height;
            int lastItemIndex = 
                firstItem <= 0 ? InternalChildren.Count - 1 : firstItem - 1;
            double lastItemTop = 
                firstItemOffset + totalContentHeight - InternalChildren[lastItemIndex].DesiredSize.Height;
 
            // Move items as needed.
            if (viewportOffset.Y < firstItemBottom)
            {
                MoveItemsFromBottomToTop();
            }
 
            if (viewportOffset.Y + ViewportHeight > lastItemTop)
            {
                MoveItemsFromTopToBottom();
            }
        }
 
        // Called by various other methods.
        private void ScrollContent(double adjustment)
        {
            SetViewport(viewportOffset.Y - adjustment);
        }
        #endregion
        
        #region Move Item Methods
        private void MoveItemsFromBottomToTop()
        {
            // Move from the left to the right 
            // until the first item that is not in the viewport.
            int index = firstItem;
            double itemTopOffset = firstItemOffset;
            int itemsInViewport = 0;
 
            // Step through the items from the top, and
            // count how many items are in the viewport.
            while (itemTopOffset < viewportOffset.Y + ViewportHeight)
            {
                itemsInViewport++;
                itemTopOffset += InternalChildren[index].DesiredSize.Height;
                index = index >= InternalChildren.Count - 1 ? 0 : index + 1;
            }
 
            // The items that are not in the viewport should be distributed
            // so there are some on either side of the viewport. 
            // It's likely that the viewport will continue to be moved in the 
            // direction it is currently being moved.
            // Move 2/3 of the items, and leave 1/3 where they are.
            int itemsNotInViewport = InternalChildren.Count - itemsInViewport;
            int itemsToRemainInPlace = itemsNotInViewport / 3;
 
            // Skip past the items that will remain in place.
            for (int i = 0; i < itemsToRemainInPlace; i++)
            {
                itemTopOffset += InternalChildren[index].DesiredSize.Height;
                index = index >= InternalChildren.Count - 1 ? 0 : index + 1;
            }
 
            // Find the amount by which firstItemOffset needs to be adjusted.
            double movedHeight = firstItemOffset + totalContentHeight - itemTopOffset;
 
            // Change the first item index, and adjust the offset to match.
            firstItemOffset -= movedHeight;
            firstItem = index;
 
            // Need to redraw the items.
            InvalidateVisual();
        }
 
        private void MoveItemsFromTopToBottom()
        {
            // Move from the top to bottom 
            // until the first item that is not in the viewport.
            int index = firstItem <= 0 ? InternalChildren.Count - 1 : firstItem - 1;
            double itemBottomOffset = firstItemOffset + totalContentHeight;
            int itemsInViewport = 0;
 
            // Step through the items from the bottom, and
            // count how many items are in the viewport.
            while (itemBottomOffset > viewportOffset.Y)
            {
                itemsInViewport++;
                itemBottomOffset -= InternalChildren[index].DesiredSize.Height;
                index = index <= 0 ? InternalChildren.Count - 1 : index - 1;
            }
 
            // The items that are not in the viewport should be distributed
            // so there are some on either side of the viewport. 
            // It's likely that the viewport will continue to be moved in the 
            // direction it is currently being moved.
            // Move 2/3 of the items, and leave 1/3 where they are.
            int itemsNotInViewport = InternalChildren.Count - itemsInViewport;
            int itemsToRemain = itemsNotInViewport / 3;
 
            // Skip past the items that will remain in place.
            for (int i = 0; i < itemsToRemain; i++)
            {
                itemBottomOffset -= InternalChildren[index].DesiredSize.Height;
                index = index <= 0 ? InternalChildren.Count - 1 : index - 1;
            }
 
            // Find the amount by which firstItemOffset needs to be adjusted.
            double movedHeight = itemBottomOffset - firstItemOffset;
 
            // Change the first item index, and adjust the offset to match.
            firstItemOffset += movedHeight;
            firstItem = index >= InternalChildren.Count - 1 ? 0 : index + 1;
 
            // Need to redraw the items.
            InvalidateVisual();
        }
        #endregion
    } // end Class LoopPanelVertical
}
#endregion

posted on 2013-02-20 11:23  winfwu  阅读(805)  评论(1编辑  收藏  举报