Skip to content

Commit 0526049

Browse files
committed
WIP Replace template with Carousel
1 parent 95f38bc commit 0526049

10 files changed

Lines changed: 307 additions & 411 deletions
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Numerics;
6+
7+
namespace CommunityToolkit.WinUI.Controls;
8+
9+
public partial class Carousel
10+
{
11+
/// <summary>
12+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
13+
/// </summary>
14+
public static readonly DependencyProperty OrientationProperty =
15+
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Carousel), new PropertyMetadata(Orientation.Horizontal));
16+
17+
/// <summary>
18+
/// The backing <see cref="DependencyProperty"/> for the <see cref="SelectedIndex"/> property.
19+
/// </summary>
20+
public static readonly DependencyProperty SelectedIndexProperty =
21+
DependencyProperty.Register(nameof(SelectedIndex), typeof(int), typeof(Carousel), new PropertyMetadata(-1, OnCarouselSelectionPropertyChanged));
22+
23+
/// <summary>
24+
/// The backing <see cref="DependencyProperty"/> for the <see cref="SelectedItem"/> property.
25+
/// </summary>
26+
public static readonly DependencyProperty SelectedItemProperty =
27+
DependencyProperty.Register(nameof(SelectedItem), typeof(object), typeof(Carousel), new PropertyMetadata(null, OnCarouselSelectionPropertyChanged));
28+
29+
/// <summary>
30+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
31+
/// </summary>
32+
public static readonly DependencyProperty ItemSpacingProperty =
33+
DependencyProperty.Register(nameof(ItemSpacing), typeof(double), typeof(Carousel), new PropertyMetadata(0));
34+
35+
/// <summary>
36+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
37+
/// </summary>
38+
public static readonly DependencyProperty TransitionDurationProperty =
39+
DependencyProperty.Register(nameof(TransitionDuration), typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromMilliseconds(200)));
40+
41+
/// <summary>
42+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
43+
/// </summary>
44+
public static readonly DependencyProperty EasingFunctionProperty =
45+
DependencyProperty.Register(nameof(TransitionDuration), typeof(EasingFunctionBase), typeof(Carousel), new PropertyMetadata(null));
46+
47+
/// <summary>
48+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
49+
/// </summary>
50+
public static readonly DependencyProperty ItemDepthProperty =
51+
DependencyProperty.Register(nameof(ItemDepth), typeof(double), typeof(Carousel), new PropertyMetadata(0));
52+
53+
/// <summary>
54+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
55+
/// </summary>
56+
public static readonly DependencyProperty ItemRotationProperty =
57+
DependencyProperty.Register(nameof(ItemRotation), typeof(double), typeof(Carousel), new PropertyMetadata(Vector3.Zero));
58+
59+
/// <summary>
60+
/// The backing <see cref="DependencyProperty"/> for the <see cref="TransitionDuration"/> property.
61+
/// </summary>
62+
public static readonly DependencyProperty InvertPositiveProperty =
63+
DependencyProperty.Register(nameof(InvertPositive), typeof(bool), typeof(Carousel), new PropertyMetadata(true));
64+
65+
/// <summary>
66+
/// Gets or sets the orientation to render items.
67+
/// </summary>
68+
public Orientation Orientation
69+
{
70+
get => (Orientation)GetValue(OrientationProperty);
71+
set => SetValue(OrientationProperty, value);
72+
}
73+
74+
/// <summary>
75+
/// Gets or sets the index of the selected item.
76+
/// </summary>
77+
public int SelectedIndex
78+
{
79+
get => (int)GetValue(SelectedIndexProperty);
80+
set => SetValue(SelectedIndexProperty, value);
81+
}
82+
83+
/// <summary>
84+
/// Gets or sets the selected item.
85+
/// </summary>
86+
public object? SelectedItem
87+
{
88+
get => (object?)GetValue(SelectedItemProperty);
89+
set => SetValue(SelectedItemProperty, value);
90+
}
91+
92+
/// <summary>
93+
/// Gets or sets the selected item.
94+
/// </summary>
95+
public double? ItemSpacing
96+
{
97+
get => (double?)GetValue(ItemSpacingProperty);
98+
set => SetValue(ItemSpacingProperty, value);
99+
}
100+
101+
/// <summary>
102+
/// Gets or sets the transition animation duration.
103+
/// </summary>
104+
public TimeSpan TransitionDuration
105+
{
106+
get => (TimeSpan)GetValue(TransitionDurationProperty);
107+
set => SetValue(TransitionDurationProperty, value);
108+
}
109+
110+
/// <summary>
111+
/// Gets or sets the transition animation duration.
112+
/// </summary>
113+
public EasingFunctionBase? EasingFunction
114+
{
115+
get => (EasingFunctionBase?)GetValue(EasingFunctionProperty);
116+
set => SetValue(EasingFunctionProperty, value);
117+
}
118+
119+
/// <summary>
120+
/// Gets or sets the depth for unselected items.
121+
/// </summary>
122+
public double ItemDepth
123+
{
124+
get => (double)GetValue(TransitionDurationProperty);
125+
set => SetValue(TransitionDurationProperty, value);
126+
}
127+
128+
/// <summary>
129+
/// Gets or sets the rotation to apply to carousel items.
130+
/// </summary>
131+
public Vector3 ItemRotation
132+
{
133+
get => (Vector3)GetValue(ItemRotationProperty);
134+
set => SetValue(ItemRotationProperty, value);
135+
}
136+
137+
/// <summary>
138+
/// Gets or sets the rotation to apply to carousel items.
139+
/// </summary>
140+
public bool InvertPositive
141+
{
142+
get => (bool)GetValue(InvertPositiveProperty);
143+
set => SetValue(InvertPositiveProperty, value);
144+
}
145+
146+
private static void OnCarouselSelectionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
147+
{
148+
if (d is not Carousel carousel)
149+
return;
150+
}
151+
}

‎components/Carousel/src/Carousel.cs‎

Lines changed: 13 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -5,104 +5,27 @@
55
namespace CommunityToolkit.WinUI.Controls;
66

77
/// <summary>
8-
/// This is an example control based off of the BoxPanel sample here: https://docs.microsoft.com/windows/apps/design/layout/boxpanel-example-custom-panel. If you need this similar sort of layout component for an application, see UniformGrid in the Toolkit.
9-
/// It is provided as an example of how to inherit from another control like <see cref="Panel"/>.
10-
/// You can choose to start here or from the <see cref="Carousel_ClassicBinding"/> or <see cref="Carousel_xBind"/> example components. Remove unused components and rename as appropriate.
8+
/// An example templated control.
119
/// </summary>
12-
public partial class Carousel : Panel
10+
public partial class Carousel : ItemsControl
1311
{
1412
/// <summary>
15-
/// Identifies the <see cref="Orientation"/> property.
13+
/// Occurs when the selected item changes.
1614
/// </summary>
17-
public static readonly DependencyProperty OrientationProperty =
18-
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Carousel), new PropertyMetadata(null, OnOrientationChanged));
15+
public event SelectionChangedEventHandler? SelectionChanged;
1916

2017
/// <summary>
21-
/// Gets the preference of the rows/columns when there are a non-square number of children. Defaults to Vertical.
18+
/// Creates a new instance of the <see cref="Carousel"/> class.
2219
/// </summary>
23-
public Orientation Orientation
20+
public Carousel()
2421
{
25-
get { return (Orientation)GetValue(OrientationProperty); }
26-
set { SetValue(OrientationProperty, value); }
22+
this.DefaultStyleKey = typeof(Carousel);
2723
}
2824

29-
// Invalidate our layout when the property changes.
30-
private static void OnOrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
31-
{
32-
if (dependencyObject is Carousel panel)
33-
{
34-
panel.InvalidateMeasure();
35-
}
36-
}
37-
38-
// Store calculations we want to use between the Measure and Arrange methods.
39-
int _columnCount;
40-
double _cellWidth, _cellHeight;
41-
42-
protected override Size MeasureOverride(Size availableSize)
43-
{
44-
// Determine the square that can contain this number of items.
45-
var maxrc = (int)Math.Ceiling(Math.Sqrt(Children.Count));
46-
// Get an aspect ratio from availableSize, decides whether to trim row or column.
47-
var aspectratio = availableSize.Width / availableSize.Height;
48-
if (Orientation == Orientation.Vertical) { aspectratio = 1 / aspectratio; }
49-
50-
int rowcount;
51-
52-
// Now trim this square down to a rect, many times an entire row or column can be omitted.
53-
if (aspectratio > 1)
54-
{
55-
rowcount = maxrc;
56-
_columnCount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
57-
}
58-
else
59-
{
60-
rowcount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
61-
_columnCount = maxrc;
62-
}
63-
64-
// Now that we have a column count, divide available horizontal, that's our cell width.
65-
_cellWidth = (int)Math.Floor(availableSize.Width / _columnCount);
66-
// Next get a cell height, same logic of dividing available vertical by rowcount.
67-
_cellHeight = Double.IsInfinity(availableSize.Height) ? Double.PositiveInfinity : availableSize.Height / rowcount;
68-
69-
double maxcellheight = 0;
70-
71-
foreach (UIElement child in Children)
72-
{
73-
child.Measure(new Size(_cellWidth, _cellHeight));
74-
maxcellheight = (child.DesiredSize.Height > maxcellheight) ? child.DesiredSize.Height : maxcellheight;
75-
}
76-
77-
return LimitUnboundedSize(availableSize, maxcellheight);
78-
}
79-
80-
// This method limits the panel height when no limit is imposed by the panel's parent.
81-
// That can happen to height if the panel is close to the root of main app window.
82-
// In this case, base the height of a cell on the max height from desired size
83-
// and base the height of the panel on that number times the #rows.
84-
Size LimitUnboundedSize(Size input, double maxcellheight)
85-
{
86-
if (Double.IsInfinity(input.Height))
87-
{
88-
input.Height = maxcellheight * _columnCount;
89-
_cellHeight = maxcellheight;
90-
}
91-
return input;
92-
}
93-
94-
protected override Size ArrangeOverride(Size finalSize)
95-
{
96-
int count = 1;
97-
double x, y;
98-
foreach (UIElement child in Children)
99-
{
100-
x = (count - 1) % _columnCount * _cellWidth;
101-
y = ((int)(count - 1) / _columnCount) * _cellHeight;
102-
Point anchorPoint = new Point(x, y);
103-
child.Arrange(new Rect(anchorPoint, child.DesiredSize));
104-
count++;
105-
}
106-
return finalSize;
107-
}
25+
/// <summary>
26+
/// Returns the container used for each item
27+
/// </summary>
28+
/// <returns>Returns always a CarouselItem</returns>
29+
protected override DependencyObject GetContainerForItemOverride()
30+
=> new CarouselItem();
10831
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.WinUI.Controls;
6+
7+
/// <summary>
8+
/// Represents the container for an item in a <see cref="Carousel"/> control
9+
/// </summary>
10+
public partial class CarouselItem : SelectorItem
11+
{
12+
private const string PointerOverState = "PointerOver";
13+
private const string PointerOverSelectedState = "PointerOverSelected";
14+
private const string PressedState = "Pressed";
15+
private const string PressedSelectedState = "PressedSelected";
16+
private const string SelectedState = "Selected";
17+
private const string NormalState = "Normal";
18+
19+
/// <summary>
20+
/// Fired when the <see cref="CarouselItem"/> is selected.
21+
/// </summary>
22+
public event EventHandler? Selected;
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="CarouselItem"/> class.
26+
/// </summary>
27+
public CarouselItem()
28+
{
29+
DefaultStyleKey = typeof(CarouselItem);
30+
31+
RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
32+
}
33+
34+
/// <inheritdoc/>
35+
protected override void OnPointerEntered(PointerRoutedEventArgs e)
36+
{
37+
base.OnPointerEntered(e);
38+
39+
VisualStateManager.GoToState(this, IsSelected ? PointerOverSelectedState : PointerOverState, true);
40+
}
41+
42+
/// <inheritdoc/>
43+
protected override void OnPointerExited(PointerRoutedEventArgs e)
44+
{
45+
base.OnPointerExited(e);
46+
47+
VisualStateManager.GoToState(this, IsSelected ? SelectedState : NormalState, true);
48+
}
49+
50+
/// <inheritdoc/>
51+
protected override void OnPointerPressed(PointerRoutedEventArgs e)
52+
{
53+
base.OnPointerPressed(e);
54+
55+
VisualStateManager.GoToState(this, IsSelected ? PressedSelectedState : PressedState, true);
56+
}
57+
58+
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
59+
{
60+
var item = (CarouselItem)sender;
61+
62+
if (item.IsSelected)
63+
{
64+
Selected?.Invoke(this, EventArgs.Empty);
65+
VisualStateManager.GoToState(item, SelectedState, true);
66+
}
67+
else
68+
{
69+
VisualStateManager.GoToState(item, NormalState, true);
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)