Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
9 changes: 9 additions & 0 deletions MainDemo.Wpf/Domain/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,15 @@ private static IEnumerable<DemoItem> GenerateDemoItems(ISnackbarMessageQueue sna
DocumentationLink.DemoPageLink<SmartHint>(),
DocumentationLink.StyleLink("SmartHint"),
});

yield return new DemoItem(
"PopupBox",
typeof(PopupBox),
new[]
{
DocumentationLink.DemoPageLink<PopupBox>(),
DocumentationLink.StyleLink("PopupBox"),
});
}

private bool DemoItemsFilter(object obj)
Expand Down
208 changes: 208 additions & 0 deletions MainDemo.Wpf/PopupBox.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
<UserControl x:Class="MaterialDesignDemo.PopupBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MaterialDesignDemo"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<!-- This is needed to avoid runtime exceptions?! Seems like this might be a bug? -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.PopupBox.xaml" />
</ResourceDictionary.MergedDictionaries>
Comment thread
Keboo marked this conversation as resolved.

<DataTemplate x:Key="ContentTemplateGrid">
<Grid Width="200" Height="100" TextElement.Foreground="{DynamicResource MaterialDesignLightForeground}">
<TextBlock Text="Popup content in grid" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>

<DataTemplate x:Key="ContentTemplateGridWithBackground">
<Grid Width="200" Height="100" Background="Fuchsia">
<TextBlock Text="Popup content in colored grid" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>

<DataTemplate x:Key="ContentTemplateButtonStack">
<!-- Margin of 10 here is to "make room" for the elevation drop shadows of the buttons. Must be compensated for in some (left/right) alignment scenarios using PopupBox.PopupHorizontalOffset -->
<StackPanel Margin="10">
<Button Content="1"
Opacity="0.5"
ToolTip="One with custom opacity" />
<Button Content="2" ToolTip="Two" />
<Button Content="3" ToolTip="Three" />
</StackPanel>
Comment thread
Keboo marked this conversation as resolved.
</DataTemplate>

<DataTemplate x:Key="ContentTemplateButtonStackWithBackground">
<Grid Background="Fuchsia">
<StackPanel Margin="10">
<Button Content="1"
Opacity="0.5"
ToolTip="One with custom opacity" />
<Button Content="2" ToolTip="Two" />
<Button Content="3" ToolTip="Three" />
</StackPanel>
</Grid>
</DataTemplate>

<x:Array x:Key="{x:Static local:PopupBox.DefaultStyleContentKey}" Type="ComboBoxItem">
<ComboBoxItem Tag="{StaticResource ContentTemplateGrid}" materialDesign:HintAssist.HelperText="Selected content works best when used with the MaterialDesignPopupBox style">Grid</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource ContentTemplateGridWithBackground}">Colored grid</ComboBoxItem>
</x:Array>

<x:Array x:Key="{x:Static local:PopupBox.MultiFloatingActionStyleContentKey}" Type="ComboBoxItem">
<ComboBoxItem Tag="{StaticResource ContentTemplateButtonStack}" materialDesign:HintAssist.HelperText="Margin in the selected content (stack of buttons) needs to be compensated for in certain alignments (left/right)">Stack of buttons</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource ContentTemplateButtonStackWithBackground}">Stack of buttons in colored grid</ComboBoxItem>
</x:Array>

<local:ComboBoxItemToDataTemplateConverter x:Key="ComboBoxItemToDataTemplateConverter" />
<local:ComboBoxItemToStyleConverter x:Key="ComboBoxItemToStyleConverter" />
<local:ComboBoxItemToHelperTextConverter x:Key="ComboBoxItemToHelperTextConverter" />

<Thickness x:Key="Spacer">0,30,0,0</Thickness>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>

<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<TextBlock Grid.Row="0" HorizontalAlignment="Center" Style="{StaticResource MaterialDesignSubtitle2TextBlock}"
Text="{Binding ElementName=ContentComboBox, Path=SelectedItem, Converter={StaticResource ComboBoxItemToHelperTextConverter}}" />

<materialDesign:PopupBox Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" SnapsToDevicePixels="True" Padding="0"
Style="{Binding ElementName=StyleComboBox, Path=SelectedItem, Converter={StaticResource ComboBoxItemToStyleConverter}}"
PopupElevation="{Binding ElementName=ElevationComboBox, Path=SelectedItem}"
PopupUniformCornerRadius="{Binding ElementName=UniformCornerRadiusComboBox, Path=SelectedItem}"
PopupHorizontalOffset="{Binding ElementName=HorizontalOffsetComboBox, Path=SelectedItem}"
PopupVerticalOffset="{Binding ElementName=VerticalOffsetComboBox, Path=SelectedItem}"
PlacementMode="{Binding ElementName=PopupBoxPlacementModeComboBox, Path=SelectedItem}">
<ContentControl ContentTemplate="{Binding ElementName=ContentComboBox, Path=SelectedItem, Converter={StaticResource ComboBoxItemToDataTemplateConverter}}" />
</materialDesign:PopupBox>
</Grid>

<StackPanel Grid.Column="1" Orientation="Vertical">
<GroupBox Header="Properties" Padding="10">
<StackPanel Orientation="Vertical">
<TextBlock Text="Style:" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="StyleComboBox" SelectionChanged="StyleComboBox_OnSelectionChanged">
<ComboBoxItem Tag="{StaticResource MaterialDesignPopupBox}">MaterialDesignPopupBox</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource MaterialDesignMultiFloatingActionPopupBox}">MaterialDesignMultiFloatingActionPopupBox</ComboBoxItem>
</ComboBox>

<TextBlock Text="Elevation:" Margin="{StaticResource Spacer}" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="ElevationComboBox" SelectedIndex="6">
<materialDesign:Elevation>Dp0</materialDesign:Elevation>
<materialDesign:Elevation>Dp1</materialDesign:Elevation>
<materialDesign:Elevation>Dp2</materialDesign:Elevation>
<materialDesign:Elevation>Dp3</materialDesign:Elevation>
<materialDesign:Elevation>Dp4</materialDesign:Elevation>
<materialDesign:Elevation>Dp5</materialDesign:Elevation>
<materialDesign:Elevation>Dp6</materialDesign:Elevation>
<materialDesign:Elevation>Dp7</materialDesign:Elevation>
<materialDesign:Elevation>Dp8</materialDesign:Elevation>
<materialDesign:Elevation>Dp12</materialDesign:Elevation>
<materialDesign:Elevation>Dp16</materialDesign:Elevation>
<materialDesign:Elevation>Dp24</materialDesign:Elevation>
</ComboBox>

<TextBlock Text="PopupUniformCornerRadius:" Margin="{StaticResource Spacer}" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="UniformCornerRadiusComboBox" SelectedIndex="2">
<system:Double>0</system:Double>
<system:Double>2</system:Double>
<system:Double>4</system:Double>
<system:Double>6</system:Double>
<system:Double>8</system:Double>
<system:Double>10</system:Double>
<system:Double>20</system:Double>
</ComboBox>

<TextBlock Text="PopupHorizontalOffset:" Margin="{StaticResource Spacer}" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="HorizontalOffsetComboBox" SelectedIndex="10">
<system:Double>-100</system:Double>
<system:Double>-50</system:Double>
<system:Double>-20</system:Double>
<system:Double>-15</system:Double>
<system:Double>-10</system:Double>
<system:Double>-5</system:Double>
<system:Double>-4</system:Double>
<system:Double>-3</system:Double>
<system:Double>-2</system:Double>
<system:Double>-1</system:Double>
<system:Double>0</system:Double>
<system:Double>1</system:Double>
<system:Double>2</system:Double>
<system:Double>3</system:Double>
<system:Double>4</system:Double>
<system:Double>5</system:Double>
<system:Double>10</system:Double>
<system:Double>15</system:Double>
<system:Double>20</system:Double>
<system:Double>50</system:Double>
<system:Double>100</system:Double>
</ComboBox>

<TextBlock Text="PopupVerticalOffset:" Margin="{StaticResource Spacer}" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="VerticalOffsetComboBox" SelectedIndex="10">
<system:Double>-100</system:Double>
<system:Double>-50</system:Double>
<system:Double>-20</system:Double>
<system:Double>-15</system:Double>
<system:Double>-10</system:Double>
<system:Double>-5</system:Double>
<system:Double>-4</system:Double>
<system:Double>-3</system:Double>
<system:Double>-2</system:Double>
<system:Double>-1</system:Double>
<system:Double>0</system:Double>
<system:Double>1</system:Double>
<system:Double>2</system:Double>
<system:Double>3</system:Double>
<system:Double>4</system:Double>
<system:Double>5</system:Double>
<system:Double>10</system:Double>
<system:Double>15</system:Double>
<system:Double>20</system:Double>
<system:Double>50</system:Double>
<system:Double>100</system:Double>
</ComboBox>

<TextBlock Text="PopupBoxPlacementMode:" Margin="{StaticResource Spacer}" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="PopupBoxPlacementModeComboBox" SelectedIndex="1">
<materialDesign:PopupBoxPlacementMode>BottomAndAlignCentres</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>BottomAndAlignLeftEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>BottomAndAlignRightEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>LeftAndAlignBottomEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>LeftAndAlignMiddles</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>LeftAndAlignTopEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>RightAndAlignBottomEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>RightAndAlignMiddles</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>RightAndAlignTopEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>TopAndAlignCentres</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>TopAndAlignLeftEdges</materialDesign:PopupBoxPlacementMode>
<materialDesign:PopupBoxPlacementMode>TopAndAlignRightEdges</materialDesign:PopupBoxPlacementMode>
</ComboBox>
</StackPanel>
</GroupBox>

<GroupBox Header="Popup" Margin="{StaticResource Spacer}" Padding="10">
<StackPanel Orientation="Vertical">
<TextBlock Text="Popup Content:" Style="{StaticResource MaterialDesignSubtitle2TextBlock}" />
<ComboBox x:Name="ContentComboBox" />
</StackPanel>
</GroupBox>
</StackPanel>
</Grid>
</UserControl>
69 changes: 69 additions & 0 deletions MainDemo.Wpf/PopupBox.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections;
using System.Globalization;
using System.Windows.Data;
using MaterialDesignThemes.Wpf;

namespace MaterialDesignDemo;

/// <summary>
/// Interaction logic for PopupBox.xaml
/// </summary>
public partial class PopupBox : UserControl
{
public const string DefaultStyleContentKey = nameof(DefaultStyleContentKey);
public const string MultiFloatingActionStyleContentKey = nameof(MultiFloatingActionStyleContentKey);

private readonly IEnumerable _defaultStyleContent;
private readonly IEnumerable _multiFloatingActionStyleContentKey;

public PopupBox()
{
InitializeComponent();

_defaultStyleContent = (IEnumerable)FindResource(DefaultStyleContentKey);
_multiFloatingActionStyleContentKey = (IEnumerable)FindResource(MultiFloatingActionStyleContentKey);

Loaded += (sender, args) => StyleComboBox.SelectedIndex = 0;
}

private void StyleComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem selectedItem = (ComboBoxItem) StyleComboBox.SelectedItem;
if (Equals(selectedItem.Content, "MaterialDesignPopupBox"))
{
ContentComboBox.ItemsSource = _defaultStyleContent;
}
else
{
ContentComboBox.ItemsSource = _multiFloatingActionStyleContentKey;
}
ContentComboBox.SelectedIndex = 0;
}
}

internal class ComboBoxItemToDataTemplateConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is ComboBoxItem { Tag: DataTemplate template} ? template : null;

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}

internal class ComboBoxItemToStyleConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is ComboBoxItem { Tag: Style style } ? style : null;

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}

internal class ComboBoxItemToHelperTextConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is ComboBoxItem item ? HintAssist.GetHelperText(item) : null!;

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
37 changes: 37 additions & 0 deletions MaterialDesignThemes.UITests/WPF/PopupBox/PopupBoxTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.ComponentModel;

namespace MaterialDesignThemes.UITests.WPF.PopupBox;

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

[Theory]
[InlineData(Elevation.Dp0)]
[InlineData(Elevation.Dp16)]
[InlineData(Elevation.Dp24)]
[Description("Issue 3129")]
public async Task PopupBox_WithElevation_AppliesElevationToNestedCard(Elevation elevation)
{
await using var recorder = new TestRecorder(App);

//Arrange
IVisualElement<Wpf.PopupBox> popupBox = await LoadXaml<Wpf.PopupBox>($@"
<materialDesign:PopupBox VerticalAlignment=""Top""
PopupElevation=""{elevation}"">
<StackPanel>
<Button Content=""More"" />
<Button Content=""Options"" />
</StackPanel>
</materialDesign:PopupBox>");

IVisualElement<Card> card = await popupBox.GetElement<Card>("/Card");

// Assert
Assert.Equal(elevation, await card.GetProperty<Elevation?>(ElevationAssist.ElevationProperty));

recorder.Success();
}
}
13 changes: 12 additions & 1 deletion MaterialDesignThemes.Wpf/Card.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ public double UniformCornerRadius
}
public static readonly DependencyProperty UniformCornerRadiusProperty
= DependencyProperty.Register(nameof(UniformCornerRadius), typeof(double), typeof(Card),
new FrameworkPropertyMetadata(DefaultUniformCornerRadius, FrameworkPropertyMetadataOptions.AffectsMeasure));
new FrameworkPropertyMetadata(DefaultUniformCornerRadius, FrameworkPropertyMetadataOptions.AffectsMeasure, UniformCornerRadiusChangedCallback));

private static void UniformCornerRadiusChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Card card = (Card) d;
card.UpdateContentClip();
}

#endregion

#region DependencyProperty : ContentClipProperty
Expand Down Expand Up @@ -59,7 +66,11 @@ public override void OnApplyTemplate()
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateContentClip();
}

private void UpdateContentClip()
{
if (_clipBorder is null)
{
return;
Expand Down
20 changes: 20 additions & 0 deletions MaterialDesignThemes.Wpf/DpiHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public static double TransformToDeviceY(Visual visual, double y)
return TransformToDeviceY(y);
}

public static double TransformFromDeviceY(Visual visual, double y)
{
var source = PresentationSource.FromVisual(visual);
if (source?.CompositionTarget != null) return y / source.CompositionTarget.TransformToDevice.M22;

return TransformFromDeviceY(y);
}

public static double TransformToDeviceX(Visual visual, double x)
{
var source = PresentationSource.FromVisual(visual);
Expand All @@ -39,8 +47,20 @@ public static double TransformToDeviceX(Visual visual, double x)
return TransformToDeviceX(x);
}

public static double TransformFromDeviceX(Visual visual, double x)
{
var source = PresentationSource.FromVisual(visual);
if (source?.CompositionTarget != null) return x / source.CompositionTarget.TransformToDevice.M11;

return TransformFromDeviceX(x);
}

public static double TransformToDeviceY(double y) => y * DpiY / StandardDpiY;

public static double TransformFromDeviceY(double y) => y / DpiY * StandardDpiY;

public static double TransformToDeviceX(double x) => x * DpiX / StandardDpiX;

public static double TransformFromDeviceX(double x) => x / DpiX * StandardDpiX;
}
}
Loading