Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,83 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<sample:SamplePageLayout>
<sample:SamplePageLayout.FluentTemplate>
<DataTemplate>
<StackPanel Padding="0,20"
Spacing="20">
<!-- Default CardContentControl Style -->
<TextBlock Text="Default CardContentControl Style"
Style="{ThemeResource TitleTextBlockStyle}" />

<utu:CardContentControl Style="{StaticResource DefaultCardContentControlStyle}">
<utu:CardContentControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<!-- Media part -->
<Image Source="ms-appx:///Assets/Media/LargeMedia.png"
Stretch="Uniform"
MaxHeight="194" />

<StackPanel Grid.Row="1"
Margin="16,14">

<!-- Header part -->
<TextBlock Text="Default Card"
MaxLines="1"
Style="{ThemeResource TitleTextBlockStyle}" />

<!-- SubHeader part -->
<TextBlock Text="With media, supporting text and action buttons"
Margin="0,4,0,12"
MaxLines="2"
Style="{ThemeResource SubtitleTextBlockStyle}" />

<!-- Supporting content part -->
<TextBlock Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor."
Style="{ThemeResource BodyTextBlockStyle}" />

<!-- Action buttons part -->
<StackPanel Margin="0,12,0,0"
Spacing="6"
Orientation="Horizontal">
<Button Content="ACTION 1"
Padding="10,2" />
<Button Content="ACTION 2"
Padding="10,2" />
</StackPanel>

</StackPanel>
</Grid>
</DataTemplate>
</utu:CardContentControl.ContentTemplate>
</utu:CardContentControl>

<!-- Disabled Default CardContentControl -->
<TextBlock Text="Disabled Default CardContentControl Style"
Style="{ThemeResource TitleTextBlockStyle}" />

<utu:CardContentControl IsEnabled="False" Style="{StaticResource DefaultCardContentControlStyle}">
<utu:CardContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Margin="16">
<TextBlock Text="Disabled Card"
MaxLines="1"
Style="{ThemeResource TitleTextBlockStyle}" />
<TextBlock Text="This card is disabled"
Margin="0,4,0,0"
Style="{ThemeResource BodyTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</utu:CardContentControl.ContentTemplate>
</utu:CardContentControl>
</StackPanel>
</DataTemplate>
</sample:SamplePageLayout.FluentTemplate>
<sample:SamplePageLayout.MaterialTemplate>
<DataTemplate>
<StackPanel Padding="0,20"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,43 @@

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<sample:SamplePageLayout>
<sample:SamplePageLayout.FluentTemplate>
<DataTemplate>
<StackPanel Padding="0,20"
Spacing="20">

<!-- Card with default style -->
<utu:Card HeaderContent="Default Card"
Style="{StaticResource DefaultCardStyle}"
SubHeaderContent="Using DefaultCardStyle" />

<!-- Card with default style and supporting text -->
<utu:Card HeaderContent="Default Card"
Style="{StaticResource DefaultCardStyle}"
SubHeaderContent="With supporting text"
SupportingContent="{StaticResource SupportingTextSample}" />

<!-- Card with default style and media -->
<utu:Card HeaderContent="Default Card"
Style="{StaticResource DefaultCardStyle}"
SubHeaderContent="With media"
MediaContent="ms-appx:///Assets/Media/LargeMedia.png" />

<!-- Card with default style, media and supporting text -->
<utu:Card HeaderContent="Default Card"
Style="{StaticResource DefaultCardStyle}"
SubHeaderContent="With media and supporting text"
MediaContent="ms-appx:///Assets/Media/LargeMedia.png"
SupportingContent="{StaticResource SupportingTextSample}" />

<!-- Disabled Card with default style -->
<utu:Card HeaderContent="Disabled Default Card"
Style="{StaticResource DefaultCardStyle}"
SubHeaderContent="With title and subtitle only"
IsEnabled="False" />
</StackPanel>
</DataTemplate>
</sample:SamplePageLayout.FluentTemplate>
<sample:SamplePageLayout.MaterialTemplate>
<DataTemplate>
<StackPanel Padding="0,20"
Expand Down
214 changes: 214 additions & 0 deletions src/Uno.Toolkit.UI/Controls/Card/Card.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="using:Uno.UI.Toolkit"
xmlns:utu="using:Uno.Toolkit.UI">

<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="CardBackground" ResourceKey="CardBackgroundFillColorDefaultBrush" />
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="CardBorderBrushPointerOver" ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="CardBorderBrushFocused" ResourceKey="ControlFillColorTertiaryBrush" />
<x:Double x:Key="CardMinHeight">72</x:Double>
<x:Double x:Key="CardMaxWidth">344</x:Double>
<x:Double x:Key="CardElevation">6</x:Double>
<Thickness x:Key="CardPadding">16</Thickness>
<Thickness x:Key="CardBorderThickness">1</Thickness>
<Thickness x:Key="CardElevationMargin">6</Thickness>
<CornerRadius x:Key="CardCornerRadius">4</CornerRadius>
</ResourceDictionary>

<ResourceDictionary x:Key="Light">
<StaticResource x:Key="CardBackground" ResourceKey="CardBackgroundFillColorDefaultBrush" />
<StaticResource x:Key="CardBorderBrush" ResourceKey="CardStrokeColorDefaultBrush" />
<StaticResource x:Key="CardBorderBrushPointerOver" ResourceKey="ControlFillColorSecondaryBrush" />
<StaticResource x:Key="CardBorderBrushFocused" ResourceKey="ControlFillColorTertiaryBrush" />
<x:Double x:Key="CardMinHeight">72</x:Double>
<x:Double x:Key="CardMaxWidth">344</x:Double>
<x:Double x:Key="CardElevation">6</x:Double>
<Thickness x:Key="CardPadding">16</Thickness>
<Thickness x:Key="CardBorderThickness">1</Thickness>
<Thickness x:Key="CardElevationMargin">6</Thickness>
<CornerRadius x:Key="CardCornerRadius">4</CornerRadius>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

<!-- Card Templates -->
<DataTemplate x:Key="DefaultHeaderContentTemplate">
<TextBlock Margin="16,16,16,0"
MaxLines="1"
Style="{ThemeResource TitleTextBlockStyle}"
Text="{Binding}" />
</DataTemplate>

<DataTemplate x:Key="DefaultSubHeaderContentTemplate">
<TextBlock Margin="16,0,16,16"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
MaxLines="2"
Style="{ThemeResource CaptionTextBlockStyle}"
Text="{Binding}" />
</DataTemplate>

<DataTemplate x:Key="DefaultSupportingContentTemplate">
<TextBlock Margin="16,0,16,16"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{ThemeResource BodyTextBlockStyle}"
Text="{Binding}" />
</DataTemplate>

<DataTemplate x:Key="DefaultMediaContentTemplate">
<Image MaxHeight="194"
Source="{Binding}"
Stretch="Uniform" />
</DataTemplate>

<!-- Default Card Style -->
<Style x:Key="DefaultCardStyle" TargetType="utu:Card">
<Setter Property="MinHeight" Value="{ThemeResource CardMinHeight}" />
<Setter Property="MaxWidth" Value="{ThemeResource CardMaxWidth}" />
<Setter Property="CornerRadius" Value="{ThemeResource CardCornerRadius}" />
<Setter Property="Margin" Value="{ThemeResource CardElevationMargin}" />
<Setter Property="Padding" Value="{ThemeResource CardPadding}" />
<Setter Property="Background" Value="{ThemeResource CardBackground}" />
<Setter Property="BorderBrush" Value="{ThemeResource CardBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource CardBorderThickness}" />
<Setter Property="HeaderContentTemplate" Value="{StaticResource DefaultHeaderContentTemplate}" />
<Setter Property="SubHeaderContentTemplate" Value="{StaticResource DefaultSubHeaderContentTemplate}" />
<Setter Property="SupportingContentTemplate" Value="{StaticResource DefaultSupportingContentTemplate}" />
<Setter Property="MediaContentTemplate" Value="{StaticResource DefaultMediaContentTemplate}" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />

<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="utu:Card">
<Grid x:Name="GridRoot"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
MaxWidth="{TemplateBinding MaxWidth}"
MaxHeight="{TemplateBinding MaxHeight}"
Margin="{TemplateBinding Margin}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<!-- Border for PointerOver State -->
<Border x:Name="HoverOverlay"
Grid.RowSpan="4"
Background="{ThemeResource CardBorderBrushPointerOver}"
Opacity="0" />

<!-- Border for Focused State -->
<Border x:Name="FocusedOverlay"
Grid.RowSpan="4"
Background="{ThemeResource CardBorderBrushFocused}"
Opacity="0" />

<!-- Media content part -->
<ContentPresenter x:Name="MediaContentPresenter"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding MediaContent}"
ContentTemplate="{TemplateBinding MediaContentTemplate}"
IsHitTestVisible="False"
Visibility="{Binding MediaContent, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EmptyOrNullToCollapsedConverter}}" />

<!-- Header part -->
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="1"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding HeaderContent}"
ContentTemplate="{TemplateBinding HeaderContentTemplate}"
Visibility="{Binding HeaderContent, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EmptyOrNullToCollapsedConverter}}" />

<!-- SubHeader part -->
<ContentPresenter x:Name="SubHeaderContentPresenter"
Grid.Row="2"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding SubHeaderContent}"
ContentTemplate="{TemplateBinding SubHeaderContentTemplate}"
Visibility="{Binding SubHeaderContent, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EmptyOrNullToCollapsedConverter}}" />

<!-- Supporting Content part -->
<ContentPresenter x:Name="SupportingContentPresenter"
Grid.Row="3"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding SupportingContent}"
ContentTemplate="{TemplateBinding SupportingContentTemplate}"
Visibility="{Binding SupportingContent, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EmptyOrNullToCollapsedConverter}}" />

<!-- Icons section part -->
<ContentPresenter x:Name="IconsContentPresenter"
Grid.Row="3"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding IconsContent}"
ContentTemplate="{TemplateBinding IconsContentTemplate}"
Visibility="{Binding IconsContentTemplate, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource NullToCollapsedConverter}}" />
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The visibility is bound to IconsContentTemplate instead of IconsContent. This means the presenter will be visible even when there's no actual content, as long as the template is set. Should bind to IconsContent to match the pattern used by other content presenters (lines 125, 135, 145, 155).

Suggested change
Visibility="{Binding IconsContentTemplate, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource NullToCollapsedConverter}}" />
Visibility="{Binding IconsContent, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EmptyOrNullToCollapsedConverter}}" />

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Binding to IconsContentTemplate is intentional and matches the Material styles pattern. The Icons section is designed to only be visible when a template is explicitly provided (not when content is provided). This is consistent with how all the Material Card styles handle icons visibility (see MaterialOutlinedCardStyle, MaterialFilledCardStyle, MaterialElevatedCardStyle, etc.).

<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<VisualState.Setters>
<Setter Target="HoverOverlay.Opacity" Value="0" />
<Setter Target="FocusedOverlay.Opacity" Value="0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver">
<VisualState.Setters>
<Setter Target="HoverOverlay.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Target="HoverOverlay.Opacity" Value="0" />
<Setter Target="FocusedOverlay.Opacity" Value="0" />
</VisualState.Setters>
</VisualState>

<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Target="HoverOverlay.Opacity" Value="0" />
<Setter Target="FocusedOverlay.Opacity" Value="0" />
<Setter Target="GridRoot.Opacity" Value="0.38" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<VisualState.Setters>
<Setter Target="FocusedOverlay.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerFocused" />
<VisualState x:Name="Unfocused">
<VisualState.Setters>
<Setter Target="FocusedOverlay.Opacity" Value="0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Loading
Loading