Skip to content

Commit 78c4aa3

Browse files
Refactor fix for 2689 to use attached properties (#2762)
* Refactor fix for 2689 to use attached properties Added attached properties for UniformCornerRadius and Style. These properties are inherited and can thus be used to manipulate a Card located inside of another UIElement (eg. the Flipper). * Make CardAssist.CardStyle lenient towards unaccepted style Fallback to default(Style) if the TargetType is not Card. * Renamed CardAssist and improved guard in CardStyle setter (of attached property) You mentioned on stream that fact that derived types would not work for the CardStyle because of the guard which is completely correct. So I loosened up the requirements by using IsAssignableFrom() instead. * Fixed broken Flipper (UI) tests Rename operation did not pick up the string literal in these tests
1 parent 8578fba commit 78c4aa3

File tree

8 files changed

+213
-16
lines changed

8 files changed

+213
-16
lines changed

MainDemo.Wpf/Cards.xaml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,14 +649,42 @@
649649
<materialDesign:Flipper
650650
Style="{StaticResource MaterialDesignCardFlipper}"
651651
IsFlippedChanged="Flipper_OnIsFlippedChanged"
652-
UniformCornerRadius="8">
652+
materialDesign:FlipperAssist.UniformCornerRadius="8">
653653
<materialDesign:Flipper.FrontContent>
654654
<Button
655655
Style="{StaticResource MaterialDesignFlatButton}"
656656
Command="{x:Static materialDesign:Flipper.FlipCommand}"
657657
Margin="8"
658658
Width="184"
659-
Content="Rounded Card Flipper"/>
659+
Content="Rounded Card Flipper 1"/>
660+
</materialDesign:Flipper.FrontContent>
661+
<materialDesign:Flipper.BackContent>
662+
<Button
663+
Style="{StaticResource MaterialDesignFlatButton}"
664+
Command="{x:Static materialDesign:Flipper.FlipCommand}"
665+
Margin="8"
666+
Width="184"
667+
Content="GO BACK"/>
668+
</materialDesign:Flipper.BackContent>
669+
</materialDesign:Flipper>
670+
</smtx:XamlDisplay>
671+
672+
<smtx:XamlDisplay
673+
UniqueKey="cards_12"
674+
Margin="4 4 0 0"
675+
VerticalContentAlignment="Top">
676+
<materialDesign:Flipper
677+
Style="{StaticResource MaterialDesignCardFlipper}"
678+
IsFlippedChanged="Flipper_OnIsFlippedChanged"
679+
materialDesign:FlipperAssist.UniformCornerRadius="8"
680+
materialDesign:FlipperAssist.CardStyle="{StaticResource MaterialDesignOutlinedCard}">
681+
<materialDesign:Flipper.FrontContent>
682+
<Button
683+
Style="{StaticResource MaterialDesignFlatButton}"
684+
Command="{x:Static materialDesign:Flipper.FlipCommand}"
685+
Margin="8"
686+
Width="184"
687+
Content="Rounded Card Flipper 2"/>
660688
</materialDesign:Flipper.FrontContent>
661689
<materialDesign:Flipper.BackContent>
662690
<Button
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace MaterialDesignThemes.UITests.WPF.Cards;
2+
3+
public class ElevatedCardTests : TestBase
4+
{
5+
public ElevatedCardTests(ITestOutputHelper output)
6+
: base(output)
7+
{ }
8+
9+
[Fact]
10+
public async Task ElevatedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnBorder()
11+
{
12+
await using var recorder = new TestRecorder(App);
13+
14+
//Arrange
15+
IVisualElement<Card> card = await LoadXaml<Card>(
16+
@"<materialDesign:Card Content=""Hello World"" Style=""{StaticResource MaterialDesignElevatedCard}"" UniformCornerRadius=""5"" />");
17+
IVisualElement<Border> internalBorder = await card.GetElement<Border>();
18+
19+
//Act
20+
CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius();
21+
22+
//Assert
23+
Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft);
24+
Assert.Equal(5, internalBorderCornerRadius.Value.TopRight);
25+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight);
26+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft);
27+
28+
recorder.Success();
29+
}
30+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
namespace MaterialDesignThemes.UITests.WPF.Flippers;
2+
3+
public class FlipperTests : TestBase
4+
{
5+
public FlipperTests(ITestOutputHelper output)
6+
: base(output)
7+
{ }
8+
9+
[Fact]
10+
public async Task Flipper_UniformCornerRadiusAndOutlinedCardStyleAttachedPropertiesApplied_AppliesCornerRadiusOnBorder()
11+
{
12+
await using var recorder = new TestRecorder(App);
13+
14+
//Arrange
15+
IVisualElement<Flipper> flipper = await LoadXaml<Flipper>(
16+
@"<materialDesign:Flipper Style=""{StaticResource MaterialDesignCardFlipper}"" materialDesign:FlipperAssist.CardStyle=""{StaticResource MaterialDesignOutlinedCard}"" materialDesign:FlipperAssist.UniformCornerRadius=""5"" />");
17+
IVisualElement<Card> internalCard = await flipper.GetElement<Card>();
18+
IVisualElement<Border> internalBorder = await internalCard.GetElement<Border>();
19+
20+
//Act
21+
CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius();
22+
23+
//Assert
24+
Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft);
25+
Assert.Equal(5, internalBorderCornerRadius.Value.TopRight);
26+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight);
27+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft);
28+
29+
recorder.Success();
30+
}
31+
32+
[Fact]
33+
public async Task Flipper_UniformCornerRadiusAndElevatedCardStyleAttachedPropertiesApplied_AppliesCornerRadiusOnBorder()
34+
{
35+
await using var recorder = new TestRecorder(App);
36+
37+
//Arrange
38+
IVisualElement<Flipper> flipper = await LoadXaml<Flipper>(
39+
@"<materialDesign:Flipper Style=""{StaticResource MaterialDesignCardFlipper}"" materialDesign:FlipperAssist.CardStyle=""{StaticResource MaterialDesignElevatedCard}"" materialDesign:FlipperAssist.UniformCornerRadius=""5"" />");
40+
IVisualElement<Card> internalCard = await flipper.GetElement<Card>();
41+
IVisualElement<Border> internalBorder = await internalCard.GetElement<Border>();
42+
43+
//Act
44+
CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius();
45+
46+
//Assert
47+
Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft);
48+
Assert.Equal(5, internalBorderCornerRadius.Value.TopRight);
49+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight);
50+
Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft);
51+
52+
recorder.Success();
53+
}
54+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using Xunit;
2+
3+
namespace MaterialDesignThemes.Wpf.Tests;
4+
5+
public class FlipperAssistTests
6+
{
7+
private readonly FrameworkElement _testElement;
8+
9+
public FlipperAssistTests()
10+
{
11+
_testElement = new FrameworkElement();
12+
}
13+
14+
[StaFact]
15+
public void CardStyle_CardStyleNotSet_AttachedPropertyNotSet()
16+
{
17+
// Assert
18+
Assert.Null(FlipperAssist.GetCardStyle(_testElement));
19+
}
20+
21+
[StaFact]
22+
public void CardStyle_StyleWithWrongTargetType_AttachedPropertyNotSet()
23+
{
24+
// Arrange
25+
var style = new Style(typeof(Button));
26+
27+
// Act
28+
FlipperAssist.SetCardStyle(_testElement, style);
29+
30+
// Assert
31+
Assert.Null(FlipperAssist.GetCardStyle(_testElement));
32+
}
33+
34+
[StaFact]
35+
public void CardStyle_StyleWithCorrectTargetType_AttachedPropertySet()
36+
{
37+
// Arrange
38+
var style = new Style(typeof(Card));
39+
40+
// Act
41+
FlipperAssist.SetCardStyle(_testElement, style);
42+
43+
// Assert
44+
Assert.Equal(style, FlipperAssist.GetCardStyle(_testElement));
45+
}
46+
47+
[StaFact]
48+
public void CardStyle_StyleWithDerivedCardTargetType_AttachedPropertySet()
49+
{
50+
// Arrange
51+
var style = new Style(typeof(DerivedCard));
52+
53+
// Act
54+
FlipperAssist.SetCardStyle(_testElement, style);
55+
56+
// Assert
57+
Assert.Equal(style, FlipperAssist.GetCardStyle(_testElement));
58+
}
59+
60+
internal class DerivedCard : Card { }
61+
}

MaterialDesignThemes.Wpf/Flipper.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,6 @@ private static void OnIsFlippedChanged(
143143
instance.RaiseEvent(args);
144144
}
145145

146-
public static readonly DependencyProperty UniformCornerRadiusProperty = DependencyProperty.Register(
147-
nameof(UniformCornerRadius), typeof(double), typeof(Flipper), new PropertyMetadata(default(double)));
148-
149-
/// <summary>
150-
/// Gets or sets the (uniform) corner radius applied the the <see cref="Flipper"/> when the MaterialDesignCardFlipper style is applied.
151-
/// </summary>
152-
public double UniformCornerRadius
153-
{
154-
get => (double)GetValue(UniformCornerRadiusProperty);
155-
set => SetValue(UniformCornerRadiusProperty, value);
156-
}
157-
158146
public override void OnApplyTemplate()
159147
{
160148
base.OnApplyTemplate();
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace MaterialDesignThemes.Wpf;
2+
3+
public static class FlipperAssist
4+
{
5+
#region AttachedProperty : UniformCornerRadiusProperty
6+
/// <summary>
7+
/// Controls the (uniform) corner radius of the contained card
8+
/// </summary>
9+
public static readonly DependencyProperty UniformCornerRadiusProperty
10+
= DependencyProperty.RegisterAttached("UniformCornerRadius", typeof(double), typeof(FlipperAssist),
11+
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits));
12+
13+
public static void SetUniformCornerRadius(DependencyObject element, double value) => element.SetValue(UniformCornerRadiusProperty, value);
14+
public static double GetUniformCornerRadius(DependencyObject element) => (double)element.GetValue(UniformCornerRadiusProperty);
15+
#endregion
16+
17+
#region AttachedProperty : CardStyleProperty
18+
/// <summary>
19+
/// Controls the style of the contained card
20+
/// </summary>
21+
public static readonly DependencyProperty CardStyleProperty
22+
= DependencyProperty.RegisterAttached("CardStyle", typeof(Style), typeof(FlipperAssist),
23+
new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits, null, CoerceCardStyleCallback));
24+
private static object? CoerceCardStyleCallback(DependencyObject d, object baseValue)
25+
{
26+
if (baseValue is Style style && !typeof(Card).IsAssignableFrom(style.TargetType))
27+
return default(Style);
28+
return baseValue;
29+
}
30+
31+
public static void SetCardStyle(DependencyObject element, Style value) => element.SetValue(CardStyleProperty, value);
32+
public static Style GetCardStyle(DependencyObject element) => (Style)element.GetValue(CardStyleProperty);
33+
#endregion
34+
}

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Card.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</MultiBinding>
2929
</AdornerDecorator.OpacityMask>
3030
<Border Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"
31-
CornerRadius="{TemplateBinding UniformCornerRadius}">
31+
CornerRadius="{TemplateBinding UniformCornerRadius, Converter={x:Static converters:DoubleToCornerRadiusConverter.Instance}}">
3232
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"
3333
x:Name="PART_ClipBorder"
3434
Clip="{TemplateBinding ContentClip}" />

MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Flipper.xaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@
162162
</VisualStateGroup>
163163
</VisualStateManager.VisualStateGroups>
164164
<wpf:Plane3D x:Name="PART_Plane3D" RotationY="0" ZFactor="2.055">
165-
<wpf:Card wpf:ShadowAssist.ShadowDepth="{TemplateBinding wpf:ShadowAssist.ShadowDepth}" UniformCornerRadius="{TemplateBinding UniformCornerRadius}">
165+
<wpf:Card wpf:ShadowAssist.ShadowDepth="{TemplateBinding wpf:ShadowAssist.ShadowDepth}"
166+
UniformCornerRadius="{TemplateBinding wpf:FlipperAssist.UniformCornerRadius}"
167+
Style="{TemplateBinding wpf:FlipperAssist.CardStyle}">
166168
<Grid>
167169
<ContentPresenter x:Name="FrontContentPresenter"
168170
Margin="{TemplateBinding Padding}"

0 commit comments

Comments
 (0)