Skip to content

Commit ebed1fa

Browse files
committed
chore: add AnimationGrid and code example
1 parent f09a349 commit ebed1fa

File tree

8 files changed

+290
-0
lines changed

8 files changed

+290
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<UserControl
2+
x:Class="WPFDevelopers.Samples.ExampleViews.AnimationGridExample"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:controls="clr-namespace:WPFDevelopers.Samples.Controls"
6+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
7+
xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"
8+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
9+
xmlns:wd="https://github.com/WPFDevelopersOrg/WPFDevelopers"
10+
d:DesignHeight="450"
11+
d:DesignWidth="800"
12+
mc:Ignorable="d">
13+
<UserControl.Resources>
14+
<DataTemplate x:Key="GridItemTemplate">
15+
<Button Content="{Binding Content}" />
16+
</DataTemplate>
17+
<Style TargetType="ToggleButton">
18+
<Setter Property="Width" Value="30" />
19+
<Setter Property="Height" Value="20" />
20+
<Setter Property="Template">
21+
<Setter.Value>
22+
<ControlTemplate TargetType="ToggleButton">
23+
<Border
24+
x:Name="border"
25+
Background="Transparent"
26+
BorderBrush="Transparent"
27+
BorderThickness="{TemplateBinding BorderThickness}"
28+
CornerRadius="4">
29+
<wd:PathIcon x:Name="pathIcon" Data="{Binding Data}" />
30+
</Border>
31+
<ControlTemplate.Triggers>
32+
<Trigger Property="IsChecked" Value="True">
33+
<Setter Property="Foreground" Value="{DynamicResource WD.PrimaryBrush}" />
34+
</Trigger>
35+
<Trigger Property="IsMouseOver" Value="True">
36+
<Setter TargetName="border" Property="BorderBrush" Value="{DynamicResource WD.PrimaryBrush}" />
37+
</Trigger>
38+
</ControlTemplate.Triggers>
39+
</ControlTemplate>
40+
</Setter.Value>
41+
</Setter>
42+
</Style>
43+
<DataTemplate x:Key="ToggleItemTemplate">
44+
<ToggleButton
45+
Command="{Binding IsSelectedCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
46+
CommandParameter="{Binding .}"
47+
IsChecked="{Binding IsSelected}"
48+
Tag="{Binding Content}" />
49+
</DataTemplate>
50+
</UserControl.Resources>
51+
<controls:CodeViewer Header="{Binding NavigateMenuItem.Name}">
52+
<Grid>
53+
<wd:AnimationGrid
54+
x:Name="MyPanel"
55+
ItemTemplate="{StaticResource GridItemTemplate}"
56+
ItemsSource="{Binding GridItems, RelativeSource={RelativeSource AncestorType=UserControl}}" />
57+
<Border
58+
Margin="0,10"
59+
Padding="6"
60+
HorizontalAlignment="Center"
61+
VerticalAlignment="Top"
62+
Background="{DynamicResource WD.BackgroundBrush}"
63+
CornerRadius="3"
64+
Effect="{StaticResource WD.PrimaryShadowDepth}">
65+
<ItemsControl ItemTemplate="{StaticResource ToggleItemTemplate}" ItemsSource="{Binding GridItems, RelativeSource={RelativeSource AncestorType=UserControl}}">
66+
<ItemsControl.ItemsPanel>
67+
<ItemsPanelTemplate>
68+
<StackPanel Orientation="Horizontal" />
69+
</ItemsPanelTemplate>
70+
</ItemsControl.ItemsPanel>
71+
</ItemsControl>
72+
</Border>
73+
</Grid>
74+
<controls:CodeViewer.SourceCodes>
75+
<controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/AnimationGridExample.xaml" CodeType="Xaml" />
76+
<controls:SourceCodeModel CodeSource="/WPFDevelopers.SamplesCode;component/ExampleViews/AnimationGridExample.xaml.cs" CodeType="CSharp" />
77+
</controls:CodeViewer.SourceCodes>
78+
</controls:CodeViewer>
79+
</UserControl>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System.Collections.Generic;
2+
using System.Collections.ObjectModel;
3+
using System.Linq;
4+
using System.Windows;
5+
using System.Windows.Controls;
6+
using System.Windows.Input;
7+
using WPFDevelopers.Samples.Helpers;
8+
9+
namespace WPFDevelopers.Samples.ExampleViews
10+
{
11+
/// <summary>
12+
/// AnimationGridExample.xaml 的交互逻辑
13+
/// </summary>
14+
public partial class AnimationGridExample : UserControl
15+
{
16+
public ObservableCollection<GridItem> GridItems
17+
{
18+
get { return (ObservableCollection<GridItem>)GetValue(GridItemsProperty); }
19+
set { SetValue(GridItemsProperty, value); }
20+
}
21+
22+
public static readonly DependencyProperty GridItemsProperty =
23+
DependencyProperty.Register("GridItems", typeof(ObservableCollection<GridItem>), typeof(AnimationGridExample), new PropertyMetadata(null));
24+
public AnimationGridExample()
25+
{
26+
InitializeComponent();
27+
Loaded += OnAnimatedGridExample_Loaded;
28+
}
29+
30+
private void OnAnimatedGridExample_Loaded(object sender, RoutedEventArgs e)
31+
{
32+
var list = new List<GridItem>();
33+
list.Add(new GridItem { Content = "Single", Data = "M0.5,0.5 L60.5,0.5 L60.5,43.26 L0.5,43.26 z", IsSelected = true });
34+
list.Add(new GridItem { Content = "Dual", Data = "M0,0 L61,0 L61,43.760002 L0,43.760002 z M25.5,0 L35.5,0 L35.5,43.760002 L25.5,43.760002 z" });
35+
list.Add(new GridItem { Content = "Three", Data = "M0,0 L61,0 L61,43.760002 L0,43.760002 z M17,0.5 L22,0.5 L22,43.260002 L17,43.260002 z M39,0.5 L44,0.5 L44,43.260002 L39,43.260002 z" });
36+
GridItems = new ObservableCollection<GridItem>(list);
37+
}
38+
39+
public ICommand IsSelectedCommand => new RelayCommand(param =>
40+
{
41+
if (param == null) return;
42+
var item = (GridItem)param;
43+
if (item == null) return;
44+
MyPanel.ShowItem(item);
45+
});
46+
47+
}
48+
public class GridItem : ViewModelBase
49+
{
50+
public string Content { get; set; }
51+
public string Data { get; set; }
52+
53+
private bool _isSelected;
54+
public bool IsSelected
55+
{
56+
get => _isSelected;
57+
set { _isSelected = value; NotifyPropertyChange("IsSelected"); }
58+
}
59+
}
60+
}

src/WPFDevelopers.Samples.Shared/Helpers/MenuEnum.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public enum MenuEnum
9595
NavScrollPanel,
9696
IconicThumbnail,
9797
GestureUnlock,
98+
AnimationGrid,
9899
VirtualizingWrapPanel,
99100
AcrylicBlur,
100101
TaskbarInfo

src/WPFDevelopers.Samples.Shared/ViewModels/MainVM.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,9 @@ void MenuItemSelection(string _menuName)
391391
case MenuEnum.GestureUnlock:
392392
ControlPanel = new GestureUnlockExample();
393393
break;
394+
case MenuEnum.AnimationGrid:
395+
ControlPanel = new AnimationGridExample();
396+
break;
394397
case MenuEnum.VirtualizingWrapPanel:
395398
ControlPanel = new VirtualizingWrapPanel();
396399
new VirtualizingWrapPanelExample().MaskShowDialog();

src/WPFDevelopers.Samples.Shared/WPFDevelopers.Samples.Shared.projitems

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@
6363
<SubType>Code</SubType>
6464
<DependentUpon>AcrylicBlurWindowExample.xaml</DependentUpon>
6565
</Compile>
66+
<Compile Include="$(MSBuildThisFileDirectory)ExampleViews\AnimationGridExample.xaml.cs">
67+
<DependentUpon>AnimationGridExample.xaml</DependentUpon>
68+
</Compile>
6669
<Compile Include="$(MSBuildThisFileDirectory)ExampleViews\AnimationAudioExample.xaml.cs">
6770
<SubType>Code</SubType>
6871
<DependentUpon>AnimationAudioExample.xaml</DependentUpon>
@@ -616,6 +619,10 @@
616619
<SubType>Designer</SubType>
617620
<Generator>MSBuild:Compile</Generator>
618621
</Page>
622+
<Page Include="$(MSBuildThisFileDirectory)ExampleViews\AnimationGridExample.xaml">
623+
<SubType>Designer</SubType>
624+
<Generator>MSBuild:Compile</Generator>
625+
</Page>
619626
<Page Include="$(MSBuildThisFileDirectory)ExampleViews\AnimationAudioExample.xaml">
620627
<Generator>MSBuild:Compile</Generator>
621628
<SubType>Designer</SubType>

src/WPFDevelopers.SamplesCode/WPFDevelopers.SamplesCode.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\NavScrollPanelExample.xaml.cs" Link="ExampleViews\NavScrollPanelExample.xaml.cs" DependentUpon="NavScrollPanelExample.xaml" />
162162
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\IconicThumbnailWindowExample.xaml.cs" Link="ExampleViews\IconicThumbnailWindowExample.xaml.cs" DependentUpon="IconicThumbnailWindowExample.xaml" />
163163
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\GestureUnlockExample.xaml.cs" Link="ExampleViews\GestureUnlockExample.xaml.cs" DependentUpon="GestureUnlockExample.xaml" />
164+
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\AnimationGridExample.xaml.cs" Link="ExampleViews\AnimationGridExample.xaml.cs" DependentUpon="AnimationGridExample.xaml" />
164165
</ItemGroup>
165166
<ItemGroup>
166167
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\AboutWindow.xaml">
@@ -550,5 +551,8 @@
550551
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\GestureUnlockExample.xaml">
551552
<Link>ExampleViews\GestureUnlockExample.xaml</Link>
552553
</Resource>
554+
<Resource Include="..\WPFDevelopers.Samples.Shared\ExampleViews\AnimationGridExample.xaml">
555+
<Link>ExampleViews\AnimationGridExample.xaml</Link>
556+
</Resource>
553557
</ItemGroup>
554558
</Project>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Windows;
6+
using System.Windows.Controls;
7+
using System.Windows.Media.Animation;
8+
9+
namespace WPFDevelopers.Controls
10+
{
11+
public class AnimationGrid : Grid
12+
{
13+
private readonly object _syncLock = new object();
14+
15+
public static readonly DependencyProperty ItemsSourceProperty =
16+
DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(AnimationGrid),
17+
new PropertyMetadata(null, OnItemsSourceChanged));
18+
19+
public static readonly DependencyProperty ItemTemplateProperty =
20+
DependencyProperty.Register(nameof(ItemTemplate), typeof(DataTemplate), typeof(AnimationGrid),
21+
new PropertyMetadata(null));
22+
23+
private readonly Dictionary<object, FrameworkElement> _itemMap = new Dictionary<object, FrameworkElement>();
24+
private readonly HashSet<object> _visibleItems = new HashSet<object>();
25+
26+
public IEnumerable ItemsSource
27+
{
28+
get => (IEnumerable)GetValue(ItemsSourceProperty);
29+
set => SetValue(ItemsSourceProperty, value);
30+
}
31+
32+
public DataTemplate ItemTemplate
33+
{
34+
get => (DataTemplate)GetValue(ItemTemplateProperty);
35+
set => SetValue(ItemTemplateProperty, value);
36+
}
37+
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
38+
{
39+
if (d is AnimationGrid panel)
40+
{
41+
panel.InitializeItems();
42+
}
43+
}
44+
45+
private void InitializeItems()
46+
{
47+
Children.Clear();
48+
ColumnDefinitions.Clear();
49+
_itemMap.Clear();
50+
_visibleItems.Clear();
51+
52+
if (ItemsSource == null || ItemTemplate == null) return;
53+
54+
foreach (var item in ItemsSource)
55+
{
56+
var content = (FrameworkElement)ItemTemplate.LoadContent();
57+
content.DataContext = item;
58+
content.Visibility = Visibility.Collapsed;
59+
content.Width = 0;
60+
_itemMap[item] = content;
61+
Children.Add(content);
62+
}
63+
if (_itemMap.Count > 0)
64+
{
65+
var first = _itemMap.First();
66+
_visibleItems.Add(first.Key);
67+
first.Value.Visibility = Visibility.Visible;
68+
UpdateLayoutAnimated();
69+
}
70+
}
71+
72+
public void ShowItem(object item)
73+
{
74+
lock (_syncLock)
75+
{
76+
if (!_itemMap.ContainsKey(item))
77+
return;
78+
if (_visibleItems.Contains(item))
79+
_visibleItems.Remove(item);
80+
else
81+
_visibleItems.Add(item);
82+
_itemMap[item].Visibility = Visibility.Visible;
83+
UpdateLayoutAnimated();
84+
}
85+
}
86+
87+
private void UpdateLayoutAnimated()
88+
{
89+
ColumnDefinitions.Clear();
90+
var visibleCount = Math.Max(1, _visibleItems.Count);
91+
var width = this.Width;
92+
var targetWidth = ActualWidth / visibleCount;
93+
int index = 0;
94+
foreach (var item in _itemMap.Keys)
95+
{
96+
ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
97+
var element = _itemMap[item];
98+
SetColumn(element, index);
99+
if (_visibleItems.Contains(item))
100+
{
101+
AnimateWidth(element, targetWidth);
102+
}
103+
else
104+
{
105+
AnimateWidth(element, 0, () => element.Visibility = Visibility.Collapsed);
106+
}
107+
index++;
108+
}
109+
}
110+
111+
private void AnimateWidth(FrameworkElement element, double targetWidth, Action completed = null)
112+
{
113+
var anim = new DoubleAnimation
114+
{
115+
To = targetWidth,
116+
Duration = TimeSpan.FromMilliseconds(300),
117+
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
118+
};
119+
if (completed != null)
120+
{
121+
anim.Completed += delegate { completed(); };
122+
}
123+
element.BeginAnimation(WidthProperty, anim);
124+
}
125+
126+
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
127+
{
128+
base.OnRenderSizeChanged(sizeInfo);
129+
if (_visibleItems.Count > 0)
130+
{
131+
UpdateLayoutAnimated();
132+
}
133+
}
134+
}
135+
}

src/WPFDevelopers.Shared/WPFDevelopers.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<Import_RootNamespace>WPFDevelopers</Import_RootNamespace>
1010
</PropertyGroup>
1111
<ItemGroup>
12+
<Compile Include="$(MSBuildThisFileDirectory)Controls\AnimationGrid\AnimationGrid.cs" />
1213
<Compile Include="$(MSBuildThisFileDirectory)Controls\BaseControls\ScaleBase.cs" />
1314
<Compile Include="$(MSBuildThisFileDirectory)Controls\BaseControls\SliderRepeatButton.cs" />
1415
<Compile Include="$(MSBuildThisFileDirectory)Controls\BaseControls\WDBorder.cs" />

0 commit comments

Comments
 (0)