MaterialDesignInXamlToolkit Examples

repository·master·Indexed 19 days ago

https://github.com/keboo/materialdesigninxaml.examples

A collection of practical samples for using the MaterialDesignInXamlToolkit in WPF applications. Examples include animating MaterialDesignThemes.Wpf.Card shadows and opacity, implementing badged control animations, custom focus behaviors using Microsoft.Xaml.Behaviors.Wpf, populating DataGrids with EFCore and SQLite, creating custom themes with MahApps, and building horizontal menus using DrawerHost and DockPanel.

Tokens
8.3K
Snippets
19
Records
30
Agent score
67%

What's inside materialdesigninxaml.examples

  1. Overview of MVVM.Async patterns

    master
    The MVVM.Async sample project demonstrates various patterns for invoking asynchronous methods in response to property changes within the MVVM (Model-View-ViewModel) architectural pattern. It explores how to handle the complexities of async/await when a property setter or a command needs to trigger asynchronous operations without blocking the UI thread or causing common pitfalls like race conditions or re-entrancy issues.
  2. Modify control templates at run-time using TreeHelpers

    master

    The Utilities project provides a XAML-friendly way to modify properties of elements located inside a control's template at run-time without needing to re-template the entire control. This is achieved by targeting specific TemplatePartNames and setting their properties.

    Note: This approach is conceptually similar to XAML reflection. It can be brittle; if the underlying control's template changes (e.g., via a library update), the TemplatePartName might no longer exist, causing the modification to fail.

  3. Learn Material Design in XAML via Video and Blogs

    master

    For visual learners and those seeking deep dives into specific components, use the following resources:

    Video Streams

    Deep Dive Blog Posts

  4. How VisualToImageSourceConverter works

    master

    The VisualToImageSourceConverter implements the IValueConverter interface to transform a FrameworkElement (like a PackIcon) into a RenderTargetBitmap.

    Internal Logic:

    1. It calls Measure and Arrange on the input element to ensure it has a valid layout size.
    2. It initializes a RenderTargetBitmap using the element's ActualWidth and ActualHeight with a fixed DPI of 96 and PixelFormats.Pbgra32 format.
    3. It calls Render(element) to draw the visual into the bitmap.

    Note: This implementation uses a fixed DPI of 96. For production applications, you should consider using the actual client DPI to ensure icon sharpness across different displays.

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is FrameworkElement element)
        {
            element.Measure(new Size(element.Width, element.Height));
            element.Arrange(new Rect(new Size(element.Width, element.Height)));
            var rtb = new RenderTargetBitmap((int)Math.Ceiling(element.ActualWidth), 
                (int)Math.Ceiling(element.ActualHeight), 96, 96, PixelFormats.Pbgra32);
            rtb.Render(element);
    
            return rtb;
        }
    
        return null;
    }
  5. Synchronize ObservableCollections across threads

    master

    When loading data asynchronously (e.g., using Entity Framework Core in a background task) into an ObservableCollection that is bound to a UI element, you must call BindingOperations.EnableCollectionSynchronization in your ViewModel constructor. This allows the collection to be modified from a background thread without throwing exceptions.

    // In the ViewModel constructor
    BindingOperations.EnableCollectionSynchronization(People, _lockObject);
  6. How to implement custom themes with MDIX and MahApps

    master

    To create custom themes using Material Design In XAML (MDIX) and MahApps, you must integrate resource dictionaries and manage color palettes. This implementation involves three main approaches:

    1. MDIX SwatchesProvider: Uses standard MDIX swatch logic.
    2. Custom Resource Dictionaries: Defining your own color mappings.
    3. Code-behind: Generating or applying themes dynamically via code.

    Key Implementation Details

    Resource Dictionary Merging

    In your App.xaml, you must merge the appropriate resource dictionaries. This follows the pattern established in the MDIX library documentation to ensure all styles and brushes are correctly loaded.

    Color Mapping

    A common pattern is to define a set of base colors in App.xaml (e.g., light, mid, dark, and accent) and map MahApps brushes to these colors. While this example uses 4 colors, you can extend this list to support more complex color requirements.

    Handling MahApps Brushes with PaletteHelper

    The standard MDIX PaletteHelper makes assumptions about MahApps brush names. If you are using newer or additional MahApps brushes that the standard helper does not recognize, you should use a custom MahAppPaletteHelper class that derives from the MDIX PaletteHelper to account for these extra brushes.

  7. Implement a horizontal menu using DrawerHost and DockPanel

    master

    To create a horizontal menu layout, use a materialDesign:DrawerHost to manage the drawer visibility. The drawer content should contain a DockPanel that holds both a ToggleButton (styled with MaterialDesignHamburgerToggleButton) and the Menu control.

    Key implementation details:

    1. Drawer Control: Bind the IsLeftDrawerOpen property of the DrawerHost to the IsChecked property of a MenuToggleButton.
    2. Menu Layout: Use a VirtualizingStackPanel as the Menu.ItemsPanel to manage the menu items.
    3. Menu Styling: To ensure sub-menus appear correctly in a side drawer, use utilities:TreeHelpers.Modifiers to set the Popup.PlacementProperty to PlacementMode.Right for the MenuItem style. This ensures the popup part (PART_Popup) is positioned relative to the drawer content.
    <materialDesign:DrawerHost IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
        <materialDesign:DrawerHost.LeftDrawerContent>
            <DockPanel MinWidth="212">
                <ToggleButton Style="{StaticResource MaterialDesignHamburgerToggleButton}" 
                                DockPanel.Dock="Top"
                                HorizontalAlignment="Right" Margin="16"
                                IsChecked="{Binding ElementName=MenuToggleButton, Path=IsChecked, Mode=TwoWay}" />
                <Menu>
                    <Menu.Resources>
                        <Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
                            <Setter Property="utilities:TreeHelpers.Modifiers">
                                <Setter.Value>
                                    <utilities:ModifierCollection>
                                        <utilities:Modifier Property="{x:Static Popup.PlacementProperty}" 
                                                            Value="{x:Static PlacementMode.Right}" 
                                                            TemplatePartName="PART_Popup" />
                                    </utilities:ModifierCollection>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Menu.Resources>
                    <Menu.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel />
                        </ItemsPanelTemplate>
                    </Menu.ItemsPanel>
                    <MenuItem Header="Item 1">
                        <MenuItem Header="Sub Item 1" />
                    </MenuItem>
                </Menu>
            </DockPanel>
        </materialDesign:DrawerHost.LeftDrawerContent>
        
        <Grid>
            <!-- Main Content Area -->
            <ToggleButton x:Name="MenuToggleButton" Style="{StaticResource MaterialDesignHamburgerToggleButton}" />
        </Grid>
    </materialDesign:DrawerHost>
  8. Animate Card shadows using Code-behind

    master

    To animate shadows in C#, first clone an existing shadow resource (like MaterialDesignShadowDepth1) to create a unique instance for your control. This prevents animating the shared resource itself. Then, programmatically construct Storyboard objects using DoubleAnimation, set their target properties using PropertyPath, and attach them to the control's MouseEnter and MouseLeave events.

    // 1. Clone the resource to create a unique instance for the card
    var shadow1 = (DropShadowEffect)FindResource("MaterialDesignShadowDepth1");
    var effect = new DropShadowEffect
    {
        BlurRadius = shadow1.BlurRadius,
        ShadowDepth = shadow1.ShadowDepth,
        Direction = shadow1.Direction,
        Color = shadow1.Color,
        Opacity = shadow1.Opacity,
        RenderingBias = shadow1.RenderingBias
    };
    card.Effect = effect;
    
    // 2. Create the Enter Storyboard
    Storyboard mouseEnterStoryboard = new Storyboard();
    var enterBlurRadiusAnimation = new DoubleAnimation(shadow2.BlurRadius, new Duration(TimeSpan.FromSeconds(0.5)));
    Storyboard.SetTargetProperty(enterBlurRadiusAnimation, new PropertyPath(nameof(DropShadowEffect.BlurRadius)));
    Storyboard.SetTarget(enterBlurRadiusAnimation, effect);
    mouseEnterStoryboard.Children.Add(enterBlurRadiusAnimation);
    
    // ... (repeat for ShadowDepth and MouseLeave) ...
    
    mouseEnterStoryboard.Freeze();
    
    // 3. Attach to events
    card.MouseEnter += (sender, e) =>
    {
        card.BeginStoryboard(mouseEnterStoryboard);
    };
    card.MouseLeave += (sender, e) =>
    {
        card.BeginStoryboard(mouseLeaveStoryboard);
    };
  9. Bind a PackIcon to Window.Icon using VisualToImageSourceConverter

    master

    To use a Material Design PackIcon as your application's window icon, you must convert the visual control into an ImageSource. This is achieved using the VisualToImageSourceConverter.

    Steps:

    1. Register the Converter: Add the VisualToImageSourceConverter to your Window.Resources so it can be referenced via a StaticResource.
    2. Create a Binding: Bind the Window.Icon property to a PackIcon control. Use the converter within the binding to transform the icon control into a renderable bitmap.
    3. Configure the Icon: Set the Kind (from PackIconKind), Width, Height, and Foreground on the PackIcon to define how the icon appears in the window title bar.
    <Window.Resources>
        <local:VisualToImageSourceConverter x:Key="VisualToImageSourceConverter" />
    </Window.Resources>
    
    <!-- ... -->
    
    <Window.Icon>
        <Binding Converter="{StaticResource VisualToImageSourceConverter}">
            <Binding.Source>
                <materialDesign:PackIcon Kind="Smiley" Width="30" Height="30" Foreground="Red" />
            </Binding.Source>
        </Binding>
    </Window.Icon>
  10. Animate Card shadows using XAML

    master

    To animate the shadow of a MaterialDesignThemes.Wpf.Card, you must manually define a DropShadowEffect within the Effect property of the card. This allows you to assign an x:Name to the effect, which is required for Storyboard animations to target it. You can then use EventTriggers (such as MouseEnter and MouseLeave) to trigger Storyboards that animate properties like BlurRadius and ShadowDepth.

    <md:Card.Effect>
        <!-- Assign x:Name to allow Storyboard targeting -->
        <DropShadowEffect x:Name="DropShadowEffect" BlurRadius="5" ShadowDepth="1" Direction="270" Color="{StaticResource MaterialDesignShadow}" Opacity=".42" RenderingBias="Performance" />
    </md:Card.Effect>
    
    <md:Card.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.MouseEnter">
            <BeginStoryboard>
                <Storyboard TargetName="DropShadowEffect">
                    <DoubleAnimation Storyboard.TargetProperty="BlurRadius" To="25" Duration="0:0:0.5" />
                    <DoubleAnimation Storyboard.TargetProperty="ShadowDepth" To="8" Duration="0:0:0.5" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
        <EventTrigger RoutedEvent="FrameworkElement.MouseLeave">
            <BeginStoryboard>
                <Storyboard TargetName="DropShadowEffect">
                    <DoubleAnimation Storyboard.TargetProperty="BlurRadius" To="5" Duration="0:0:0.5" />
                    <DoubleAnimation Storyboard.TargetProperty="ShadowDepth" To="1" Duration="0:0:0.5" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </md:Card.Triggers>