Điều này nghe có vẻ giống như một nhiệm vụ thú vị, vì vậy tôi đã quyết định thực hiện điều gì đó dọc theo những gì bạn muốn. Tôi nghĩ rằng tôi có thể chia sẻ nó để bạn có thể cải thiện nó hoặc sử dụng nó theo ý thích của bạn. Trước hết, nhiệm vụ này vượt quá khả năng triển khai của tôi trong chỉ XAML. Tôi không nói rằng nó không thể được thực hiện, chỉ là nó là một chút khó khăn. Thay vào đó, tôi đã triển khai loại Bảng điều khiển của riêng mình (được gọi là GapPanel
). Điều này nghe có vẻ tồi tệ hơn, nhưng có một vài điều tốt đẹp về nó, tuy nhiên, giống như khả năng thực hiện RoutedEvent
s để trả lời trong XAML cho hoạt ảnh.
Vì vậy, đây là tải mã.Đầu tiên là 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>
Và (kinh tởm) của một bảng điều khiển ..
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));
}
}
}
Leave a comment nếu có bất cứ điều gì bạn cảm thấy tôi cần phải giải thích.
Ngoài ra, tôi không thể giúp quảng cáo cuốn sách "WPF4 Unleashed" của Adam Nathan. Nhiều nếu không phải tất cả mọi thứ tôi quản lý để làm ở trên được giải thích chi tiết trong cuốn sách này, vì vậy nó là một nguồn lực tuyệt vời cho bất cứ ai muốn tìm hiểu thêm về WPF.
Ý tưởng tuyệt vời! Tôi nghĩ rằng Infragistics có một cái gì đó như thế –
Cảm ơn @HighCore. Tải xuống bản dùng thử của Infragistic để xem bạn có thể tham khảo điều khiển nào. – erodewald