Skip to content
Merged

Fix2658 #2757

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions MainDemo.Wpf/Cards.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,68 @@
</materialDesign:Card>
</smtx:XamlDisplay>

<smtx:XamlDisplay
UniqueKey="cards_1b"
Margin="4 4 0 16"
VerticalContentAlignment="Top">
<materialDesign:Card Width="200" Style="{StaticResource MaterialDesignOutlinedCard}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Source="Resources/Chartridge046_small.jpg"
Height="140"
Width="196"
Stretch="UniformToFill"/>
<Button
Grid.Row="0"
Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Margin="0 0 16 -20">
<materialDesign:PackIcon Kind="Bike" />
</Button>
<StackPanel Grid.Row="1" Margin="8 24 8 0" >
<TextBlock FontWeight="Bold">Outlined style</TextBlock>
<TextBlock
TextWrapping="Wrap"
VerticalAlignment="Center"
Text="Removes the drop shadow and flattens the look of the card."/>
</StackPanel>
<StackPanel
HorizontalAlignment="Right"
Grid.Row="2"
Orientation="Horizontal"
Margin="8">
<Button
Style="{StaticResource MaterialDesignToolButton}"
Width="30" Padding="2 0 2 0"
materialDesign:RippleAssist.IsCentered="True">
<materialDesign:PackIcon Kind="ShareVariant" />
</Button>
<Button
Style="{StaticResource MaterialDesignToolButton}"
Width="30"
Padding="2 0 2 0"
materialDesign:RippleAssist.IsCentered="True">
<materialDesign:PackIcon Kind="Heart" />
</Button>
<materialDesign:PopupBox
Style="{StaticResource MaterialDesignToolPopupBox}"
Padding="2 0 2 0">
<StackPanel>
<Button Content="More"/>
<Button Content="Options"/>
</StackPanel>
</materialDesign:PopupBox>
</StackPanel>
</Grid>
</materialDesign:Card>
</smtx:XamlDisplay>

<smtx:XamlDisplay
UniqueKey="cards_2"
Margin="4 4 0 16"
Expand Down
52 changes: 52 additions & 0 deletions MaterialDesignThemes.UITests/WPF/Cards/OutlinedCardTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Windows.Media;

namespace MaterialDesignThemes.UITests.WPF.Cards;

public class OutlinedCardTests : TestBase
{
public OutlinedCardTests(ITestOutputHelper output)
: base(output)
{ }

[Fact]
public async Task OutlinedCard_UsesThemeColorForBorder()
{
await using var recorder = new TestRecorder(App);

//Arrange
IVisualElement<Card> card = await LoadXaml<Card>(
@"<materialDesign:Card Content=""Hello World"" Style=""{StaticResource MaterialDesignOutlinedCard}""/>");
Color dividerColor = await GetThemeColor("MaterialDesignDivider");
IVisualElement<Border> internalBorder = await card.GetElement<Border>();

//Act
Color? internalBorderColor = await internalBorder.GetBorderBrushColor();

//Assert
Assert.Equal(dividerColor, internalBorderColor);

recorder.Success();
}

[Fact]
public async Task OutlinedCard_UniformCornerRadiusApplied_AppliesCornerRadiusOnBorder()
{
await using var recorder = new TestRecorder(App);

//Arrange
IVisualElement<Card> card = await LoadXaml<Card>(
@"<materialDesign:Card Content=""Hello World"" Style=""{StaticResource MaterialDesignOutlinedCard}"" UniformCornerRadius=""5"" />");
IVisualElement<Border> internalBorder = await card.GetElement<Border>();

//Act
CornerRadius? internalBorderCornerRadius = await internalBorder.GetCornerRadius();

//Assert
Assert.Equal(5, internalBorderCornerRadius.Value.TopLeft);
Assert.Equal(5, internalBorderCornerRadius.Value.TopRight);
Assert.Equal(5, internalBorderCornerRadius.Value.BottomRight);
Assert.Equal(5, internalBorderCornerRadius.Value.BottomLeft);

recorder.Success();
}
}
1 change: 1 addition & 0 deletions MaterialDesignThemes.UITests/XamlTestMixins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static async Task InitialzeWithMaterialDesign(this IApp app,
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme=""{baseTheme}"" PrimaryColor=""{primary}"" SecondaryColor=""{secondary}"" {colorAdjustString}/>

<ResourceDictionary Source = ""pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/Generic.xaml"" />
<ResourceDictionary Source = ""pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml"" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Globalization;
using MaterialDesignThemes.Wpf.Converters;
using Xunit;

namespace MaterialDesignThemes.Wpf.Tests.Converters;

public class DoubleToCornerRadiusConverterTests
{
[Theory]
[InlineData(-0.16, 0.0)]
[InlineData(0.16, 0.16)]
[InlineData(5.0, 5.0)]
public void AllCultureParseParameterCorrectly(object parameter, double expectedCornerRadius)
{
var converter = new DoubleToCornerRadiusConverter();
foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
var cornerRadius = (CornerRadius?)converter.Convert(parameter, typeof(CornerRadius), parameter, culture);

Assert.Equal(expectedCornerRadius, cornerRadius.Value.TopLeft);
Assert.Equal(expectedCornerRadius, cornerRadius.Value.TopRight);
Assert.Equal(expectedCornerRadius, cornerRadius.Value.BottomRight);
Assert.Equal(expectedCornerRadius, cornerRadius.Value.BottomLeft);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Globalization;
using System.Windows.Data;

namespace MaterialDesignThemes.Wpf.Converters;

internal class DoubleToCornerRadiusConverter : IValueConverter
{
public static readonly DoubleToCornerRadiusConverter Instance = new();

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => new CornerRadius(Math.Max(0, (double)value));

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
1 change: 1 addition & 0 deletions MaterialDesignThemes.Wpf/Themes/Generic.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<!-- set up default styles for our custom Material Design in XAML Toolkit controls -->
<Style TargetType="{x:Type local:Clock}" BasedOn="{StaticResource MaterialDesignClock}" />
<Style TargetType="{x:Type local:Badged}" BasedOn="{StaticResource MaterialDesignBadge}" />
<Style TargetType="{x:Type local:Card}" BasedOn="{StaticResource MaterialDesignElevatedCard}" />
<Style TargetType="{x:Type local:PopupBox}" BasedOn="{StaticResource MaterialDesignPopupBox}" />
<Style TargetType="{x:Type local:TimePicker}" BasedOn="{StaticResource MaterialDesignTimePicker}" />

Expand Down
107 changes: 69 additions & 38 deletions MaterialDesignThemes.Wpf/Themes/MaterialDesignTheme.Card.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,78 @@
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Shadows.xaml" />
</ResourceDictionary.MergedDictionaries>

<ControlTemplate TargetType="{x:Type wpf:Card}" x:Key="CardTemplate">
<ControlTemplate.Resources>
<converters:ShadowEdgeConverter x:Key="ShadowEdgeConverter" />
</ControlTemplate.Resources>
<Grid Background="Transparent">
<AdornerDecorator CacheMode="{Binding RelativeSource={RelativeSource Self}, Path=(wpf:ShadowAssist.CacheMode)}">
<AdornerDecorator.OpacityMask>
<MultiBinding Converter="{StaticResource ShadowEdgeConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowDepth)" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowEdges)" />
</MultiBinding>
</AdornerDecorator.OpacityMask>
<Border Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"
CornerRadius="{TemplateBinding UniformCornerRadius}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"
x:Name="PART_ClipBorder"
Clip="{TemplateBinding ContentClip}" />
</Border>
</AdornerDecorator>
<ContentPresenter
x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}">
</ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ClipContent" Value="True">
<Setter Property="Clip" TargetName="ContentPresenter" Value="{Binding ContentClip, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type wpf:Card}">
<Setter Property="Template" Value="{StaticResource CardTemplate}" />
<Style x:Key="MaterialDesignElevatedCard" TargetType="{x:Type wpf:Card}">
<Setter Property="Background" Value="{DynamicResource MaterialDesignCardBackground}" />
<Setter Property="wpf:ShadowAssist.ShadowDepth" Value="Depth2" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type wpf:Card}">
<ControlTemplate.Resources>
<converters:ShadowEdgeConverter x:Key="ShadowEdgeConverter" />
</ControlTemplate.Resources>
<Grid Background="Transparent">
<AdornerDecorator CacheMode="{Binding RelativeSource={RelativeSource Self}, Path=(wpf:ShadowAssist.CacheMode)}">
<AdornerDecorator.OpacityMask>
<MultiBinding Converter="{StaticResource ShadowEdgeConverter}">
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight"/>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowDepth)" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(wpf:ShadowAssist.ShadowEdges)" />
</MultiBinding>
</AdornerDecorator.OpacityMask>
<Border Effect="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(wpf:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"
CornerRadius="{TemplateBinding UniformCornerRadius}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"
x:Name="PART_ClipBorder"
Clip="{TemplateBinding ContentClip}" />
</Border>
</AdornerDecorator>
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ClipContent" Value="True">
<Setter Property="Clip" TargetName="ContentPresenter" Value="{Binding ContentClip, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

<Style x:Key="MaterialDesignOutlinedCard" TargetType="{x:Type wpf:Card}">
<Setter Property="Background" Value="{DynamicResource MaterialDesignCardBackground}" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type wpf:Card}">
<Grid Background="Transparent">
<Border BorderThickness="1" CornerRadius="{TemplateBinding UniformCornerRadius, Converter={x:Static converters:DoubleToCornerRadiusConverter.Instance}}" BorderBrush="{DynamicResource MaterialDesignDivider}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"
x:Name="PART_ClipBorder"
Clip="{TemplateBinding ContentClip}">
<ContentPresenter x:Name="ContentPresenter"
Margin="{TemplateBinding Padding}"
Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentControl.ContentTemplateSelector}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
</Border>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ClipContent" Value="True">
<Setter Property="Clip" TargetName="ContentPresenter" Value="{Binding ContentClip, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

</ResourceDictionary>