Caliburn.Micro 在行为事件上附加事件处理程序
Caliburn.Micro attach event handler on event of Behavior
我编写了自己的 Behavior 来处理滑动手势并将其放入 ListView 的 ItemTemplate。如果滑动完成,我会为 LeftSwipe 或 RightSwipe 引发事件。此事件应由我的 ViewModel 处理。
我使用 Caliburn.Micro 的语法将处理程序附加到事件:cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)"
。
这是我的行为:
public class SwipeInteractionBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
// ...
}
public void Detach()
{
// ...
}
public event EventHandler LeftSwipe;
public event EventHandler RightSwipe;
// ...
// ...
private void OnLeftSwipe(FrameworkElement element)
{
// ...
if (LeftSwipe != null)
{
LeftSwipe(this, EventArgs.Empty);
}
}
private void OnRightSwipe(FrameworkElement element)
{
// ...
if (RightSwipe != null)
{
RightSwipe(this, EventArgs.Empty);
}
}
}
这就是我在 ListViews ItemTemplate 中使用此行为的方式:
<ListView x:Name="Links" IsItemClickEnabled="True" SelectionMode="None" cm:Message.Attach="[Event ItemClick] = [Click($source, $eventArgs)]">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid>
<Border x:Name="todoItem" Loaded="Border_Loaded" Background="White">
<i:Interaction.Behaviors>
<local:SwipeInteractionBehavior cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)]" />
</i:Interaction.Behaviors>
<Grid>
<StackPanel>
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" />
<TextBlock Text="{Binding Url}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource ListToString}}" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
如果我引发 LeftSwipe 事件,我会 运行 出现异常,这是我的 StackTrace:
System.Exception was not handled.
HResult=-2146233088
Message=No target found for method LeftSwipe.
Source=Caliburn.Micro.Platform
StackTrace:
at Caliburn.Micro.ActionMessage.Invoke(Object eventArgs)
at Caliburn.Micro.TriggerAction`1.Execute(Object sender, Object parameter)
at Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender, ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTriggerBehavior.OnEvent(Object sender, Object eventArgs)
at ReaderApp.SwipeInteractionBehavior.<>c__DisplayClass5.<OnLeftSwipe>b__4()
at ReaderApp.Extensions.FrameworkElementExtension.<>c__DisplayClass2.<Animate>b__0(Object s, Object e)
InnerException:
视图模型:
public class MainViewModel : ViewModelBase
{
private readonly IEventAggregator eventAggregator;
private readonly Database database;
public BindableCollection<Link> Links
{
get;
private set;
}
public MainViewModel(INavigationService navigationService, IEventAggregator eventAggregator, Database database)
: base(navigationService)
{
this.eventAggregator = eventAggregator;
this.database = database;
Links = new BindableCollection<Link>();
}
public async void LeftSwipe(object sender, EventArgs e)
{
// ...
}
public void RightSwipe(object sender, EventArgs e)
{
// ...
}
}
所以问题是 ActionMessage
继承了 TriggerAction<FrameworkElement>
这意味着它不能正确附加到 SwipeInteractionBehavior
.
WPF / Silverlight / Windows Phone 8 Interactivity SDK 和 WinRT Interactivity SDK 之间存在一些主要 API 差异,这也让问题变得复杂。您可以在 Parser 评论中看到我的意思。
我建议将 SwipeInteractionBehavior
作为 触发行为 实现,这与 EventTriggerBehavior
非常相似,这曾经是一个单独的基础 class 但对于 WinRT,它仍然是 IBehavior
,区别在于它有一个 属性 类型为 ActionCollection
的 Actions。愚蠢的是,没有接口是基础 class 强制执行此操作,因此 Caliburn.Micro 无法做出一些假设。
然后您将使用 Interaction.ExecuteActions
来触发这些操作,最后您的 xaml 应该看起来像这样。
<Border x:Name="SwipeTarget">
<i:Interaction.Behaviors>
<local:SwipeEventBehavior Direction="Left">
<cm:ActionMessage AssociatedObject="{Binding ElementName=SwipeTarget" Method="LeftSwipe" />
</local:SwipeEventBehavior>
</i:Interaction.Behaviors>
</Border>
有点啰嗦,但我们需要解决 SDK 更改带来的限制。
我编写了自己的 Behavior 来处理滑动手势并将其放入 ListView 的 ItemTemplate。如果滑动完成,我会为 LeftSwipe 或 RightSwipe 引发事件。此事件应由我的 ViewModel 处理。
我使用 Caliburn.Micro 的语法将处理程序附加到事件:cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)"
。
这是我的行为:
public class SwipeInteractionBehavior : DependencyObject, IBehavior
{
public DependencyObject AssociatedObject { get; private set; }
public void Attach(DependencyObject associatedObject)
{
// ...
}
public void Detach()
{
// ...
}
public event EventHandler LeftSwipe;
public event EventHandler RightSwipe;
// ...
// ...
private void OnLeftSwipe(FrameworkElement element)
{
// ...
if (LeftSwipe != null)
{
LeftSwipe(this, EventArgs.Empty);
}
}
private void OnRightSwipe(FrameworkElement element)
{
// ...
if (RightSwipe != null)
{
RightSwipe(this, EventArgs.Empty);
}
}
}
这就是我在 ListViews ItemTemplate 中使用此行为的方式:
<ListView x:Name="Links" IsItemClickEnabled="True" SelectionMode="None" cm:Message.Attach="[Event ItemClick] = [Click($source, $eventArgs)]">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid>
<Border x:Name="todoItem" Loaded="Border_Loaded" Background="White">
<i:Interaction.Behaviors>
<local:SwipeInteractionBehavior cm:Message.Attach="[Event LeftSwipe] = [LeftSwipe($source, $eventArgs)]" />
</i:Interaction.Behaviors>
<Grid>
<StackPanel>
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" />
<TextBlock Text="{Binding Url}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
<TextBlock Text="{Binding Tags, Converter={StaticResource ListToString}}" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
如果我引发 LeftSwipe 事件,我会 运行 出现异常,这是我的 StackTrace:
System.Exception was not handled.
HResult=-2146233088
Message=No target found for method LeftSwipe.
Source=Caliburn.Micro.Platform
StackTrace:
at Caliburn.Micro.ActionMessage.Invoke(Object eventArgs)
at Caliburn.Micro.TriggerAction`1.Execute(Object sender, Object parameter)
at Microsoft.Xaml.Interactivity.Interaction.ExecuteActions(Object sender, ActionCollection actions, Object parameter)
at Microsoft.Xaml.Interactions.Core.EventTriggerBehavior.OnEvent(Object sender, Object eventArgs)
at ReaderApp.SwipeInteractionBehavior.<>c__DisplayClass5.<OnLeftSwipe>b__4()
at ReaderApp.Extensions.FrameworkElementExtension.<>c__DisplayClass2.<Animate>b__0(Object s, Object e)
InnerException:
视图模型:
public class MainViewModel : ViewModelBase
{
private readonly IEventAggregator eventAggregator;
private readonly Database database;
public BindableCollection<Link> Links
{
get;
private set;
}
public MainViewModel(INavigationService navigationService, IEventAggregator eventAggregator, Database database)
: base(navigationService)
{
this.eventAggregator = eventAggregator;
this.database = database;
Links = new BindableCollection<Link>();
}
public async void LeftSwipe(object sender, EventArgs e)
{
// ...
}
public void RightSwipe(object sender, EventArgs e)
{
// ...
}
}
所以问题是 ActionMessage
继承了 TriggerAction<FrameworkElement>
这意味着它不能正确附加到 SwipeInteractionBehavior
.
WPF / Silverlight / Windows Phone 8 Interactivity SDK 和 WinRT Interactivity SDK 之间存在一些主要 API 差异,这也让问题变得复杂。您可以在 Parser 评论中看到我的意思。
我建议将 SwipeInteractionBehavior
作为 触发行为 实现,这与 EventTriggerBehavior
非常相似,这曾经是一个单独的基础 class 但对于 WinRT,它仍然是 IBehavior
,区别在于它有一个 属性 类型为 ActionCollection
的 Actions。愚蠢的是,没有接口是基础 class 强制执行此操作,因此 Caliburn.Micro 无法做出一些假设。
然后您将使用 Interaction.ExecuteActions
来触发这些操作,最后您的 xaml 应该看起来像这样。
<Border x:Name="SwipeTarget">
<i:Interaction.Behaviors>
<local:SwipeEventBehavior Direction="Left">
<cm:ActionMessage AssociatedObject="{Binding ElementName=SwipeTarget" Method="LeftSwipe" />
</local:SwipeEventBehavior>
</i:Interaction.Behaviors>
</Border>
有点啰嗦,但我们需要解决 SDK 更改带来的限制。