WPF accesses only single instance of derived class with dependency property

痞子三分冷 提交于 2020-01-15 01:22:24

问题


I have a simple class derived from Button with a dependency property:

public sealed class CircleButton : Button
{
    public Visual Visual
    {
        get { return (Visual)GetValue(VisualProperty); }
        set { SetValue(VisualProperty, value); }
    }

    public static readonly DependencyProperty VisualProperty = DependencyProperty.Register("Visual", typeof(Visual), typeof(CircleButton));
}

I have the following style for the CircleButton:

<Style x:Key="CircleButtonStyle" TargetType="{x:Type controls1:CircleButton}">
    ...
    <Setter Property="Content">
        <Setter.Value>
            <Rectangle Style="{StaticResource CircleButtonRectangleStyle}"/>
        </Setter.Value>
    </Setter>
    ...
</Style>

The rectangle style referenced in the CircleButton style is defined as:

<Style x:Key="CircleButtonRectangleStyle" TargetType="{x:Type Rectangle}">
    <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Width, Converter={StaticResource ArithmeticConverter}}"/>
    <Setter Property="Height" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Height, Converter={StaticResource ArithmeticConverter}}"/>
    <Setter Property="Fill" Value="White"/>
    <Setter Property="OpacityMask">
        <Setter.Value>
            <VisualBrush Visual="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Visual}"/>
        </Setter.Value>
    </Setter>
</Style>

Finally, my XAML defines three CircleButton instances:

        <StackPanel Grid.Row="1" Grid.Column="2">
            <controls:CircleButton HorizontalAlignment="Center" VerticalAlignment="Center" Width="30.0" Height="30.0" 
                    Style="{DynamicResource CircleButtonStyle}" Command="{Binding Add}" ToolTip="Add new product filter." BorderBrush="White" BorderThickness="1" 
                                   Background="{DynamicResource AccentColorBrush}" Visual="{DynamicResource appbar_add}"/>
            <controls:CircleButton HorizontalAlignment="Center" VerticalAlignment="Center" Width="30.0" Height="30.0" 
                    Style="{DynamicResource CircleButtonStyle}" Command="{Binding Remove}" ToolTip="Remove product filter." BorderBrush="White" BorderThickness="1" 
                                   Background="{DynamicResource AccentColorBrush}" Visual="{DynamicResource appbar_minus}"/>
            <controls:CircleButton HorizontalAlignment="Center" VerticalAlignment="Center" Width="30.0" Height="30.0" 
                    Style="{DynamicResource CircleButtonStyle}" Command="{Binding Remove}" ToolTip="Remove product filter." BorderBrush="White" BorderThickness="1" 
                                   Background="{DynamicResource AccentColorBrush}" Visual="{DynamicResource appbar_music}"/>
        </StackPanel>

All three buttons display, but only the last in the sequence has an image (it is the correct one). This occurs no matter how many buttons I add.

Anyone see what I'm missing and what I need to do to correct it?


回答1:


It's not obvious, but in fact you're only defining one instance of VisualBrush. There will be exactly one copy of the content of that Setter.Value element. Each time it gets added to one of your circle buttons, it gets yanked out of the previous one.

Try applying a template instead, and apply that visual brush to an element in the template. Elements in a template are created individually every time the template is applied.

Now, you can't apply a ControlTemplate to a Rectangle. But you can apply lots of templates to lots of things, so no big deal.

This might work. Button is a subclass of ContentControl so it's got a ContentTemplate property, which is instantiated to display whatever you put in the Button's Content property. This use is a little funny because it doesn't display the Content -- but you're not using Content.

<Style x:Key="CircleButtonStyle" TargetType="{x:Type controls1:CircleButton}">
    <!-- ... -->
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Rectangle
                    Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Width, Converter={StaticResource ArithmeticConverter}}"
                    Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Height, Converter={StaticResource ArithmeticConverter}}"
                    Fill="White"
                    >
                    <Rectangle.OpacityMask>
                        <VisualBrush Visual="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls1:CircleButton}}, Path=Visual}"/>
                    </Rectangle.OpacityMask>
                </Rectangle>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

But you know what type ContentControl.Content is? It's Object. You can stuff anything in there, and the ContentTemplate can do whatever it likes with whatever it finds. I bet this would work just fine, and might save you having to write that CircleButton subclass at all:

<Style x:Key="CircleButtonStyle" TargetType="{x:Type Button}">
    <!-- ... -->
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Rectangle
                    Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}, Path=Width, Converter={StaticResource ArithmeticConverter}}"
                    Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}, Path=Height, Converter={StaticResource ArithmeticConverter}}"
                    Fill="White"
                    >
                    <Rectangle.OpacityMask>
                        <VisualBrush Visual="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}, Path=Content}"/>
                    </Rectangle.OpacityMask>
                </Rectangle>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!-- ... -->

<Button 
    Style="{DynamicResource CircleButtonStyle}" 
    Command="{Binding Remove}" 
    ToolTip="Remove product filter." 
    Content="{DynamicResource appbar_music}"
    />


来源:https://stackoverflow.com/questions/36207572/wpf-accesses-only-single-instance-of-derived-class-with-dependency-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!