3

简而言之:我有一个Style. 它使用TemplateBinding了相当多的参数来使其参数化,而不是一遍又一遍地重复自己。但是,当使用该样式的触发器并且在该触发器的 setter 中使用资源时,它就不会出现!甚至没有显示默认值。这是一个复制此问题的小程序:

测试字典.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:lcl="clr-namespace:MyNamespace">
    <Style TargetType="Button" x:Key="BtnTest">
        <Style.Resources>
            <Label Content="{TemplateBinding lcl:TestClass.String}" x:Key="innerLabel"/>
        </Style.Resources>
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="True">
                <Setter Property="Content" Value="{DynamicResource innerLabel}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</ResourceDictionary>

主窗口.xaml

<Window x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lcl="clr-namespace:MyNamespace"
        Title="Test" Width="500" Height="350">
    <Window.Resources>
        <ResourceDictionary Source="TestDictionary.xaml"/>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Content="Enable/Disable" Click="Click"/>
        <Button Grid.Column="1" x:Name="btn" Style="{DynamicResource BtnTest}" lcl:TestClass.String="TESTING"/>
    </Grid>
</Window>

主窗口.xaml.cs

using System.Windows;

namespace MyNamespace
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void Click(object sender, RoutedEventArgs e)
        {
            btn.IsEnabled = !btn.IsEnabled;
        }
    }
    public class TestClass
    {
        public static string GetString(DependencyObject obj)
        {
            return (string)obj.GetValue(StringProperty);
        }

        public static void SetString(DependencyObject obj, string value)
        {
            obj.SetValue(StringProperty, value);
        }
        public static readonly DependencyProperty StringProperty =
            DependencyProperty.RegisterAttached("String", typeof(string), typeof(TestClass), new PropertyMetadata("Default!"));
    }
}

而不是使用 a TemplateBinding,我还尝试了这个:

{Binding Path=lcl:TestClass.String, RelativeSource={RelativeSource AncestorType={x:Type Button}}}

它仍然没有工作。我知道我可能做错了什么,但问题是:它是什么?

4

3 回答 3

1

现在我看到了细节。你应该在相对源之前写的内容是这样的:

Binding Path=(lcl:TestClass.String)

不要忘记添加括号。

于 2013-06-24T19:50:40.720 回答
1

要完成这项工作,您真正需要的只是RelativeSource在绑定中使用。由于您Button在样式触发器中设置附加属性,因此您可以绑定到 self 上的附加属性:

<Style TargetType="Button" x:Key="BtnTest">
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="True">
            <Setter Property="Content" 
                    Value="{Binding Path=(lcl:TestClass.String), RelativeSource={RelativeSource Self}}"/>
        </Trigger>
    </Style.Triggers>
</Style>

使用您的方法的一件很酷的事情Button是,因为ContentControl您的附加属性可以是任何对象,而不仅仅是字符串。

并澄清你以前的方法出了什么问题 -

  • 正如其他人所说,TemplateBinding仅适用于ControlTemplates. 它也仅在DependencyProperty您为其创建模板的类上定义时才有效(例如,您永远不能这样TemplateBindingGrid.Row
  • 绑定到附加属性时,整个内容需要放在括号中,否则 WPF 将尝试绑定到属性的属性。否则你的RelativeSource绑定很接近!
  • 我认为如果你想拥有一个Label内部Button作为内容,它可能会起作用(我没有测试过),但这似乎不是最好的主意,因为你Button可以托管任何你想要的对象。

编辑更复杂的例子

因此,如果您需要显示多个动态属性,我建议使用DataTemplate

<Style TargetType="Button" x:Key="BtnTest">
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="True">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Label Content="{Binding Path=(lcl:TestClass.String), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
                    </DataTemplate>
                </Setter.Value>
            </Setter> 
        </Trigger>
    </Style.Triggers>
</Style>

另外,我想指出,DataTemplateSelector如果您有多个不同的标准来更改内容的外观,a 可能更适用。

于 2013-06-25T20:02:55.047 回答
0

您的示例不起作用,因为TemplateBinding仅适用于ControlTemplate. 要实现类似于 a TemplateBindingin的目标,Resources您需要做其他事情。这是一个例子。

为了TemplateBinding工作,您需要稍微修复代码(这只是一个没有资源的示例):

<Style x:Key="BtnTest" TargetType="{x:Type Button}">
    <Setter Property="MinHeight" Value="100" />
    <Setter Property="MinWidth" Value="200" />
    <Setter Property="BorderThickness" Value="2" />
    <Setter Property="BorderBrush" Value="Blue" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Background="{TemplateBinding Background}">
                    <ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding lcl:TestClass.String}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Opacity" Value="0.5" />
        </Trigger>
    </Style.Triggers>
</Style>

关于此主题的有用链接:这里和这里也是。

编辑:

您也可以使用应用程序设置来代替TestClass. 打开“项目 -> 属性:MyNamespace... -> 设置”并添加您的设置:

名称--------类型--------范围--------

LabelText---string--------User----------默认

LabelText为in 代码设置您的值。例如:

    public MainWindow()
    {
        InitializeComponent();

        MyNamespace.Properties.Settings.Default.LabelText = "Testing";
    }

并使用这个 ResourceDictionary:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:properties="clr-namespace:MyNamespace.Properties"
                xmlns:lcl="clr-namespace:MyNamespace">

<Style TargetType="Button" x:Key="BtnTest">
    <Style.Resources>
        <Label x:Key="innerLabel" Content="{Binding Source={x:Static properties:Settings.Default}, Path=LabelText, Mode=TwoWay}" />
    </Style.Resources>

    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="True">
            <Setter Property="Content" Value="{DynamicResource innerLabel}"/>
        </Trigger>
    </Style.Triggers>
</Style>

于 2013-06-22T19:07:16.987 回答