首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >TemplateBinding in EventTrigger

TemplateBinding in EventTrigger
EN

Stack Overflow用户
提问于 2012-12-19 16:10:32
回答 1查看 1.9K关注 0票数 2

我在EventTrigger中有以下绑定:

代码语言:javascript
复制
<ControlTemplate.Triggers>
    <EventTrigger RoutedEvent="PreviewMouseDown">
        <SoundPlayerAction Source="{Binding Path=SoundFile, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource soundFileConverter}}" />
    </EventTrigger>
    ...

该过程如下:自定义控件(即其模板)具有一个名为SoundFile的属性,该属性是枚举类型。在转换器中,应该将此枚举值转换为Uri,以将其传递给SoundPlayerAction。

问题是:无论如何都不会调用转换器。输出窗口显示以下错误:

无法为目标元素找到控制FrameworkElement或FrameworkContentElement的方法。BindingExpression:Path=SoundFile;DataItem=null;目标元素是'SoundPlayerAction‘HashCode=46763000);目标属性是'Source’(类型'Uri')

绑定表达式有什么问题?

编辑:

为了进行更好的概述,下面是控件的整个模板:

代码语言:javascript
复制
<Style TargetType="{x:Type controls:Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type controls:Button}">
                <Border Name="Border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="Transparent"
                        BorderThickness="0">
                    <Border.CornerRadius>
                        <MultiBinding Converter="{StaticResource areaCornerRadiusConverter}">
                            <MultiBinding.Bindings>
                                <Binding Path="RoundType" RelativeSource="{RelativeSource TemplatedParent}" />
                                <Binding Path="ActualHeight" RelativeSource="{RelativeSource TemplatedParent}" />
                            </MultiBinding.Bindings>
                        </MultiBinding>
                    </Border.CornerRadius>
                    <TextBlock Margin="{Binding Path=RoundType,
                                                RelativeSource={RelativeSource TemplatedParent},
                                                Converter={StaticResource buttonMarginConverter}}"
                               FontSize="{TemplateBinding FontSize}"
                               Style="{StaticResource innerTextBlock}"
                               Text="{TemplateBinding Text}" />
                </Border>
                <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="PreviewMouseDown">
                        <SoundPlayerAction Source="{Binding Path=SoundFile, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource soundFileConverter}}" />
                    </EventTrigger>                        
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

编辑2:

我尝试了另一种方法:将PART_SoundPlayerAction的SoundPlayerAction属性设置为SoundPlayerAction,然后使用GetTemplateChild.从代码隐藏中检索它。但是GetTemplateChild总是返回null。真烦人。似乎什么都起不到作用..。

编辑3:

现在,在Blachshma的回答中,我得到了在控制无效期间调用转换器的消息。但当房产发生变化时就不会了。此外,转换器返回的值不作为源应用于SoundPlayerAction。

我实现了BindingProxy:

代码语言:javascript
复制
public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public SoundFile Data
    {
        get { return (SoundFile)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(SoundFile), typeof(BindingProxy), new UIPropertyMetadata(SoundFile.None));
}

我把Path=Data.SoundFile改成了Path=Data。有什么错误吗?

编辑4:

MakeSoundCommand的解决方案工作得很好。非常感谢布拉奇玛。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-12-22 00:45:54

嗯,您说得对,尝试在特定的位置使用绑定(样式中的EventTrigger )是很痛苦的。

我发现解决这个问题的最好方法是同时使用冷冻物 (继承DataContext)和静态BindingProxies.

为了在您的情况下解决这个问题,首先创建一个可冻结类,我们将其称为BindingProxy:

代码语言:javascript
复制
public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

因此,我们的BindingProxy类实现了Freezable并公开了一个名为Data的属性。这将是保存DataContext的属性。

现在,在我们的资源中,我们将创建一个使用这个类的StaticResource .我们将它命名为"proxy":

代码语言:javascript
复制
<Window.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />

注意,我们将窗口的DataContext绑定到Data属性。这将允许使用从SoundPlayerAction访问它。

最后,让我们更新SoundPlayerAction以使用我们的代理:

代码语言:javascript
复制
<EventTrigger RoutedEvent="PreviewMouseDown">
    <SoundPlayerAction Source="{Binding Source={StaticResource proxy}, Path=Data.SoundFile,Converter={StaticResource soundFileConverter}}" />
</EventTrigger>   

与您使用的常规绑定不同,我们要绑定到StaticResource ( BindingProxy类的实例)。由于Data属性绑定到窗口的DataContext,所以我们可以使用Data.SoundFile获得SoundFile属性。另外,由于所有这些都是通过Source={Binding完成的,所以仍然可以调用soundFileConverter将字符串转换为URI。

更新: OP不希望每次使用该控件(这是合法的)时将BindingProxy类放入某个<Window.Resources>标记中,因此在这个版本中,我们将把所有的逻辑放在ResourceDictionary中,并修改一些XAML以使其继续工作。

因此,在我们的资源字典中,我们将使用System.Windows.InteractivityMicrosoft.Expression.Interactions (都可以通过共混SDK免费获得)

代码语言:javascript
复制
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

因为在以前的版本中,我们可以指望绑定是通过Window.Resources预先形成的,所以这一次我们必须自己做.我们将修改原始模板以添加一个<i:Interaction.Triggers>,它将取代EventTrigger,但允许通过专用命令播放声音:

代码语言:javascript
复制
<local:MakeSoundCommand x:Key="soundCommand"/>
<Style  TargetType="{x:Type controls:Button}" >
  ....
  ....
   <i:Interaction.Triggers>
     <i:EventTrigger EventName="PreviewMouseDown">
        <local:EventToCommand Command="{StaticResource soundCommand}" CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SoundFile, Converter={StaticResource soundFileConverter}}" />
     </i:EventTrigger>
   </i:Interaction.Triggers>
   <TextBlock Margin="{Binding Path=RoundType,....

它就放在TextBlock之前,它所做的是处理PreviewMouseDown事件,并调用一个名为MakeSoundCommand类型的soundCommand的命令。

这是MakeSoundCommand的实现

代码语言:javascript
复制
public class MakeSoundCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        Uri uri = parameter as Uri;

        if (uri != null)
        {
            StreamResourceInfo sri = Application.GetResourceStream(uri);
            SoundPlayer simpleSound = new SoundPlayer(sri.Stream);
            simpleSound.Play();
        }
    }

其余代码保持不变。注意:所使用的EventToCommand来自MVVM轻型工具包,可以下载这里

最终结果:

Generic.xaml:

代码语言:javascript
复制
<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/expression/2010/interactivity"
                xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
                xmlns:local="<YOUR NAMESPACE>">

<local:MakeSoundCommand x:Key="soundCommand" />
<local:SoundFileToUriConverter:Key="soundFileConverter" />
<Style TargetType="{x:Type controls:Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType=" {x:Type controls:Button}">
                <Border Name="Border"
                    Background="{TemplateBinding Background}"
                    BorderBrush="Transparent"
                    BorderThickness="0">
                    <Border.CornerRadius>
                      ....
                    </Border.CornerRadius>
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="PreviewMouseDown">
                          <local:EventToCommand Command="{StaticResource soundCommand}" CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SoundFile, Converter={StaticResource soundFileConverter}}" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                    <TextBlock Margin="{Binding Path=RoundType,
                                            RelativeSource={RelativeSource TemplatedParent}}" .... />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13956447

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档