8
  • 滚动是水平的
  • 触摸驱动。
  • 项目向下流动,然后进入下一列
  • 触摸一个项目会将面板滚动到一个设定点,因此详细视图将始终位于同一位置。
  • 下一列将“中断”并向右移动,以在所选项目的上下文中显示详细信息窗格。
  • 触摸任何可见项目(在不同的列中)将“关闭”显示的细节,然后将新选定的项目动画到左侧静态点并再次切掉下一列以显示细节。触摸同一列中的任何可见项目只会执行淡出动画。

以下是一些简单的模拟:

其中 90% 对我来说很简单,但是创建一个可以“分离”自身以显示项目的包装面板的过程在很大程度上让我难以理解。任何意见,将不胜感激。

4

2 回答 2

2

解决方案之一是:

您可以通过更改其中一些按钮的边距来分隔包装面板内的按钮(在 grid 内)(当然,如果您想保持按钮大小并避免将它们移动到下一行,您还需要更改窗口的大小)。

例如,如果您有 4 列和三行名为按钮 1、2、3 等的按钮...当单击第一列中的按钮时,按钮 2、6、10 得到:

  new thickness(space,0,0,0);

这将按变量空间的值移动右侧的所有按钮;

接着

  window.width += space;

然后将作为网格子级的文本框定位在具有宽度空间的合适位置。

撤消时

      new thickness(0,0,0,0);

      window.width -= space;

它对我很有效,但我对其他解决方案很好奇。

于 2013-03-05T17:36:25.747 回答
2

这听起来像是一项有趣的任务,所以我决定按照你想要的方式实现一些东西。我想我可以分享它,以便您可以改进它或根据自己的喜好使用它。首先,这项任务超出了我在XAML 中实现的技能。我不是说做不到,只是有点难。相反,我实现了自己的 Panel 类型(称为GapPanel)。这听起来可能更糟,但它仍然有一些好处,例如实现RoutedEvents 以在 XAML 中响应动画的可能性。

所以,这是代码的负载。首先是 XAML

<Window x:Class="SlidingWrapPanel.SecondAttempt"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SlidingWrapPanel"
        Title="Wrapped items with details pane" Height="250" Width="600">
    <Window.Resources>
        <ControlTemplate TargetType="Button" x:Key="ItemButtonTemplate">
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Trigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="ButtonBorder"
                                            Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                            To="#999999" Duration="0:0:0.5" />
                            </Storyboard>
                        </BeginStoryboard>
                    </Trigger.EnterActions>
                    <Trigger.ExitActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetName="ButtonBorder"
                                            Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                            To="#3e3e3e" Duration="0:0:0.5" />
                            </Storyboard>
                        </BeginStoryboard>
                    </Trigger.ExitActions>
                </Trigger>
            </ControlTemplate.Triggers>
            <Border x:Name="ButtonBorder" Background="#3e3e3e" BorderBrush="#222" BorderThickness="1">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding Content}" Margin="0" />
            </Border>
        </ControlTemplate>
        <Style x:Key="ItemGridStyle" TargetType="{x:Type Grid}">
            <Setter Property="Background" Value="#3e3e3e"/>
            <Setter Property="Width" Value="150"/>
            <Setter Property="Height" Value="100"/>
            <Setter Property="Margin" Value="1"/>
        </Style>
        <Style x:Key="ItemButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <Binding Source="{StaticResource ItemButtonTemplate}"/>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="DetailsGridStyle" TargetType="{x:Type Grid}">
            <Setter Property="Background" Value="#3e3e3e"/>
            <Setter Property="Width" Value="160"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="RenderTransform">
                <Setter.Value>
                    <TranslateTransform X="-160" />
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="DetailsTextStyle" TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontFamily" Value="Segoe WP Light"/>
            <Setter Property="Margin" Value="15"/>
        </Style>
        <Storyboard x:Key="ExpandColumnAnimation">
            <DoubleAnimation Storyboard.TargetProperty="GapWidth" Storyboard.TargetName="ItemsPanel"
                             From="0" To="{Binding ActualWidth, ElementName=DetailsPanel}" Duration="0:0:0.75">
                <DoubleAnimation.EasingFunction>
                    <QuinticEase EasingMode="EaseOut"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                           Storyboard.TargetName="DetailsPanel">
                <DiscreteDoubleKeyFrame KeyTime="0" Value="{Binding GapX, ElementName=ItemsPanel}"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
        <Storyboard x:Key="CollapseColumnAnimation">
            <DoubleAnimation Storyboard.TargetProperty="GapWidth" Storyboard.TargetName="ItemsPanel"
                             To="0" Duration="0:0:0.5">
                <DoubleAnimation.EasingFunction>
                    <QuinticEase EasingMode="EaseIn"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                           Storyboard.TargetName="DetailsPanel">
                <DiscreteDoubleKeyFrame KeyTime="0:0:0.5" Value="-160"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    <Grid>
        <Grid>
            <Grid x:Name="DetailsPanel" Style="{StaticResource DetailsGridStyle}">
                <ScrollViewer>
                    <TextBlock Style="{StaticResource DetailsTextStyle}">
                        <Run Text="Details" FontSize="18"/>
                        <LineBreak />
                        <Run Text="Some text"/>
                    </TextBlock>
                </ScrollViewer>
            </Grid>
        </Grid>
        <local:GapPanel x:Name="ItemsPanel">
            <local:GapPanel.Triggers>
                <EventTrigger RoutedEvent="local:GapPanel.ColumnChanged">
                    <BeginStoryboard Storyboard="{StaticResource ExpandColumnAnimation}"/>
                </EventTrigger>
                <EventTrigger RoutedEvent="local:GapPanel.CloseGap">
                    <BeginStoryboard Storyboard="{StaticResource CollapseColumnAnimation}"/>
                </EventTrigger>
            </local:GapPanel.Triggers>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 1" />
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 2" />
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 3" />
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 4"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 5"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 6"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 7"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 8"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 9"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 10"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 11"/>
            </Grid>
            <Grid Style="{StaticResource ItemGridStyle}">
                <Button Style="{StaticResource ItemButtonStyle}" Content="Item 12"/>
            </Grid>
        </local:GapPanel>
    </Grid>
</Window>

和面板的(可憎的)..

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace SlidingWrapPanel {
    public class GapPanel : Panel, INotifyPropertyChanged {
        private readonly IDictionary<UIElement, int> columns;
        private readonly IDictionary<int, double> gapCoordinates;
        private object opened;

        public static readonly DependencyProperty GapColumnProperty =
            DependencyProperty.Register("GapColumn", typeof(int), typeof(GapPanel), new FrameworkPropertyMetadata(default(int), FrameworkPropertyMetadataOptions.AffectsRender, columnChanged));

        public static readonly DependencyProperty GapWidthProperty =
            DependencyProperty.Register("GapWidth", typeof(double), typeof(GapPanel), new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender));

        public static readonly RoutedEvent ColumnChangedEvent;
        public static readonly RoutedEvent CloseGapEvent;

        static GapPanel() {
            ColumnChangedEvent = EventManager.RegisterRoutedEvent("ColumnChanged", RoutingStrategy.Bubble, typeof(RoutedEvent), typeof(GapPanel));
            CloseGapEvent = EventManager.RegisterRoutedEvent("CloseGap", RoutingStrategy.Bubble, typeof(RoutedEvent), typeof(GapPanel));
        }

        public GapPanel() {
            columns = new Dictionary<UIElement, int>();
            gapCoordinates = new Dictionary<int, double>();
            GapWidth = 0;
            GapColumn = -1;
        }

        public int GapColumn {
            get { return (int)GetValue(GapColumnProperty); }
            set { SetValue(GapColumnProperty, value); }
        }

        public double GapWidth {
            get { return (double)GetValue(GapWidthProperty); }
            set { SetValue(GapWidthProperty, value); }
        }

        public double GapX {
            get {
                double value;
                gapCoordinates.TryGetValue(GapColumn, out value);
                return value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public event RoutedEventHandler ColumnChanged {
            add { AddHandler(ColumnChangedEvent, value); }
            remove { RemoveHandler(ColumnChangedEvent, value); }
        }

        public event RoutedEventHandler CloseGap {
            add { AddHandler(CloseGapEvent, value); }
            remove { RemoveHandler(CloseGapEvent, value); }
        }

        protected override Size ArrangeOverride(Size finalSize) {
            Point location = new Point();
            double position = 0;
            double columnWidth = 0;
            int col = 0;
            foreach (UIElement child in Children) {
                columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
                position += child.DesiredSize.Height;
                if (position > finalSize.Height && columnWidth > 0) {
                    location.X += columnWidth;
                    if (col == GapColumn) {
                        location.X += GapWidth;
                    }
                    ++col;
                    columnWidth = 0;
                    position = child.DesiredSize.Height;
                    location.Y = 0;
                }
                columns[child] = col;
                child.Arrange(new Rect(location, child.DesiredSize));

                location.Y = position;
            }

            return finalSize;
        }

        protected override Size MeasureOverride(Size availableSize) {
            double width = 0, height = 0;
            double position = 0;
            double columnWidth = 0;
            int col = 0;
            foreach (UIElement child in Children) {
                child.Measure(availableSize);

                columnWidth = Math.Max(columnWidth, child.DesiredSize.Width);
                position += child.DesiredSize.Height;
                if (position > availableSize.Height && columnWidth > 0) {
                    width += columnWidth;
                    ++col;
                    columnWidth = child.DesiredSize.Width;
                    position = child.DesiredSize.Height;
                    height = Math.Max(height, child.DesiredSize.Height);
                }
                gapCoordinates[col] = width + columnWidth;
            }

            return new Size(width + GapWidth, height);
        }

        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) {
            base.OnVisualChildrenChanged(visualAdded, visualRemoved);
            UIElement element = visualAdded as UIElement;
            if (element != null) {
                element.PreviewMouseLeftButtonDown += expandAtVisual;
            }
            element = visualRemoved as UIElement;
            if (element != null) {
                element.PreviewMouseLeftButtonDown -= expandAtVisual;
            }
        }

        private void expandAtVisual(object sender, MouseButtonEventArgs e) {
            // find element column
            int column = columns[(UIElement)sender];
            GapWidth = 0;
            GapColumn = column;
            if (opened == sender) {
                RaiseEvent(new RoutedEventArgs(CloseGapEvent, this));
            }
            opened = sender;
        }

        private void onPropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        private static void columnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            ((GapPanel)d).onPropertyChanged("GapX");
            ((GapPanel)d).RaiseEvent(new RoutedEventArgs(ColumnChangedEvent, d));
        }
    }
}

如果您觉得有什么需要我解释的,请发表评论。

另外,我不禁要宣传 Adam Nathan 的《WPF4 Unleashed》一书。本书对我在上面所做的所有事情进行了详细的解释,因此对于任何想要了解更多关于 WPF 的人来说,它都是一个很好的资源。

于 2013-03-06T03:04:48.657 回答