Skip to content

Commit 57abc6a

Browse files
committed
Add CompareSlider
1 parent 8bc024f commit 57abc6a

14 files changed

Lines changed: 737 additions & 1 deletion

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ Install-Package DevWinUI
150150
## 🔥 DevWinUI.Controls 🔥
151151
### ⚡ What’s Inside? ⚡
152152

153+
- ✨ CompareSlider
153154
- ✨ TransitioningContentControl
154155
- ✨ DateTimePicker
155156
- ✨ CalendarWithClock
@@ -255,6 +256,10 @@ Install-Package DevWinUI.ContextMenu
255256

256257
## 🕰️ History 🕰️
257258

259+
### CompareSlider
260+
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/CompareSlider.gif)
261+
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/CompareSlider2.gif)
262+
258263
### TransitioningContentControl
259264
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/TransitioningContentControl.gif)
260265

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
using Microsoft.UI.Xaml.Controls.Primitives;
2+
using Microsoft.UI.Xaml.Shapes;
3+
4+
namespace DevWinUI;
5+
6+
[TemplatePart(Name = BeforeImageElement, Type = typeof(Image))]
7+
[TemplatePart(Name = RectangleElement, Type = typeof(Rectangle))]
8+
[TemplatePart(Name = PART_Thumb, Type = typeof(Thumb))]
9+
[TemplatePart(Name = PART_Line, Type = typeof(Line))]
10+
public partial class CompareSlider : Control
11+
{
12+
public bool ShowLineAndThumb
13+
{
14+
get { return (bool)GetValue(ShowLineAndThumbProperty); }
15+
set { SetValue(ShowLineAndThumbProperty, value); }
16+
}
17+
18+
public static readonly DependencyProperty ShowLineAndThumbProperty =
19+
DependencyProperty.Register(nameof(ShowLineAndThumb), typeof(bool), typeof(CompareSlider), new PropertyMetadata(true));
20+
21+
public Style LineStyle
22+
{
23+
get { return (Style)GetValue(LineStyleProperty); }
24+
set { SetValue(LineStyleProperty, value); }
25+
}
26+
27+
public static readonly DependencyProperty LineStyleProperty =
28+
DependencyProperty.Register(nameof(LineStyle), typeof(Style), typeof(CompareSlider), new PropertyMetadata(default(Style)));
29+
30+
public Style ThumbStyle
31+
{
32+
get { return (Style)GetValue(ThumbStyleProperty); }
33+
set { SetValue(ThumbStyleProperty, value); }
34+
}
35+
36+
public static readonly DependencyProperty ThumbStyleProperty =
37+
DependencyProperty.Register(nameof(ThumbStyle), typeof(Style), typeof(CompareSlider), new PropertyMetadata(default(Style)));
38+
39+
public double Value
40+
{
41+
get { return (double)GetValue(ValueProperty); }
42+
set { SetValue(ValueProperty, value); }
43+
}
44+
45+
public static readonly DependencyProperty ValueProperty =
46+
DependencyProperty.Register(nameof(Value), typeof(double), typeof(CompareSlider), new PropertyMetadata(0.0, OnValueChanged));
47+
48+
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
49+
{
50+
var ctl = (CompareSlider)d;
51+
if (ctl != null)
52+
{
53+
ctl.ValueChanged?.Invoke(ctl, (double)e.NewValue);
54+
ctl.UpdateValue((double)e.NewValue);
55+
}
56+
}
57+
58+
public ImageSource SourceImage
59+
{
60+
get => (ImageSource)GetValue(SourceContentProperty);
61+
set => SetValue(SourceContentProperty, value);
62+
}
63+
public static readonly DependencyProperty SourceContentProperty =
64+
DependencyProperty.Register(nameof(SourceImage), typeof(ImageSource), typeof(CompareSlider), new PropertyMetadata(default(ImageSource)));
65+
66+
public ImageSource TargetImage
67+
{
68+
get => (ImageSource)GetValue(TargetContentProperty);
69+
set => SetValue(TargetContentProperty, value);
70+
}
71+
public static readonly DependencyProperty TargetContentProperty =
72+
DependencyProperty.Register(nameof(TargetImage), typeof(ImageSource), typeof(CompareSlider), new PropertyMetadata(default(ImageSource)));
73+
74+
public Orientation Orientation
75+
{
76+
get { return (Orientation)GetValue(OrientationProperty); }
77+
set { SetValue(OrientationProperty, value); }
78+
}
79+
public static readonly DependencyProperty OrientationProperty =
80+
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(CompareSlider), new PropertyMetadata(Orientation.Horizontal, OnOrientationChanged));
81+
82+
private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
83+
{
84+
var ctl = (CompareSlider)d;
85+
if (ctl != null)
86+
{
87+
ctl.UpdateTemplate();
88+
}
89+
}
90+
private void UpdateTemplate()
91+
{
92+
Template = Orientation switch
93+
{
94+
Orientation.Horizontal => HorizontalTemplate,
95+
Orientation.Vertical => VerticalTemplate,
96+
_ => Template
97+
};
98+
}
99+
public event EventHandler<double> ValueChanged;
100+
public ControlTemplate? HorizontalTemplate { get; set; }
101+
public ControlTemplate? VerticalTemplate { get; set; }
102+
103+
private const string BeforeImageElement = "PART_BeforeImage";
104+
private const string RectangleElement = "PART_Rectangle";
105+
private const string PART_Line = "PART_Line";
106+
private const string PART_Thumb = "PART_Thumb";
107+
private Image beforeImage;
108+
private Rectangle rectangle;
109+
private Thumb thumb;
110+
private Line line;
111+
private double _currentOffsetX;
112+
private double _currentOffsetY;
113+
114+
private TranslateTransform translateTransform;
115+
116+
public CompareSlider()
117+
{
118+
if (Application.Current.Resources["HorizontalTemplate"] is ControlTemplate horizontalTemplate)
119+
HorizontalTemplate = horizontalTemplate;
120+
121+
if (Application.Current.Resources["VerticalTemplate"] is ControlTemplate verticalTemplate)
122+
VerticalTemplate = verticalTemplate;
123+
}
124+
125+
protected override void OnApplyTemplate()
126+
{
127+
base.OnApplyTemplate();
128+
129+
line = GetTemplateChild(PART_Line) as Line;
130+
thumb = GetTemplateChild(PART_Thumb) as Thumb;
131+
beforeImage = GetTemplateChild(BeforeImageElement) as Image;
132+
rectangle = GetTemplateChild(RectangleElement) as Rectangle;
133+
134+
beforeImage.SizeChanged -= OnSizeChanged;
135+
beforeImage.SizeChanged += OnSizeChanged;
136+
137+
SizeChanged -= OnSizeChanged;
138+
SizeChanged += OnSizeChanged;
139+
140+
beforeImage.ImageOpened -= OnImageOpened;
141+
beforeImage.ImageOpened += OnImageOpened;
142+
143+
thumb.DragDelta -= OnThumbDragDelta;
144+
thumb.DragDelta += OnThumbDragDelta;
145+
146+
_currentOffsetX = 0;
147+
_currentOffsetY = 0;
148+
149+
translateTransform = new TranslateTransform();
150+
thumb.RenderTransform = translateTransform;
151+
line.RenderTransform = translateTransform;
152+
}
153+
154+
private void OnThumbDragDelta(object sender, DragDeltaEventArgs e)
155+
{
156+
if (beforeImage == null || thumb == null || line == null || rectangle == null)
157+
{
158+
return;
159+
}
160+
161+
if (Orientation == Orientation.Horizontal)
162+
{
163+
if (_currentOffsetX == 0)
164+
{
165+
_currentOffsetX = translateTransform.X;
166+
}
167+
168+
double beforeImageWidth = (beforeImage.ActualWidth - thumb.ActualWidth) / 2;
169+
double offsetAdjustment = thumb.ActualWidth / 2;
170+
double leftBound = -beforeImageWidth - offsetAdjustment;
171+
double rightBound = beforeImageWidth + offsetAdjustment;
172+
173+
// Calculate the new offset
174+
double newOffsetX = _currentOffsetX + e.HorizontalChange;
175+
newOffsetX = Math.Max(leftBound, Math.Min(rightBound, newOffsetX));
176+
177+
// Update the offset and translate transform
178+
_currentOffsetX = newOffsetX;
179+
translateTransform.X = _currentOffsetX;
180+
181+
// Calculate the percentage value
182+
double totalRange = rightBound - leftBound;
183+
double normalizedValue = (_currentOffsetX - leftBound) / totalRange;
184+
double percentageValue = normalizedValue * 100;
185+
186+
// Update the value based on the drag
187+
Value = percentageValue;
188+
}
189+
else
190+
{
191+
if (_currentOffsetY == 0)
192+
{
193+
_currentOffsetY = translateTransform.Y;
194+
}
195+
196+
double beforeImageHeight = (beforeImage.ActualHeight - thumb.ActualHeight) / 2;
197+
double offsetAdjustment = thumb.ActualHeight / 2;
198+
double topBound = -beforeImageHeight - offsetAdjustment;
199+
double bottomBound = beforeImageHeight + offsetAdjustment;
200+
201+
// Calculate the new offset
202+
double newOffsetY = _currentOffsetY + e.VerticalChange;
203+
newOffsetY = Math.Max(topBound, Math.Min(bottomBound, newOffsetY));
204+
205+
// Update the offset and translate transform
206+
_currentOffsetY = newOffsetY;
207+
translateTransform.Y = _currentOffsetY;
208+
209+
// Calculate the percentage value
210+
double totalRange = bottomBound - topBound;
211+
double normalizedValue = (_currentOffsetY - topBound) / totalRange;
212+
double percentageValue = normalizedValue * 100;
213+
214+
// Update the value based on the drag
215+
Value = percentageValue;
216+
}
217+
}
218+
219+
private void OnImageOpened(object sender, RoutedEventArgs e)
220+
{
221+
beforeImage.ImageOpened -= OnImageOpened;
222+
UpdateValue(Value);
223+
}
224+
225+
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
226+
{
227+
UpdateValue(Value);
228+
}
229+
230+
public void UpdateValue(double newValue)
231+
{
232+
if (rectangle == null || beforeImage == null || thumb == null || line == null)
233+
{
234+
return;
235+
}
236+
237+
if (Orientation == Orientation.Horizontal)
238+
{
239+
newValue = Math.Max(0, Math.Min(100, newValue));
240+
241+
// Recalculate the rectangle width based on the new value
242+
double rectangleWidth = beforeImage.ActualWidth * (newValue / 100.0);
243+
rectangle.Width = rectangleWidth;
244+
rectangle.Height = beforeImage.ActualHeight;
245+
line.Height = beforeImage.ActualHeight;
246+
247+
// Calculate the new translateTransform.X value based on the rectangle's width
248+
translateTransform.X = rectangleWidth - (beforeImage.ActualWidth / 2);
249+
250+
// Update _currentOffsetX to the corresponding offset for the new value
251+
double beforeImageWidth = (beforeImage.ActualWidth - thumb.ActualWidth) / 2;
252+
double offsetAdjustment = thumb.ActualWidth / 2;
253+
double leftBound = -beforeImageWidth - offsetAdjustment;
254+
double rightBound = beforeImageWidth + offsetAdjustment;
255+
256+
// Map the new value (0-100) to the corresponding offset
257+
double totalRange = rightBound - leftBound;
258+
double newOffsetX = leftBound + (newValue / 100) * totalRange;
259+
260+
// Set _currentOffsetX and update the thumb position
261+
_currentOffsetX = newOffsetX;
262+
translateTransform.X = _currentOffsetX;
263+
}
264+
else
265+
{
266+
newValue = Math.Max(0, Math.Min(100, newValue));
267+
268+
// Recalculate the rectangle height based on the new value
269+
double rectangleHeight = beforeImage.ActualHeight * (newValue / 100.0);
270+
rectangle.Height = rectangleHeight;
271+
rectangle.Width = beforeImage.ActualWidth;
272+
line.Width = beforeImage.ActualWidth;
273+
274+
// Calculate the new translateTransform.Y value based on the rectangle's height
275+
translateTransform.Y = rectangleHeight - (beforeImage.ActualHeight / 2);
276+
277+
// Update _currentOffsetY to the corresponding offset for the new value
278+
double beforeImageHeight = (beforeImage.ActualHeight - thumb.ActualHeight) / 2;
279+
double offsetAdjustment = thumb.ActualHeight / 2;
280+
double topBound = -beforeImageHeight - offsetAdjustment;
281+
double bottomBound = beforeImageHeight + offsetAdjustment;
282+
283+
// Map the new value (0-100) to the corresponding offset
284+
double totalRange = bottomBound - topBound;
285+
double newOffsetY = topBound + (newValue / 100) * totalRange;
286+
287+
// Set _currentOffsetY and update the thumb position
288+
_currentOffsetY = newOffsetY;
289+
translateTransform.Y = _currentOffsetY;
290+
}
291+
}
292+
}

0 commit comments

Comments
 (0)