gpt4 book ai didi

c# - EventTrigger 中的模板绑定(bind)

转载 作者:太空宇宙 更新时间:2023-11-03 11:11:23 32 4
gpt4 key购买 nike

我在 EventTrigger 中有以下绑定(bind):

<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="PreviewMouseDown">
<SoundPlayerAction Source="{Binding Path=SoundFile, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource soundFileConverter}}" />
</EventTrigger>
...

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

这就是问题所在:无论如何都不会调用转换器。输出窗口出现以下错误:

Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=SoundFile; DataItem=null; target element is 'SoundPlayerAction' HashCode=46763000); target property is 'Source' (type 'Uri')

绑定(bind)表达式有什么问题?

编辑:

为了更好地了解,这里是控件的整个模板:

<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:

我尝试了另一种方法:将 SoundPlayerAction 的名称属性设置为 PART_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 的解决方案运行良好。非常感谢 Blachshma。

最佳答案

嗯,你是对的,尝试在特定位置(样式中的 EventTrigger)使用绑定(bind)是一件很痛苦的事情。

我发现解决此问题的最佳方法是同时使用 Freezables (继承 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}" />

注意,我们将 Window 的 DataContext 绑定(bind)到 Data属性(property)。这将允许使用从 SoundPlayerAction 访问它.

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

<EventTrigger RoutedEvent="PreviewMouseDown">
<SoundPlayerAction Source="{Binding Source={StaticResource proxy}, Path=Data.SoundFile,Converter={StaticResource soundFileConverter}}" />
</EventTrigger>

我们绑定(bind)的不是您使用的常规绑定(bind),而是绑定(bind)到 StaticResource,即我们的 BindingProxy 类的实例。自 Data属性绑定(bind)到窗口的 DataContext,我们可以使用 Data.SoundFile 获取 SoundFile 属性.作为额外的奖励,因为所有这些都是通过 Source={Binding 完成的您仍然可以调用 soundFileConverter 将字符串转换为 URI。

更新:OP不想放BindingProxy类里面一些<Window.Resources>每次他使用这个控件(这是合法的)时都标记,所以在这个版本中,我们将把所有逻辑放在 ResourceDictionary 中并修改一些 XAML 以便它继续工作..

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

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

因为在以前的版本中我们可以指望通过 Window.Resources 执行绑定(bind),这次我们将不得不自己做...我们将修改原始模板以添加一个 <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事件并调用名为 soundCommand 的命令类型 MakeSoundCommand .

这是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 Light Toolkit 的那个并可下载here

最终结果:

通用.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>

关于c# - EventTrigger 中的模板绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13956447/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com