10

我需要创建格式良好的按钮图块,例如 Windows 8 起始页。是否有任何可用于自定义 ListView 的工具包,它可能支持平铺视图或网格视图,具有一些格式并且可能是一些动画选项。

我尝试创建自己的自定义列表视图,但这似乎是一项复杂的任务。

4

2 回答 2

35

我不知道一个不错的免费平铺控件。DevExpress有一个漂亮的商业版本。

如果您指定您的确切要求(即您需要配置哪些属性,需要什么样的动画,......)并且我找到了时间,但我会试一试。

编辑:我创建了一个带有 WrapPanel 作为 ItemsPanel 的 ItemsControl。使用 MVVM 模式,根据您的需要和数据对象扩展控件应该不会太困难。比较难做的是 DragDrop 行为部分——当然还有一些改进的余地。我没有包括图像。

在此处输入图像描述

TileControl.xaml:

<UserControl x:Class="WpfApplication1.TileControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:local="clr-namespace:WpfApplication1"
             xmlns:beh="clr-namespace:WpfApplication1.Behavior"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.DataContext>
        <local:ViewModel />
    </UserControl.DataContext>

    <UserControl.Resources>
        <local:TileTypeToColorConverter x:Key="TileTypeToColorConverter" />
    </UserControl.Resources>

    <Grid>
        <Image Source="/WpfApplication1;component/Themes/background.png" Stretch="UniformToFill" />
        <Border x:Name="darkenBorder" Background="Black" Opacity="0.6" />
        <ItemsControl ItemsSource="{Binding Tiles}" Background="Transparent" Margin="5">
            <i:Interaction.Behaviors>
                <beh:ItemsControlDragDropBehavior />
            </i:Interaction.Behaviors>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="local:TileModel">
                    <Button Content="{Binding Text}" Background="{Binding TileType, Converter={StaticResource TileTypeToColorConverter}}"
                               Command="{Binding ClickCommand}" Width="120" Height="110" Padding="5" RenderTransformOrigin="0.5, 0.5"  >
                        <Button.RenderTransform>
                            <TransformGroup>
                                <ScaleTransform />
                                <SkewTransform/>
                                <RotateTransform/>
                                <TranslateTransform/>
                            </TransformGroup>
                        </Button.RenderTransform>
                        <Button.Template>
                            <ControlTemplate TargetType="Button">
                                <Border Padding="5" Background="Transparent">
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="*" />
                                            <RowDefinition Height="Auto" />
                                        </Grid.RowDefinitions>
                                        <Border x:Name="tileBackground" Grid.RowSpan="2" Background="{TemplateBinding Background}" Opacity="0.9" />
                                        <Image Source="{Binding Image}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="50"  />
                                        <ContentPresenter TextElement.Foreground="White" Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
                                    </Grid>
                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                        <Button.Resources>
                            <ElasticEase x:Key="easeOutBounce" EasingMode="EaseOut" Springiness="6" Oscillations="4" />
                        </Button.Resources>
                        <Button.Triggers>
                            <EventTrigger RoutedEvent="Button.Click">
                                <BeginStoryboard>
                                    <Storyboard Duration="00:00:00.05" AutoReverse="True">
                                        <DoubleAnimation To="0.1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
                                        <DoubleAnimation To="0.1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
                                        <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
                                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.1" To="1.0" EasingFunction="{StaticResource easeOutBounce}" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Button.Triggers>
                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</UserControl>

ViewModel、TileModel、TileType、ActionCommand:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Media.Imaging;

namespace WpfApplication1
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private ObservableCollection<TileModel> _tiles;
        public ObservableCollection<TileModel> Tiles { get { return _tiles; } set { _tiles = value; OnPropertyChanged("Tiles"); } }

        public ViewModel()
        {

           Tiles= new ObservableCollection<TileModel>()
            {
                new TileModel() { Text = "Facebook", Image = Properties.Resources.Facebook.ToBitmapImage(), TileType = TileType.Website },
                new TileModel() { Text = "Skype", Image = Properties.Resources.Skype.ToBitmapImage(), TileType = TileType.Application },
                new TileModel() { Text = "Ask.com", Image = Properties.Resources.AskCom.ToBitmapImage(), TileType = TileType.Website  },
                new TileModel() { Text = "Amazon", Image = Properties.Resources.Amazon.ToBitmapImage(), TileType = TileType.Website },
                new TileModel() { Text = "Evernote", Image = Properties.Resources.Evernote.ToBitmapImage(), TileType = TileType.Application },
                new TileModel() { Text = "Twitter", Image = Properties.Resources.Twitter.ToBitmapImage(), TileType = TileType.Website },
                new TileModel() { Text = "Internet Explorer",  Image = Properties.Resources.InterneExplorer.ToBitmapImage(), TileType = TileType.Browser },
                new TileModel() { Text = "Android", Image = Properties.Resources.Android.ToBitmapImage(), TileType = TileType.Application },
                new TileModel() { Text = "Winamp", Image = Properties.Resources.Winamp.ToBitmapImage(), TileType = TileType.Application },
                new TileModel() { Text = "YouTube", Image = Properties.Resources.YouTube.ToBitmapImage(), TileType = TileType.Website },
            };
        }

    }


    public class TileModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string _text;
        public string Text { get { return _text; } set { _text = value; OnPropertyChanged("Text"); } }

        private BitmapSource _image;
        public BitmapSource Image { get { return _image; } set { _image = value; OnPropertyChanged("Image"); } }

        private TileType _tileType;
        public TileType TileType { get { return _tileType; } set { _tileType = value; OnPropertyChanged("TileType"); } }

        public ICommand ClickCommand { get; private set; }

        public TileModel()
        {
            ClickCommand = new ActionCommand(Click);
        }

        private void Click()
        {
            // execute appropriate action
        }

    }

    public enum TileType
    {
        Browser,
        Website,
        Application
    }

    public class ActionCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        private Action _action;

        public ActionCommand(Action action)
        {
            _action = action;
        }

        public bool CanExecute(object parameter) { return true; }

        public void Execute(object parameter)
        {
            if (_action != null)
                _action();
        }
    }
}

ItemsControlDragDropBehavior:

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace WpfApplication1.Behavior
{
    public class ItemsControlDragDropBehavior : Behavior<ItemsControl>
    {
        private bool _isMouseDown;
        private bool _isDragging;
        private Point _dragStartPosition;
        private UIElement _dragItem;
        private UIElement _dragContainer;
        private IDataObject _dataObject;
        private int _currentDropIndex;
        private Point _lastCheckPoint;

        protected override void OnAttached()
        {
            this.AssociatedObject.AllowDrop = true;
            this.AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
            this.AssociatedObject.PreviewMouseMove += AssociatedObject_PreviewMouseMove;
            this.AssociatedObject.PreviewDragOver += AssociatedObject_PreviewDragOver;
            this.AssociatedObject.PreviewDrop += AssociatedObject_PreviewDrop;
            this.AssociatedObject.PreviewMouseLeftButtonUp += AssociatedObject_PreviewMouseLeftButtonUp;

            base.OnAttached();
        }

        void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            ItemsControl itemsControl = (ItemsControl)sender;
            Point p = e.GetPosition(itemsControl);

            object data = itemsControl.GetDataObjectFromPoint(p);
            _dataObject = data != null ? new DataObject(data.GetType(), data) : null;

            _dragContainer = itemsControl.GetItemContainerFromPoint(p);
            if (_dragContainer != null)
                _dragItem = GetItemFromContainer(_dragContainer);

            if (data != null)
            {
                _isMouseDown = true;
                _dragStartPosition = p;
            }
        }

        void AssociatedObject_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (_isMouseDown)
            {
                ItemsControl itemsControl = (ItemsControl)sender;
                Point currentPosition = e.GetPosition(itemsControl);
                if ((_isDragging == false) && (Math.Abs(currentPosition.X - _dragStartPosition.X) > SystemParameters.MinimumHorizontalDragDistance) ||
                    (Math.Abs(currentPosition.Y - _dragStartPosition.Y) > SystemParameters.MinimumVerticalDragDistance))
                {
                    DragStarted(e.GetPosition(itemsControl));
                }
                e.Handled = true;
            }
        }

        void AssociatedObject_PreviewDragOver(object sender, DragEventArgs e)
        {
            UpdateDropIndex(e.GetPosition(this.AssociatedObject));
        }

        void AssociatedObject_PreviewDrop(object sender, DragEventArgs e)
        {
            UpdateDropIndex(e.GetPosition(this.AssociatedObject));
        }

        void AssociatedObject_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            _isMouseDown = false;
        }

        private void DragStarted(Point p)
        {
            if (!_isDragging)
            {
                _isDragging = true;

                if (_dragContainer != null)
                    _dragContainer.Opacity = 0.3;

                _currentDropIndex = FindDropIndex(p);

                DragDropEffects e = DragDrop.DoDragDrop(this.AssociatedObject, _dataObject, DragDropEffects.Copy | DragDropEffects.Move);

                ResetState();
            }
        }

        private void ResetState()
        {
            if (_dragContainer != null)
                _dragContainer.Opacity = 1.0;

            _isMouseDown = false;
            _isDragging = false;
            _dataObject = null;
            _dragItem = null;
            _dragContainer = null;
            _currentDropIndex = -1;
        }

        private void UpdateDropIndex(Point p)
        {
            if ((_lastCheckPoint - p).Length > SystemParameters.MinimumHorizontalDragDistance) // prevent too frequent call
            {
                int dropIndex = FindDropIndex(p);
                if (dropIndex != _currentDropIndex && dropIndex > -1)
                {
                    this.AssociatedObject.RemoveItem(_dataObject);
                    this.AssociatedObject.AddItem(_dataObject, dropIndex);
                    _currentDropIndex = dropIndex;
                }
                _lastCheckPoint = p;
            }
        }

        private int FindDropIndex(Point p)
        {
            ItemsControl itemsControl = this.AssociatedObject;
            UIElement dropTargetContainer = null;

            dropTargetContainer = itemsControl.GetItemContainerFromPoint(p);

            int index = -1;
            if (dropTargetContainer != null)
            {
                index = itemsControl.ItemContainerGenerator.IndexFromContainer(dropTargetContainer);

                if (!IsPointInTopHalf(p))
                    index = index++; // in second half of item, add after
            }
            else if (IsPointAfterAllItems(itemsControl, p))
            {
                // still within itemscontrol, but after all items
                index = itemsControl.Items.Count - 1;
            }

            return index;
        }

        public bool IsPointInTopHalf(Point p)
        {
            ItemsControl itemsControl = this.AssociatedObject;

            bool isInTopHalf = false;

            UIElement selectedItemContainer = itemsControl.GetItemContainerFromPoint(p);
            Point relativePosition = Mouse.GetPosition(selectedItemContainer);

            if (IsItemControlOrientationHorizontal())
                isInTopHalf = relativePosition.X < ((FrameworkElement)selectedItemContainer).ActualWidth / 2;
            else
                isInTopHalf = relativePosition.Y < ((FrameworkElement)selectedItemContainer).ActualHeight / 2;

            return isInTopHalf;
        }

        private bool IsItemControlOrientationHorizontal()
        {
            bool isHorizontal = false;
            Panel panel = GetItemsPanel();
            if (panel is WrapPanel)
                isHorizontal = ((WrapPanel)panel).Orientation == Orientation.Horizontal;
            else if (panel is StackPanel)
                isHorizontal = ((StackPanel)panel).Orientation == Orientation.Horizontal;

            return isHorizontal;
        }

        private UIElement GetItemFromContainer(UIElement container)
        {
            UIElement item = null;
            if (container != null)
                item = VisualTreeHelper.GetChild(container, 0) as UIElement;
            return item;
        }

        private Panel GetItemsPanel()
        {
            ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(this.AssociatedObject);
            Panel itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel;
            return itemsPanel;
        }

        private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
        {
            T child = default(T);

            int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < numVisuals; i++)
            {
                Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
                child = v as T;
                if (child == null)
                {
                    child = GetVisualChild<T>(v);
                }
                if (child != null)
                {
                    break;
                }
            }
            return child;
        }

        /// still needs some work
        private static bool IsPointAfterAllItems(ItemsControl itemsControl, Point point)
        {
            bool isAfter = false;

            UIElement target = itemsControl.GetLastItemContainer();
            Point targetPos = target.TransformToAncestor(itemsControl).Transform(new Point(0, 0));
            Point relativeToTarget = new Point(point.X - targetPos.X, point.Y - targetPos.Y);

            if (relativeToTarget.X >= 0 && relativeToTarget.Y >= 0)
            {
                var bounds = VisualTreeHelper.GetDescendantBounds(target);
                isAfter = !bounds.Contains(relativeToTarget);
            }
            return isAfter;
        }
    }

    public static class ItemsControlExtensions
    {
        public static object GetDataObjectFromPoint(this ItemsControl itemsControl, Point p)
        {
            UIElement element = itemsControl.InputHitTest(p) as UIElement;

            while (element != null)
            {
                if (element == itemsControl)
                    return null;

                object data = itemsControl.ItemContainerGenerator.ItemFromContainer(element);
                if (data != DependencyProperty.UnsetValue)
                    return data;
                else
                    element = VisualTreeHelper.GetParent(element) as UIElement;
            }
            return null;
        }

        public static UIElement GetItemContainerFromPoint(this ItemsControl itemsControl, Point p)
        {
            UIElement element = itemsControl.InputHitTest(p) as UIElement;

            while (element != null)
            {
                object data = itemsControl.ItemContainerGenerator.ItemFromContainer(element);

                if (data != DependencyProperty.UnsetValue)
                    return element;
                else
                    element = VisualTreeHelper.GetParent(element) as UIElement;
            }

            return element;
        }

        public static UIElement GetLastItemContainer(this ItemsControl itemsControl)
        {
            UIElement container = null;
            if (itemsControl.HasItems)
                container = itemsControl.GetItemContainerAtIndex(itemsControl.Items.Count - 1);

            return container;
        }

        public static UIElement GetItemContainerAtIndex(this ItemsControl itemsControl, int index)
        {
            UIElement container = null;

            if (itemsControl != null && itemsControl.Items.Count > index && itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                container = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as UIElement;
            else
                container = itemsControl;

            return container;
        }

        public static void AddItem(this ItemsControl itemsControl, IDataObject item, int insertIndex)
        {
            if (itemsControl.ItemsSource != null)
            {
                foreach (string format in item.GetFormats())
                {
                    object data = item.GetData(format);
                    IList iList = itemsControl.ItemsSource as IList;
                    if (iList != null)
                        iList.Insert(insertIndex, data);
                    else
                    {
                        Type type = itemsControl.ItemsSource.GetType();
                        Type genericList = type.GetInterface("IList`1");
                        if (genericList != null)
                            type.GetMethod("Insert").Invoke(itemsControl.ItemsSource, new object[] { insertIndex, data });
                    }
                }
            }
            else
                itemsControl.Items.Insert(insertIndex, item);
        }

        public static void RemoveItem(this ItemsControl itemsControl, IDataObject itemToRemove)
        {
            if (itemToRemove != null)
            {
                foreach (string format in itemToRemove.GetFormats())
                {
                    object data = itemToRemove.GetData(format);
                    int index = itemsControl.Items.IndexOf(data);
                    if (index > -1)
                        itemsControl.RemoveItemAt(index);
                }
            }
        }

        public static void RemoveItemAt(this ItemsControl itemsControl, int removeIndex)
        {
            if (removeIndex != -1 && removeIndex < itemsControl.Items.Count)
            {
                if (itemsControl.ItemsSource != null)
                {
                    IList iList = itemsControl.ItemsSource as IList;
                    if (iList != null)
                    {
                        iList.RemoveAt(removeIndex);
                    }
                    else
                    {
                        Type type = itemsControl.ItemsSource.GetType();
                        Type genericList = type.GetInterface("IList`1");
                        if (genericList != null)
                            type.GetMethod("RemoveAt").Invoke(itemsControl.ItemsSource, new object[] { removeIndex });
                    }
                }
                else
                    itemsControl.Items.RemoveAt(removeIndex);
            }
        }
    }
}

TileTypeToColorConverter:

public class TileTypeToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        SolidColorBrush brush = new SolidColorBrush();
        TileType type = (TileType)value;
        switch (type)
        {
            case TileType.Browser: brush.Color = Colors.Maroon; break;
            case TileType.Application: brush.Color = Colors.DodgerBlue; break;
            case TileType.Website: brush.Color = Colors.DarkGoldenrod; break;
        }
        return brush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
于 2013-04-04T12:02:27.860 回答
1

您可以简单地使用项目控制,i)。在项目面板中,只需给出您想要的行数和列数 ii)。如果要在此处动态生成按钮,只需将按钮列表分配给它。

<ItemsControl x:Name="lstButtons"
              Grid.Row="0"
              Grid.Column="1">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <UniformGrid Columns="4"
                   Rows="4" />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Button  Click="CLICK_EVENT_HERE"
               Style="Use metro Button Style here" />
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
于 2013-04-08T08:59:22.407 回答