我在EventTrigger中有以下绑定:
<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')
绑定表达式有什么问题?
编辑:
为了进行更好的概述,下面是控件的整个模板:
<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:
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的解决方案工作得很好。非常感谢布拉奇玛。
发布于 2012-12-22 00:45:54
嗯,您说得对,尝试在特定的位置使用绑定(样式中的EventTrigger )是很痛苦的。
我发现解决这个问题的最好方法是同时使用冷冻物 (继承DataContext)和静态BindingProxies.
为了在您的情况下解决这个问题,首先创建一个可冻结类,我们将其称为BindingProxy:
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":
<Window.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />注意,我们将窗口的DataContext绑定到Data属性。这将允许使用从SoundPlayerAction访问它。
最后,让我们更新SoundPlayerAction以使用我们的代理:
<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.Interactivity和Microsoft.Expression.Interactions (都可以通过共混SDK免费获得)
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"因为在以前的版本中,我们可以指望绑定是通过Window.Resources预先形成的,所以这一次我们必须自己做.我们将修改原始模板以添加一个<i:Interaction.Triggers>,它将取代EventTrigger,但允许通过专用命令播放声音:
<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的实现
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:
<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>https://stackoverflow.com/questions/13956447
复制相似问题