1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
namespace MaterialDesignThemes.Wpf
{
[TemplatePart(Name = Plane3DPartName, Type = typeof(Plane3D))]
[TemplateVisualState(GroupName = TemplateFlipGroupName, Name = TemplateFlippedStateName)]
[TemplateVisualState(GroupName = TemplateFlipGroupName, Name = TemplateUnflippedStateName)]
public class Flipper : Control
{
public static RoutedCommand FlipCommand = new RoutedCommand();
public const string Plane3DPartName = "PART_Plane3D";
public const string TemplateFlipGroupName = "FlipStates";
public const string TemplateFlippedStateName = "Flipped";
public const string TemplateUnflippedStateName = "Unflipped";
private Plane3D _plane3D;
static Flipper()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Flipper), new FrameworkPropertyMetadata(typeof(Flipper)));
}
public Flipper()
{
CommandBindings.Add(new CommandBinding(FlipCommand, FlipHandler));
}
public static readonly DependencyProperty FrontContentProperty = DependencyProperty.Register(
nameof(FrontContent), typeof(object), typeof(Flipper), new PropertyMetadata(default(object)));
public object FrontContent
{
get => GetValue(FrontContentProperty);
set => SetValue(FrontContentProperty, value);
}
public static readonly DependencyProperty FrontContentTemplateProperty = DependencyProperty.Register(
nameof(FrontContentTemplate), typeof(DataTemplate), typeof(Flipper), new PropertyMetadata(default(DataTemplate)));
public DataTemplate FrontContentTemplate
{
get => (DataTemplate) GetValue(FrontContentTemplateProperty);
set => SetValue(FrontContentTemplateProperty, value);
}
public static readonly DependencyProperty FrontContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(FrontContentTemplateSelector), typeof(DataTemplateSelector), typeof(Flipper), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector FrontContentTemplateSelector
{
get => (DataTemplateSelector) GetValue(FrontContentTemplateSelectorProperty);
set => SetValue(FrontContentTemplateSelectorProperty, value);
}
public static readonly DependencyProperty FrontContentStringFormatProperty = DependencyProperty.Register(
nameof(FrontContentStringFormat), typeof(string), typeof(Flipper), new PropertyMetadata(default(string)));
public string FrontContentStringFormat
{
get => (string) GetValue(FrontContentStringFormatProperty);
set => SetValue(FrontContentStringFormatProperty, value);
}
public static readonly DependencyProperty BackContentProperty = DependencyProperty.Register(
nameof(BackContent), typeof(object), typeof(Flipper), new PropertyMetadata(default(object)));
public object BackContent
{
get => (object) GetValue(BackContentProperty);
set => SetValue(BackContentProperty, value);
}
public static readonly DependencyProperty BackContentTemplateProperty = DependencyProperty.Register(
nameof(BackContentTemplate), typeof(DataTemplate), typeof(Flipper), new PropertyMetadata(default(DataTemplate)));
public DataTemplate BackContentTemplate
{
get => (DataTemplate)GetValue(BackContentTemplateProperty);
set => SetValue(BackContentTemplateProperty, value);
}
public static readonly DependencyProperty BackContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(BackContentTemplateSelector), typeof(DataTemplateSelector), typeof(Flipper), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector BackContentTemplateSelector
{
get => (DataTemplateSelector)GetValue(BackContentTemplateSelectorProperty);
set => SetValue(BackContentTemplateSelectorProperty, value);
}
public static readonly DependencyProperty BackContentStringFormatProperty = DependencyProperty.Register(
nameof(BackContentStringFormat), typeof(string), typeof(Flipper), new PropertyMetadata(default(string)));
public string BackContentStringFormat
{
get => (string)GetValue(BackContentStringFormatProperty);
set => SetValue(BackContentStringFormatProperty, value);
}
public static readonly DependencyProperty IsFlippedProperty = DependencyProperty.Register(
nameof(IsFlipped), typeof(bool), typeof(Flipper), new PropertyMetadata(default(bool), IsFlippedPropertyChangedCallback));
private static void IsFlippedPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var flipper = (Flipper)dependencyObject;
flipper.UpdateVisualStates(true);
flipper.RemeasureDuringFlip();
OnIsFlippedChanged(flipper, dependencyPropertyChangedEventArgs);
}
public bool IsFlipped
{
get => (bool) GetValue(IsFlippedProperty);
set => SetValue(IsFlippedProperty, value);
}
public static readonly RoutedEvent IsFlippedChangedEvent =
EventManager.RegisterRoutedEvent(
nameof(IsFlipped),
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<bool>),
typeof(Flipper));
public event RoutedPropertyChangedEventHandler<bool> IsFlippedChanged
{
add => AddHandler(IsFlippedChangedEvent, value);
remove => RemoveHandler(IsFlippedChangedEvent, value);
}
private static void OnIsFlippedChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (Flipper)d;
var args = new RoutedPropertyChangedEventArgs<bool>(
(bool)e.OldValue,
(bool)e.NewValue)
{ RoutedEvent = IsFlippedChangedEvent };
instance.RaiseEvent(args);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
UpdateVisualStates(false);
_plane3D = GetTemplateChild(Plane3DPartName) as Plane3D;
}
private void RemeasureDuringFlip()
{
//not entirely happy hardcoding this, but I have explored other options I am not happy with, and this will do for now
const int storyboardMs = 400;
const int granularity = 6;
var remeasureInterval = new TimeSpan(0, 0, 0, 0, storyboardMs/granularity);
var refreshCount = 0;
var plane3D = _plane3D;
if (plane3D == null) return;
DispatcherTimer dt = null;
dt = new DispatcherTimer(remeasureInterval, DispatcherPriority.Normal,
(sender, args) =>
{
plane3D.InvalidateMeasure();
if (refreshCount++ == granularity)
dt.Stop();
}, Dispatcher);
dt.Start();
}
private void UpdateVisualStates(bool useTransitions)
{
VisualStateManager.GoToState(this, IsFlipped ? TemplateFlippedStateName : TemplateUnflippedStateName,
useTransitions);
}
private void FlipHandler(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
SetCurrentValue(IsFlippedProperty, !IsFlipped);
}
}
}
|