2013-03-27 29 views
9

Tôi cần tạo các nút có định dạng tốt, giống như trang bắt đầu của Windows 8. Có bộ công cụ nào có sẵn cho một ListView tùy chỉnh có thể hỗ trợ chế độ xem dạng lưới hoặc chế độ xem lưới, với một số định dạng và có thể là một số tùy chọn hoạt ảnh hay không.Bộ công cụ WPF cho chế độ xem danh sách lát gạch

Tôi đã cố gắng tạo danh sách tùy chỉnh của riêng mình nhưng có vẻ như đó là một nhiệm vụ phức tạp.

Trả lời

32

Tôi không biết về điều khiển lát gạch miễn phí đẹp mắt. DevExpress có phiên bản thương mại đẹp mắt.

Nếu bạn muốn chỉ định các yêu cầu chính xác của mình (nghĩa là bạn cần cấu hình các thuộc tính nào, loại hoạt ảnh nào, ...) và tôi tìm thời gian, tôi sẽ cho nó một chút.

EDIT: Tôi đã tạo một ItemsControl với một WrapPanel là ItemsPanel. Sử dụng mẫu MVVM, việc mở rộng các điều khiển theo nhu cầu của bạn và các đối tượng dữ liệu của bạn không quá khó. Khá khó làm là phần hành vi DragDrop - chắc chắn vẫn còn một số chỗ để cải thiện. Tôi không bao gồm các hình ảnh.

enter image description here

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(); 
    } 
} 
+0

hoạt ảnh-> có thể liệt kê các mục trong danh sách một lần sau một số độ trễ, kéo và thả đối tượng cho các mục xếp kề, khả năng thêm hình ảnh hoặc hộp kiểm. –

+0

gạch luôn có cùng kích thước, cùng lề? bạn cần cấu hình gì trong các ô xếp kề (ví dụ: màu, văn bản, biểu tượng, loại: hộp kiểm/nút bình thường, ...)? kiểm soát hoạt động như thế nào khi nó được thay đổi kích cỡ? –

+0

có cùng kích thước, cùng lề và có thể cấu hình-> mọi thứ (hình nền, văn bản, biểu tượng), khi thay đổi kích cỡ, nó phải chứa không gian cho các mục xếp kề khác và các mục xếp kề khác sẽ chuyển động trơn tru khi thay đổi kích thước. –

1

Bạn chỉ cần sử dụng Điều khiển mục, i). Trong Bảng điều khiển mục chỉ cung cấp số hàng và cột bạn muốn ii). Nếu nút bạn muốn ở đây sẽ được tạo động chỉ cần gán danh sách các Nút cho nút đó.

<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> 
Các vấn đề liên quan