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