Search code examples
c#wpfdependency-properties

WPF DependencyProperty definitions and usages


I am still pretty new to creating DependencyProperties and extending other controls in WPF.

I have extended a ContentControl which I want to use as a DialogHeader, I am following how I have done this in the past, but maybe I am not doing it right:

This is what I have:

DialogHeader.cs

public static class DialogHeader
{
    static DialogHeader()
    {
        AllowDraggingProperty = AllowDraggingPropertyKey.DependencyProperty;
    }

    #region - AllowDragging -

    public static readonly DependencyProperty AllowDraggingProperty;

    private static readonly DependencyPropertyKey AllowDraggingPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly("AllowDragging", typeof(bool), typeof(DialogHeader),
            new PropertyMetadata(false));

    public static bool GetAllowDragging(FrameworkElement element)
    {
        return element is null
               ? throw new ArgumentNullException(nameof(element))
               : (bool)element.GetValue(AllowDraggingProperty);
    }

    public static void SetAllowDragging(FrameworkElement element, bool value)
    {
        if (element is null)
            throw new ArgumentNullException(nameof(element));

        element.SetValue(AllowDraggingProperty, value);
    }

    #endregion - AllowDragging - 
}

ContentControlStyle.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
                    xmlns:b="clr-namespace:WpfToolkit.UX.Controls.Behaviors"
                    xmlns:ux="clr-namespace:WpfToolkit.UX.Controls.Controls">

    <ControlTemplate x:Key="DialogHeader" TargetType="{x:Type ContentControl}">

        <Border x:Name="bdrOuter"
                Height="72"
                Style="{DynamicResource DialogHeader.Border}">

            <ContentControl x:Name="ccDraggableArea"
                            Template="{DynamicResource DraggableArea}">

                <DockPanel LastChildFill="True"
                           Margin="4,0">

                    <Button x:Name="btnClose"
                            DockPanel.Dock="Right"
                            Margin="0,6,0,0"
                            Template="{DynamicResource Close}"
                            VerticalAlignment="Top" />

                    <Image x:Name="imgLogo"
                           DockPanel.Dock="Left"
                           HorizontalAlignment="Left"
                           Margin="0,8,8,4"
                           RenderOptions.BitmapScalingMode="HighQuality"
                           Source="pack://application:,,,/UX.Themes;component/Assets/Branding/Logo.png"
                           Stretch="Uniform" 
                           VerticalAlignment="Center" />

                    <Label x:Name="lblHeading"
                           Content="Dialog Header"
                           DockPanel.Dock="Left"
                           FontFamily="{DynamicResource Inter}"
                           Foreground="{DynamicResource ForegroundBrush}"
                           Template="{DynamicResource DialogHeading}"
                           VerticalAlignment="Center" />

                </DockPanel>

            </ContentControl>

        </Border>

        <ControlTemplate.Triggers>

            <Trigger Property="ux:DialogHeader.AllowDragging" Value="False">
                <Setter TargetName="ccDraggableArea" Property="Visibility" Value="Collapsed" />
            </Trigger>

        </ControlTemplate.Triggers>

    </ControlTemplate>

</ResourceDictionary>

MainWindow.xaml

<TabItem Header="Window Header/Footer" IsSelected="True">

    <Grid Background="Transparent">

        <Grid.RowDefinitions>
            <RowDefinition MinHeight="100" Height="Auto"/>
            <RowDefinition MinHeight="100" Height="Auto"/>
            <RowDefinition MinHeight="100" Height="Auto"/>
            <RowDefinition MinHeight="100" Height="Auto"/>
        </Grid.RowDefinitions>
        
        <ContentControl x:Name="ccHeader"
                        Grid.Row="0"
                        Template="{DynamicResource DialogHeader}"
                        ux:DialogHeader.AllowDragging="False"/>
        
        
        <ContentControl x:Name="ccHeaderCompact"
                        Grid.Row="1"
                        Template="{DynamicResource DialogHeader.Compact}" />
        
        <ContentControl x:Name="ccFooter"
                        Grid.Row="2"
                        Template="{DynamicResource DialogFooter}" />
        
        <ContentControl x:Name="ccFooterCompact"
                        Grid.Row="3"
                        Template="{DynamicResource DialogFooter.Compact}" />
        
    </Grid>
    
</TabItem>

What I am in the process of doing is creating our own internal WPF Toolkit of Controls and Templates to be used over a couple of our solutions. As part of this, I am creating a window that shows all of the styles and templates, hence why the MainWindow above has a tab control.

The issue I am facing at the moment is on running the app, I hit this exception:

InvalidOperationException: 'AllowDragging' property was registered as read-only and cannot be modified without an authorization key.

I assume this is caused by the setup I have, but this has worked in other instances where I have implemented this, I have never seen the ReadOnly exception before.


Solution

  • The XAML assignment

    <ContentControl ... ux:DialogHeader.AllowDragging="False" />
    

    means that you do not want to create a read-only attached property, so you must not be using DependencyProperty.RegisterAttachedReadOnly. Use DependencyProperty.RegisterAttached instead.

    This is how your code should look like:

    public static class DialogHeader
    {
        public static readonly DependencyProperty AllowDraggingProperty =
            DependencyProperty.RegisterAttached(
                "AllowDragging", typeof(bool), typeof(DialogHeader));
    
        public static bool GetAllowDragging(FrameworkElement element)
        {
            return (bool)element.GetValue(AllowDraggingProperty);
        }
    
        public static void SetAllowDragging(FrameworkElement element, bool value)
        {
            element.SetValue(AllowDraggingProperty, value);
        }
    }