2015/09/22 xamlのサンプルをGridView(Header)RowPresenterを使用するものに変更、説明を追加。
System.Windows.Interactivity.TargetedTriggerAction をWPFで使用するメモです。
確認時のExpression Blend SDKのDLLのバージョン
- System.Windows.Interactivity.dll: Runtime Version=v4.0.30319, Version=4.5.0.0
- Microsoft.Expression.Interactions.dll: Runtime Version=v4.0.30319, Version=4.5.0.0
C#コードとxamlのサンプル
ScrollViewer with non scrolling area (fixed header) でvoteされている回答の内容を、TargetedTriggerActionで行うサンプルです。
なお、このサンプルではAssociatedObjectとTargetの両方を使用しています。これが一般的な使用方法なのかは分かりません。
C#コードでTargetedTriggerActionの派生クラスを定義します。今回の例では、AssociatedObject側のスクロール位置に合わせて、Target側のスクロール位置を変更しています。
using System.Windows.Controls; using System.Windows.Interactivity; namespace WpfTest { // AssociatedObjectの型は、TypeConstraintAttributeで指定 // 互換性のない型で使おうとすると、実行時のxamlのパース中にInalidOperationExceptionが発生した // Targetの型は、TargetedTriggerActionの型パラメーターで指定 // 互換性のない型をTargetに指定すると、Invokeが呼ばれる時にInvalidOperationExceptionが発生した [TypeConstraint(typeof(ScrollViewer))] public class ReflectHorizontalScrollAction : TargetedTriggerAction<ScrollViewer> { protected override void Invoke(object parameter) { // TargetNameが未指定の場合は、TargetプロパティはAssociatedObjectを返す // TargetNameに存在しない要素名を渡すと、Targetプロパティはnullを返した(ドキュメントと異なる) if (this.Target == null || this.Target == this.AssociatedObject) { return; } // AssociatedObjectはDependencyObject型なので、必要ならキャスト var source = (ScrollViewer)this.AssociatedObject; // Targetは型パラメーターで指定した型Tなので、キャストは不要 this.Target.Width = source.ViewportWidth; this.Target.ScrollToHorizontalOffset(source.HorizontalOffset); } } }
TriggerAction Class の場合は、型引数はAssociatedObjectの型を指定していました。TargetedTriggerActionの場合は、型引数はTargetの型を指定しており、AssociatedObjectの型制約は TypeConstraintAttribute Class で指定することに注意してください。
また、コメントに振っていますが、Targetプロパティがnullを返す場合があります。
If TargetName is not set or cannot be resolved, defaults to the AssociatedObject.
TargetedTriggerAction.Target Property
とあるので、TargetNameに存在しない要素名を渡した場合でもAssociatedObjectが返るべきですが、実際はnullが返ります。
xamlです。ScrollViewerを上下に2つ配置しています。
TargetedTriggerActionは TriggerAction なので、TriggerBase の子要素として記述する必要があります。今回は下のScrollViewerがスクロールするときに自作Actionを実行したいので、 EventTrigger の子要素にしています。
自作ActionのTargetNameに上のScrollViewerを指定しているのがポイントです。
<Window x:Class="WpfTest.MainWindow" 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:sys="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:WpfTest" Title="MainWindow" Width="500" Height="120"> <Window.Resources> <GridViewColumnCollection x:Key="gvcc"> <GridViewColumn Header="Year" DisplayMemberBinding="{Binding Year}" Width="200" /> <GridViewColumn Header="Month" DisplayMemberBinding="{Binding Month}" Width="200" /> <GridViewColumn Header="Day" DisplayMemberBinding="{Binding Day}" Width="200" /> </GridViewColumnCollection> <x:Array x:Key="Dates" Type="{x:Type sys:DateTime}"> <sys:DateTime>0001/1/1</sys:DateTime> <sys:DateTime>2015/9/21</sys:DateTime> <sys:DateTime>2015/9/22</sys:DateTime> <sys:DateTime>9999/12/31</sys:DateTime> </x:Array> <DataTemplate x:Key="ItemTemplate"> <GridViewRowPresenter Columns="{StaticResource gvcc}" /> </DataTemplate> </Window.Resources> <DockPanel> <ScrollViewer x:Name="ScrollViewerHeader" DockPanel.Dock="Top" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" HorizontalAlignment="Left"> <GridViewHeaderRowPresenter Columns="{StaticResource gvcc}" /> </ScrollViewer> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ListBox ItemsSource="{StaticResource Dates}" ItemTemplate="{StaticResource ItemTemplate}" /> <!--このScrollViewerが横スクロールした時に、Header部分のScrollViewrも連動してスクロールさせる--> <i:Interaction.Triggers> <i:EventTrigger EventName="ScrollChanged"> <local:ReflectHorizontalScrollAction TargetName="ScrollViewerHeader" /> </i:EventTrigger> </i:Interaction.Triggers> </ScrollViewer> </DockPanel> </Window>
下のScrollViewerの横スクロール位置を変更すると、上のScrollViewerも連動してスクロールすることを確認できます。