WPF自定义控件按钮内容缺少多个按钮

时间:2016-03-01 17:49:50

标签: c# .net wpf xaml custom-controls

首先,这是在.NET 4.0中,因为它必须是。我知道在.NET的更高版本中已经修复了一些错误,所以如果这是一个真正的.NET错误,我想我将不得不忍受使用用户控件似乎没有这个问题。

我在WPF中创建了一个自定义控件库,以生成可在第三方软件中使用的可自定义按钮。我似乎有一个问题,但是,有多个按钮导致除了其中一个按钮之外的所有按钮都丢失了。我在SNOOP中证实了这个问题。内容并不存在。 SNOOP树到达内容演示者,然后在其下面没有任何内容,除了一个有内容的按钮。我已经创建了一个非常简单的问题示例。

我的图书馆的Generic.xaml如下:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:CustomControlsLibrary.Controls">

<Style x:Key="CustomButtonStyle" TargetType="{x:Type controls:CustomButton}">
    <Setter Property="FontSize" Value="16" />
    <Setter Property="FontWeight" Value="Bold" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:CustomButton}">
                <Border CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="3" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}">
                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" ContentSource="Content" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="Button1Style" TargetType="{x:Type controls:Button1}" BasedOn="{StaticResource CustomButtonStyle}" >
    <Setter Property="CornerRadius" Value="4" />
    <Setter Property="BorderBrush" Value="White" />
    <Setter Property="Height" Value="40" />
    <Setter Property="Width" Value="100" />
    <Setter Property="Foreground" Value="White" />
    <Setter Property="Content">
        <Setter.Value>
            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=controls:Button1}, Path=Text}" />
        </Setter.Value>
    </Setter>
</Style>

两个控制类如下:

的CustomButton:

public class CustomButton : Button
{
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(CustomButton), new FrameworkPropertyMetadata(new CornerRadius(0)));

    public CornerRadius CornerRadius
    {
        get { return (CornerRadius)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }

    static CustomButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
    }
}

Button1的:

public class Button1 : CustomButton
{

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(Button1), new FrameworkPropertyMetadata(""));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    static Button1()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Button1), new FrameworkPropertyMetadata(typeof(Button1)));
    }
}

然后我创建了一个简单的WPF应用程序,其中只有一个主窗口,其中包含MainWindow.xaml中的所有逻辑:

<Window x:Class="CustomControlLibraryTestApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:CustomControlsLibrary.Controls;assembly=CustomControlsLibrary"
    Title="MainWindow" Height="350" Width="525" Background="DarkGray">

<Window.Resources>
    <ResourceDictionary Source="pack://application:,,,/CustomControlsLibrary;component/Themes/Generic.xaml" />
</Window.Resources>

<StackPanel>
    <controls:Button1 Style="{StaticResource Button1Style}" Background="Red" Text="Button 1" />
    <controls:Button1 Style="{StaticResource Button1Style}" Background="Blue" Text="Button 2" />
</StackPanel>

运行时,按钮1的内容会丢失,而按钮2看起来很好。从窗口中删除按钮2会导致按钮1按预期显示。

如前所述,SNOOP表示当两个按钮都存在时,按钮1的内容就不存在。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我将在这里提出反对意见,首先引用马修麦克唐纳和#34; Pro WPF in C#&#34;:

  

自定义控件仍然是构建自定义窗口小部件的有用方法   你可以在应用程序之间共享,但它们不再是一个   要增强和自定义核心控件时的要求。 (至   了解这种变化有多么显着,有助于指出这一点   本书的前身,Pro .NET 2.0 Windows Forms和Custom   C#中的控件有九个完整的章节关于自定义控件和   其他章节中的其他示例。但是在这本书中,你已经成功了   没有一个自定义控制瞄准,它到第18章!)

简而言之,只需要创建额外的按钮类来控制模板中已存在的属性。您可以使用数据绑定或附加属性等轻松实现这一点,并且它将与Blend等工具更加兼容。

为了说明这一点,这是您在示例代码中公开的两个属性的帮助类:

public static class ButtonHelper
{
    public static double GetCornerRadius(DependencyObject obj)
    {
        return (double)obj.GetValue(CornerRadiusProperty);
    }

    public static void SetCornerRadius(DependencyObject obj, double value)
    {
        obj.SetValue(CornerRadiusProperty, value);
    }

    // Using a DependencyProperty as the backing store for CornerRadius.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty CornerRadiusProperty =
        DependencyProperty.RegisterAttached("CornerRadius", typeof(double), typeof(ButtonHelper), new PropertyMetadata(0.0));


    public static string GetButtonText(DependencyObject obj)
    {
        return (string)obj.GetValue(ButtonTextProperty);
    }

    public static void SetButtonText(DependencyObject obj, string value)
    {
        obj.SetValue(ButtonTextProperty, value);
    }

    // Using a DependencyProperty as the backing store for ButtonText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ButtonTextProperty =
        DependencyProperty.RegisterAttached("ButtonText", typeof(string), typeof(ButtonHelper), new PropertyMetadata(""));

}

现在我们可以立即为每个按钮类型创建两个样式,在内部绑定到这些属性:

    <Style x:Key="RoundedButtonStyle" TargetType="{x:Type Button}" >
        <Setter Property="Margin" Value="10" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="16" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="BorderBrush" Value="Red" />
        <Setter Property="Background" Value="Red" />
        <Setter Property="controls:ButtonHelper.CornerRadius" Value="4" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border CornerRadius="{Binding Path=(controls:ButtonHelper.CornerRadius),
                        RelativeSource={RelativeSource TemplatedParent}}" BorderThickness="3"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            Background="{TemplateBinding Background}">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" ContentSource="Content" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="TextButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource RoundedButtonStyle}">
        <Setter Property="BorderBrush" Value="Blue" />
        <Setter Property="Background" Value="Blue" />
        <Setter Property="controls:ButtonHelper.ButtonText" Value="TextButton" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border CornerRadius="{Binding Path=(controls:ButtonHelper.CornerRadius),
                        RelativeSource={RelativeSource TemplatedParent}}" BorderThickness="3"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            Background="{TemplateBinding Background}">
                        <TextBlock Text="{Binding Path=(controls:ButtonHelper.ButtonText),
                            RelativeSource={RelativeSource TemplatedParent}}" Background="Transparent" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

那就是它!不需要自定义控件不需要x:由于内容直接在样式中指定而共享,并且它的重量更轻。以下是使用它们的一个例子:

<UniformGrid Columns="2">

    <Button Style="{StaticResource RoundedButtonStyle}" Content="RoundedButton" />
    <Button Style="{StaticResource RoundedButtonStyle}" Content="RoundedButton big radius" controls:ButtonHelper.CornerRadius="20"/>

    <Button Style="{StaticResource TextButtonStyle}" />
    <Button Style="{StaticResource TextButtonStyle}" controls:ButtonHelper.ButtonText="TextButton new text"/>

    <Button Style="{StaticResource TextButtonStyle}" BorderBrush="Green" Background="Green"
            controls:ButtonHelper.ButtonText="Both text and radius"
            controls:ButtonHelper.CornerRadius="20" />

</UniformGrid>

结果如下:

enter image description here

我当然意识到我已经在每个模板中指定了边框,但是也可以通过在边框内放置内容控件并使用数据模板来设置内容来轻松删除。

答案 1 :(得分:1)

发生的事情是该样式实际上只有一个TextBlock实例。当样式应用于第二个按钮时,TextBlock实际上是新控件的替代品。您应该可以通过在TextBlock元素上设置x:Shared="false"来避免这种情况。