WPF Style/Template gets applied only to the last added element

社会主义新天地 提交于 2019-12-20 02:35:14

问题


I have a simple control derived from ContentControl with this code:

public class HeaderFooterControl : ContentControl
{
    static HeaderFooterControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderFooterControl), new FrameworkPropertyMetadata(typeof(HeaderFooterControl)));
    }

    public object Header
    {
        get { return (object)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(object), typeof(HeaderFooterControl));

    public object Footer
    {
        get { return (object)GetValue(FooterProperty); }
        set { SetValue(FooterProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Footer.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FooterProperty =
        DependencyProperty.Register("Footer", typeof(object), typeof(HeaderFooterControl));



    public string HeaderText
    {
        get { return (string)GetValue(HeaderTextProperty); }
        set { SetValue(HeaderTextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for HeaderText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty HeaderTextProperty =
        DependencyProperty.Register("HeaderText", typeof(string), typeof(HeaderFooterControl));



    public string FooterText
    {
        get { return (string)GetValue(FooterTextProperty); }
        set { SetValue(FooterTextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for FooterText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FooterTextProperty =
        DependencyProperty.Register("FooterText", typeof(string), typeof(HeaderFooterControl));


}

It has customizable Header and Footer properties so user can attach any controls within.

The only window in this application defined like this:

<Window x:Class="CustomControlTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CustomControlTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="{x:Type local:HeaderFooterControl}" x:Key="DefaultHeaderFooterStyle">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:HeaderFooterControl}">
                        <Grid Background="Pink">
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <ContentPresenter Content="{Binding Header, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
                            <ContentPresenter Grid.Row="1"/>
                            <ContentPresenter Content="{Binding Footer, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" Grid.Row="2"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="{x:Type local:HeaderFooterControl}" BasedOn="{StaticResource DefaultHeaderFooterStyle}">
            <Style.Setters>
                <Setter Property="Header">
                    <Setter.Value>
                        <TextBlock Text="{Binding HeaderText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HeaderFooterControl}}}" Background="Yellow" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
                    </Setter.Value>
                </Setter>
                <Setter Property="Footer">
                    <Setter.Value>
                        <TextBlock Text="{Binding FooterText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HeaderFooterControl}}}" Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
                    </Setter.Value>
                </Setter>
            </Style.Setters>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <local:HeaderFooterControl HeaderText="H1" FooterText="F1" Margin="10"/>
        <local:HeaderFooterControl HeaderText="H2" FooterText="F2" Grid.Row="1" Margin="10"/>
        <local:HeaderFooterControl HeaderText="H3" FooterText="F3" Grid.Row="2" Margin="10"/>
    </Grid>
</Window>

Resources for this window define 2 styles for my custom control. The custom control displays header, main content and footer. By default header/footer are TextBlocks with it's Text properties bound to HeaderText or FooterText properties of HeaderFooterControl. The problem is that only last added HeaderFooterControl displays any content. Why is it so?


回答1:


The only solution I found so far is to move content for header and footer parts into resources, then reference those resources from xaml code using StaticResource extention. All content are UIElements so every instance should have x:Shared="False" attribute set so it will be created every time it's needed in VisualTree. Also check out this answer, it implies the same behaviour described https://stackoverflow.com/a/8702180/371967

<Window x:Class="CustomControlTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CustomControlTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style TargetType="{x:Type local:HeaderFooterControl}" x:Key="DefaultHeaderFooterStyle">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:HeaderFooterControl}">
                        <Grid Background="Pink">
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <ContentPresenter Content="{Binding Header, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
                            <ContentPresenter Grid.Row="1"/>
                            <ContentPresenter Content="{Binding Footer, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" Grid.Row="2"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <TextBlock x:Key="DefaultHeaderContent" Text="{Binding HeaderText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HeaderFooterControl}}}" Background="Yellow" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Shared="False"/>
        <TextBlock x:Key="DefaultFooterContent" Text="{Binding FooterText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:HeaderFooterControl}}}" Background="Green" HorizontalAlignment="Stretch" VerticalAlignment="Center" x:Shared="False"/>

        <Style TargetType="{x:Type local:HeaderFooterControl}" BasedOn="{StaticResource DefaultHeaderFooterStyle}">
            <Style.Setters>
                <Setter Property="Header" Value="{StaticResource DefaultHeaderContent}"/>
                <Setter Property="Footer" Value="{StaticResource DefaultFooterContent}"/>
            </Style.Setters>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <local:HeaderFooterControl HeaderText="H1" FooterText="F1" Margin="10"/>
        <local:HeaderFooterControl HeaderText="H2" FooterText="F2" Grid.Row="1" Margin="10"/>
        <local:HeaderFooterControl HeaderText="H3" FooterText="F3" Grid.Row="2" Margin="10"/>
    </Grid>
</Window>


来源:https://stackoverflow.com/questions/38218945/wpf-style-template-gets-applied-only-to-the-last-added-element

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