Skip to content

Commit 8f7e1ff

Browse files
authored
Merge pull request #1155 from jespersh/master
Fix #1153 Prevent 24th minute and second showing up as 0
2 parents 3ae2c21 + dea0121 commit 8f7e1ff

File tree

6 files changed

+243
-60
lines changed

6 files changed

+243
-60
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using System.Linq;
2+
using System.Windows.Controls;
3+
using Xunit;
4+
5+
namespace MaterialDesignThemes.Wpf.Tests
6+
{
7+
public class ClockTests
8+
{
9+
[StaFact(Timeout = 500)]
10+
public void CanGenerateHoursButtons()
11+
{
12+
var clock = new Clock();
13+
clock.ApplyDefaultStyle();
14+
15+
Canvas hoursCanvas = clock.FindVisualChild<Canvas>(Clock.HoursCanvasPartName);
16+
var buttonContents = hoursCanvas.GetVisualChildren<ClockItemButton>().Select(x => x.Content.ToString());
17+
18+
var expected = Enumerable.Range(1, 12).Select(x => $"{x:0}");
19+
Assert.Equal(expected.OrderBy(x => x), buttonContents.OrderBy(x => x));
20+
}
21+
22+
[StaFact(Timeout = 500)]
23+
public void CanGenerateHoursButtonsWith24Hours()
24+
{
25+
var clock = new Clock {Is24Hours = true};
26+
clock.ApplyDefaultStyle();
27+
28+
Canvas hoursCanvas = clock.FindVisualChild<Canvas>(Clock.HoursCanvasPartName);
29+
var buttonContents = hoursCanvas.GetVisualChildren<ClockItemButton>().Select(x => x.Content.ToString());
30+
31+
var expected = Enumerable.Range(13, 11).Select(x => $"{x:00}")
32+
.Concat(new [] {"00"})
33+
.Concat(Enumerable.Range(1, 12).Select(x => $"{x:#}"));
34+
Assert.Equal(expected.OrderBy(x => x), buttonContents.OrderBy(x => x));
35+
}
36+
37+
[StaFact(Timeout = 500)]
38+
public void CanGenerateMinuteButtons()
39+
{
40+
var clock = new Clock();
41+
clock.ApplyDefaultStyle();
42+
43+
Canvas minutesCanvas = clock.FindVisualChild<Canvas>(Clock.MinutesCanvasPartName);
44+
var buttonContents = minutesCanvas.GetVisualChildren<ClockItemButton>().Select(x => x.Content.ToString());
45+
46+
var expected = Enumerable.Range(0, 60).Select(x => $"{x:0}");
47+
Assert.Equal(expected.OrderBy(x => x), buttonContents.OrderBy(x => x));
48+
}
49+
50+
[StaFact(Timeout = 500)]
51+
public void CanGenerateSecondsButtons()
52+
{
53+
var clock = new Clock();
54+
clock.ApplyDefaultStyle();
55+
56+
Canvas secondsCanvas = clock.FindVisualChild<Canvas>(Clock.SecondsCanvasPartName);
57+
var buttonContents = secondsCanvas.GetVisualChildren<ClockItemButton>().Select(x => x.Content.ToString());
58+
59+
var expected = Enumerable.Range(0, 60).Select(x => $"{x:0}");
60+
Assert.Equal(expected.OrderBy(x => x), buttonContents.OrderBy(x => x));
61+
}
62+
}
63+
}

MaterialDesignThemes.Wpf.Tests/MaterialDesignThemes.Wpf.Tests.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<AssemblyName>MaterialDesignThemes.Wpf.Tests</AssemblyName>
1313
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
1414
<FileAlignment>512</FileAlignment>
15+
<LangVersion>latest</LangVersion>
1516
</PropertyGroup>
1617
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1718
<DebugSymbols>true</DebugSymbols>
@@ -29,6 +30,7 @@
2930
<DefineConstants>TRACE</DefineConstants>
3031
<ErrorReport>prompt</ErrorReport>
3132
<WarningLevel>4</WarningLevel>
33+
<LangVersion>latest</LangVersion>
3234
</PropertyGroup>
3335
<ItemGroup>
3436
<Reference Include="PresentationCore" />
@@ -43,9 +45,12 @@
4345
<Reference Include="System.Xml" />
4446
</ItemGroup>
4547
<ItemGroup>
48+
<Compile Include="ClockTests.cs" />
4649
<Compile Include="DialogHostTests.cs" />
50+
<Compile Include="MdixHelper.cs" />
4751
<Compile Include="PaletteHelperFixture.cs" />
4852
<Compile Include="Properties\AssemblyInfo.cs" />
53+
<Compile Include="VisualTreeHelper.cs" />
4954
</ItemGroup>
5055
<ItemGroup>
5156
<ProjectReference Include="..\MaterialDesignThemes.Wpf\MaterialDesignThemes.Wpf.csproj">
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.IO.Packaging;
3+
using System.Windows;
4+
using System.Windows.Controls;
5+
using Xunit;
6+
7+
namespace MaterialDesignThemes.Wpf.Tests
8+
{
9+
public static class MdixHelper
10+
{
11+
static MdixHelper()
12+
{
13+
var _ = Application.Current;
14+
15+
_defaultResourceDictionary = new Lazy<ResourceDictionary>(() => GetThemeResourceDictionary("Defaults"));
16+
_genericResourceDictionary = new Lazy<ResourceDictionary>(GetGenericResourceDictionary);
17+
}
18+
19+
private static readonly Lazy<ResourceDictionary> _defaultResourceDictionary;
20+
private static ResourceDictionary DefaultResourceDictionary => _defaultResourceDictionary.Value;
21+
22+
private static readonly Lazy<ResourceDictionary> _genericResourceDictionary;
23+
private static ResourceDictionary GenericResourceDictionary => _genericResourceDictionary.Value;
24+
25+
public static void ApplyDefaultStyle<T>(this T control) where T : FrameworkElement
26+
{
27+
Style style = GetDefaultStyle<T>();
28+
Assert.True(style != null, $"Could not find default style for control type {typeof(T).FullName}");
29+
control.Style = style;
30+
Assert.True(control.ApplyTemplate(), "Failed to apply template ");
31+
}
32+
33+
private static Style GetDefaultStyle<T>() where T : FrameworkElement
34+
{
35+
return DefaultResourceDictionary[typeof(T)] as Style ??
36+
GenericResourceDictionary[typeof(T)] as Style;
37+
}
38+
39+
private static ResourceDictionary GetThemeResourceDictionary(string name)
40+
{
41+
var uri = new Uri(
42+
$"/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.{name}.xaml", UriKind.Relative);
43+
return new ResourceDictionary {
44+
Source = uri
45+
};
46+
}
47+
48+
private static ResourceDictionary GetGenericResourceDictionary()
49+
{
50+
var uri = new Uri(
51+
$"/MaterialDesignThemes.Wpf;component/Themes/Generic.xaml", UriKind.Relative);
52+
return new ResourceDictionary {
53+
Source = uri
54+
};
55+
}
56+
}
57+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Windows;
6+
7+
using WPFVisualTreeHelper= System.Windows.Media.VisualTreeHelper;
8+
9+
namespace MaterialDesignThemes.Wpf.Tests
10+
{
11+
public static class VisualTreeHelper
12+
{
13+
public static T FindVisualChild<T>(this DependencyObject source, string name) where T : FrameworkElement
14+
{
15+
return GetAllVisualChildren(source).OfType<T>().Single(x => x.Name == name);
16+
}
17+
18+
public static IEnumerable<T> GetVisualChildren<T>(this DependencyObject source) where T : DependencyObject
19+
{
20+
if (source == null) throw new ArgumentNullException(nameof(source));
21+
22+
return GetVisualChildrenImplementation();
23+
24+
IEnumerable<T> GetVisualChildrenImplementation()
25+
{
26+
int count = WPFVisualTreeHelper.GetChildrenCount(source);
27+
for (int i = 0; i < count; i++)
28+
{
29+
if (WPFVisualTreeHelper.GetChild(source, i) is T child)
30+
{
31+
yield return child;
32+
}
33+
}
34+
}
35+
}
36+
37+
private static IEnumerable<DependencyObject> GetAllVisualChildren(DependencyObject source)
38+
{
39+
var stack = new Queue<DependencyObject>();
40+
stack.Enqueue(source);
41+
42+
while (stack.Any())
43+
{
44+
DependencyObject current = stack.Dequeue();
45+
int childCount = WPFVisualTreeHelper.GetChildrenCount(current);
46+
for (int i = 0; i < childCount; i++)
47+
{
48+
var child = WPFVisualTreeHelper.GetChild(current, i);
49+
yield return child;
50+
stack.Enqueue(child);
51+
}
52+
}
53+
}
54+
}
55+
}

MaterialDesignThemes.Wpf/Clock.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,16 @@ private void GenerateButtons()
278278
if (Is24Hours)
279279
{
280280
GenerateButtons(hoursCanvas, Enumerable.Range(13, 12).ToList(), ButtonRadiusRatio,
281-
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "00");
281+
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "00",
282+
ClockDisplayMode.Hours);
282283
GenerateButtons(hoursCanvas, Enumerable.Range(1, 12).ToList(), ButtonRadiusInnerRatio,
283-
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "#");
284+
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "#",
285+
ClockDisplayMode.Hours);
284286
}
285287
else
286288
GenerateButtons(hoursCanvas, Enumerable.Range(1, 12).ToList(), ButtonRadiusRatio,
287-
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "0");
289+
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Hours, Is24Hours), i => "ButtonStyle", "0",
290+
ClockDisplayMode.Hours);
288291
}
289292

290293
if (GetTemplateChild(MinutesCanvasPartName) is Canvas minutesCanvas)
@@ -293,7 +296,8 @@ private void GenerateButtons()
293296

294297
GenerateButtons(minutesCanvas, Enumerable.Range(1, 60).ToList(), ButtonRadiusRatio,
295298
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Minutes, Is24Hours),
296-
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0");
299+
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0",
300+
ClockDisplayMode.Minutes);
297301
}
298302

299303
if (GetTemplateChild(SecondsCanvasPartName) is Canvas secondsCanvas)
@@ -302,7 +306,8 @@ private void GenerateButtons()
302306

303307
GenerateButtons(secondsCanvas, Enumerable.Range(1, 60).ToList(), ButtonRadiusRatio,
304308
new ClockItemIsCheckedConverter(() => Time, ClockDisplayMode.Seconds, Is24Hours),
305-
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0");
309+
i => ((i / 5.0) % 1) == 0.0 ? "ButtonStyle" : "LesserButtonStyle", "0",
310+
ClockDisplayMode.Seconds);
306311
}
307312

308313
void RemoveExistingButtons(Canvas canvas)
@@ -333,7 +338,7 @@ private void HourReadOutPartNameOnPreviewMouseLeftButtonDown(object sender, Mous
333338
}
334339

335340
private void GenerateButtons(Panel canvas, ICollection<int> range, double radiusRatio, IValueConverter isCheckedConverter, Func<int, string> stylePropertySelector,
336-
string format)
341+
string format, ClockDisplayMode clockDisplayMode)
337342
{
338343
var anglePerItem = 360.0 / range.Count;
339344
var radiansPerItem = anglePerItem * (Math.PI / 180);
@@ -359,7 +364,7 @@ private void GenerateButtons(Panel canvas, ICollection<int> range, double radius
359364
button.SetBinding(Canvas.LeftProperty, GetBinding("X", button));
360365
button.SetBinding(Canvas.TopProperty, GetBinding("Y", button));
361366

362-
button.Content = (i == 60 ? 0 : (i == 24 ? 0 : i)).ToString(format);
367+
button.Content = (i == 60 ? 0 : (i == 24 && clockDisplayMode == ClockDisplayMode.Hours ? 0 : i)).ToString(format);
363368
canvas.Children.Add(button);
364369
}
365370
}

0 commit comments

Comments
 (0)