Microsoft Expression Blend 4を使用しています
ブラウザを持っています..、
[XAML]ConnectionView "空のコードビハインド"
<WebBrowser local:AttachedProperties.BrowserSource="{Binding Source}">
<i:Interaction.Triggers>
<i:EventTrigger>
<i:InvokeCommandAction Command="{Binding LoadedEvent}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Navigated">
<i:InvokeCommandAction Command="{Binding NavigatedEvent}" CommandParameter="??????"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</WebBrowser>
[C#]AttachedPropertiesクラス
public static class AttachedProperties
{
public static readonly DependencyProperty BrowserSourceProperty = DependencyProperty . RegisterAttached ( "BrowserSource" , typeof ( string ) , typeof ( AttachedProperties ) , new UIPropertyMetadata ( null , BrowserSourcePropertyChanged ) );
public static string GetBrowserSource ( DependencyObject _DependencyObject )
{
return ( string ) _DependencyObject . GetValue ( BrowserSourceProperty );
}
public static void SetBrowserSource ( DependencyObject _DependencyObject , string Value )
{
_DependencyObject . SetValue ( BrowserSourceProperty , Value );
}
public static void BrowserSourcePropertyChanged ( DependencyObject _DependencyObject , DependencyPropertyChangedEventArgs _DependencyPropertyChangedEventArgs )
{
WebBrowser _WebBrowser = _DependencyObject as WebBrowser;
if ( _WebBrowser != null )
{
string URL = _DependencyPropertyChangedEventArgs . NewValue as string;
_WebBrowser . Source = URL != null ? new Uri ( URL ) : null;
}
}
}
[C#]ConnectionViewModelクラス
public class ConnectionViewModel : ViewModelBase
{
public string Source
{
get { return Get<string> ( "Source" ); }
set { Set ( "Source" , value ); }
}
public void Execute_ExitCommand ( )
{
Application . Current . Shutdown ( );
}
public void Execute_LoadedEvent ( )
{
MessageBox . Show ( "___Execute_LoadedEvent___" );
Source = ...... ;
}
public void Execute_NavigatedEvent ( )
{
MessageBox . Show ( "___Execute_NavigatedEvent___" );
}
}
[C#]ViewModelBaseクラスここ
最後に :
コマンドとのバインドはうまく機能し、メッセージボックスが表示されます
私の質問:
ナビゲートされたイベントが発生したときにコマンドパラメーターとしてNavigationEventArgsを渡す方法
簡単にサポートされていません。 記事 にEventArgsをコマンドパラメーターとして渡す方法の説明があります。
MVVMLight を使用して調べることもできます。コマンドでEventArgsを直接サポートします。あなたの状況は次のようになります。
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding NavigatedEvent}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
依存関係を最小限に抑えようとするため、MVVMLightのEventToCommandを使用する代わりに、これを自分で実装しました。これまでのところうまくいきますが、フィードバックは大歓迎です。
Xaml:
<i:Interaction.Behaviors>
<beh:EventToCommandBehavior Command="{Binding DropCommand}" Event="Drop" PassArguments="True" />
</i:Interaction.Behaviors>
ViewModel:
public ActionCommand<DragEventArgs> DropCommand { get; private set; }
this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);
private void OnDrop(DragEventArgs e)
{
// ...
}
EventToCommandBehavior:
/// <summary>
/// Behavior that will connect an UI event to a viewmodel Command,
/// allowing the event arguments to be passed as the CommandParameter.
/// </summary>
public class EventToCommandBehavior : Behavior<FrameworkElement>
{
private Delegate _handler;
private EventInfo _oldEvent;
// Event
public string Event { get { return (string)GetValue(EventProperty); } set { SetValue(EventProperty, value); } }
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));
// Command
public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } }
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));
// PassArguments (default: false)
public bool PassArguments { get { return (bool)GetValue(PassArgumentsProperty); } set { SetValue(PassArgumentsProperty, value); } }
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var beh = (EventToCommandBehavior)d;
if (beh.AssociatedObject != null) // is not yet attached at initial load
beh.AttachHandler((string)e.NewValue);
}
protected override void OnAttached()
{
AttachHandler(this.Event); // initial set
}
/// <summary>
/// Attaches the handler to the event
/// </summary>
private void AttachHandler(string eventName)
{
// detach old event
if (_oldEvent != null)
_oldEvent.RemoveEventHandler(this.AssociatedObject, _handler);
// attach new event
if (!string.IsNullOrEmpty(eventName))
{
EventInfo ei = this.AssociatedObject.GetType().GetEvent(eventName);
if (ei != null)
{
MethodInfo mi = this.GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
_handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
ei.AddEventHandler(this.AssociatedObject, _handler);
_oldEvent = ei; // store to detach in case the Event property changes
}
else
throw new ArgumentException(string.Format("The event '{0}' was not found on type '{1}'", eventName, this.AssociatedObject.GetType().Name));
}
}
/// <summary>
/// Executes the Command
/// </summary>
private void ExecuteCommand(object sender, EventArgs e)
{
object parameter = this.PassArguments ? e : null;
if (this.Command != null)
{
if (this.Command.CanExecute(parameter))
this.Command.Execute(parameter);
}
}
}
ActionCommand:
public class ActionCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<T> _action;
public ActionCommand(Action<T> action)
{
_action = action;
}
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter)
{
if (_action != null)
{
var castParameter = (T)Convert.ChangeType(parameter, typeof(T));
_action(castParameter);
}
}
}
私はいつも答えを求めてここに戻ってきたので、簡単な短いものを作りたいと思いました。
これを行うには複数の方法があります。
名前空間を追加します。
System.Windows.Interactivitiy
Microsoft.Expression.Interactions
XAML:
EventName
を使用して目的のイベントを呼び出し、Method
にMethodName
名を指定します。
<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.Microsoft.com/expression/2010/interactions">
<wi:Interaction.Triggers>
<wi:EventTrigger EventName="SelectionChanged">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="ShowCustomer"/>
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>
コード:
public void ShowCustomer()
{
// Do something.
}
GalaSoft NuGetパッケージをインストールします。
名前空間を取得します。
System.Windows.Interactivity
GalaSoft.MvvmLight.Platform
XAML:
EventName
を使用して目的のイベントを呼び出し、Command
名をバインディングに指定します。メソッドの引数を渡したい場合は、PassEventArgsToCommand
をtrueにマークします。
<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight">
<wi:Interaction.Triggers>
<wi:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding CommandNameHere}"
PassEventArgsToCommand="True" />
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>
デリゲートを実装するコード: Source
このためには、Prism MVVM NuGetパッケージを入手する必要があります。
using Microsoft.Practices.Prism.Commands;
// With params.
public DelegateCommand<string> CommandOne { get; set; }
// Without params.
public DelegateCommand CommandTwo { get; set; }
public MainWindow()
{
InitializeComponent();
// Must initialize the DelegateCommands here.
CommandOne = new DelegateCommand<string>(executeCommandOne);
CommandTwo = new DelegateCommand(executeCommandTwo);
}
private void executeCommandOne(string param)
{
// Do something here.
}
private void executeCommandTwo()
{
// Do something here.
}
DelegateCommand
なしのコード: ソース
using GalaSoft.MvvmLight.CommandWpf
public MainWindow()
{
InitializeComponent();
CommandOne = new RelayCommand<string>(executeCommandOne);
CommandTwo = new RelayCommand(executeCommandTwo);
}
public RelayCommand<string> CommandOne { get; set; }
public RelayCommand CommandTwo { get; set; }
private void executeCommandOne(string param)
{
// Do something here.
}
private void executeCommandTwo()
{
// Do something here.
}
NuGetパッケージ をダウンロードする必要があります。
XAML
:
<i:Interaction.Behaviors>
<telerek:EventToCommandBehavior
Command="{Binding DropCommand}"
Event="Drop"
PassArguments="True" />
</i:Interaction.Behaviors>
コード:
public ActionCommand<DragEventArgs> DropCommand { get; private set; }
this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);
private void OnDrop(DragEventArgs e)
{
// Do Something
}
これはかなり古い質問であることはわかっていますが、今日同じ問題にぶつかり、MVVMLightのすべてを参照することにあまり興味がなかったので、イベント引数でイベントトリガーを使用できます。過去にMVVMLightを使用しましたが、これはすばらしいフレームワークですが、プロジェクトに使用したくないだけです。
この問題を解決するために行ったのは、[〜#〜] ultra [〜#〜]最小限、[〜#〜] extremely [〜#〜]適応可能なカスタムを作成することでした。コマンドにバインドし、コマンドのCanExecuteおよびExecute関数に引数を渡すイベント引数コンバーターを提供できるトリガーアクション。イベントargsを逐語的に渡すことは望ましくありません。これにより、ビューレイヤータイプがビューモデルレイヤーに送信されることになります(MVVMでは発生しません)。
ここに私が思いついたEventCommandExecuterクラスがあります:
public class EventCommandExecuter : TriggerAction<DependencyObject>
{
#region Constructors
public EventCommandExecuter()
: this(CultureInfo.CurrentCulture)
{
}
public EventCommandExecuter(CultureInfo culture)
{
Culture = culture;
}
#endregion
#region Properties
#region Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
#region EventArgsConverterParameter
public object EventArgsConverterParameter
{
get { return (object)GetValue(EventArgsConverterParameterProperty); }
set { SetValue(EventArgsConverterParameterProperty, value); }
}
public static readonly DependencyProperty EventArgsConverterParameterProperty =
DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
public IValueConverter EventArgsConverter { get; set; }
public CultureInfo Culture { get; set; }
#endregion
protected override void Invoke(object parameter)
{
var cmd = Command;
if (cmd != null)
{
var param = parameter;
if (EventArgsConverter != null)
{
param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
}
if (cmd.CanExecute(param))
{
cmd.Execute(param);
}
}
}
}
このクラスには2つの依存関係プロパティがあり、1つはビューモデルのコマンドへのバインドを許可し、もう1つはイベント引数の変換中に必要な場合にイベントのソースをバインドできます。必要に応じて、カルチャ設定を提供することもできます(デフォルトでは現在のUIカルチャになります)。
このクラスを使用すると、ビューモデルのコマンドロジックで消費されるようにイベント引数を調整できます。ただし、イベント引数をそのまま渡す場合は、イベント引数コンバータを指定しないでください。
XAMLでのこのトリガーアクションの最も簡単な使用法は次のとおりです。
<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<cmd:EventCommandExecuter Command="{Binding Path=Update, Mode=OneTime}" EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
イベントのソースへのアクセスが必要な場合は、イベントの所有者にバインドします
<i:Interaction.Triggers>
<i:EventTrigger EventName="NameChanged">
<cmd:EventCommandExecuter
Command="{Binding Path=Update, Mode=OneTime}"
EventArgsConverter="{x:Static c:NameChangedArgsToStringConverter.Default}"
EventArgsConverterParameter="{Binding ElementName=SomeEventSource, Mode=OneTime}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
(これは、トリガーをアタッチするXAMLノードにx:Name="SomeEventSource"
が割り当てられていることを前提としています
このXAMLは、いくつかの必要な名前空間のインポートに依存しています
xmlns:cmd="clr-namespace:MyProject.WPF.Commands"
xmlns:c="clr-namespace:MyProject.WPF.Converters"
xmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
IValueConverter
(この場合はNameChangedArgsToStringConverter
と呼ばれる)を作成して、実際の変換ロジックを処理します。基本的なコンバーターの場合、通常はデフォルトのstatic readonly
コンバーターインスタンスを作成します。その後、上記のようにXAMLで直接参照できます。
このソリューションの利点は、プロジェクトに単一のクラスを追加するだけで、InvokeCommandAction
で使用するのとほぼ同じ方法で対話フレームワークを使用できることです。単一のクラス(約75行)を追加することは、同じ結果を達成するためにライブラリ全体よりもはるかに望ましいはずです。
これは@adabyronからの回答に多少似ていますが、動作の代わりにイベントトリガーを使用します。このソリューションは、イベント引数の変換機能も提供しますが、@ adabyronのソリューションもこれを実行できませんでした。私は本当にトリガーを行動よりも好む理由はありません。個人的な選択です。 IMOのどちらの戦略も合理的な選択です。
この投稿を見つけたばかりの人のために、新しいバージョン(このトピックでは公式ドキュメントがスリムであるため正確なバージョンは不明)では、CommandParameterが指定されていない場合、InvokeCommandActionのデフォルトの動作はCommandParameterとしてアタッチされるイベント。したがって、オリジナルのポスターのXAMLは次のように簡単に記述できます。
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<i:InvokeCommandAction Command="{Binding NavigatedEvent}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
その後、コマンドで、タイプNavigationEventArgs
(または適切なイベント引数タイプ)のパラメーターを受け入れると、自動的に提供されます。
Joshbがすでに述べたことに追加する-これは私にとってはうまく機能します。必ずMicrosoft.Expression.Interactions.dllおよびSystem.Windows.Interactivity.dllへの参照を追加し、xamlで以下を実行してください。
xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
私は自分のニーズのためにこのようなものを使用することになりました。これは、カスタムパラメータを渡すこともできることを示しています。
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding Path=DataContext.RowSelectedItem, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
InvokeCommandAction
で簡単にできるとは思わない-MVVMLightなどから EventToCommand
を見てみよう。
Blend for Visual Studio 2013の動作とアクションでは、InvokeCommandActionを使用できます。 Dropイベントでこれを試しましたが、XAMLでCommandParameterが指定されていませんでしたが、驚いたことに、Execute ActionパラメーターにはDragEventArgsが含まれていました。これは他のイベントでも起こると思いますが、テストしていません。
InvokeCommandAction
が設定されていない場合、PrismのCommandParameter
はデフォルトでイベント引数を渡します。
以下に例を示します。 prism:InvokeCommandAction
の代わりにi:InvokeCommandAction
を使用していることに注意してください。
<i:Interaction.Triggers>
<i:EventTrigger EventName="Sorting">
<prism:InvokeCommandAction Command="{Binding SortingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
これは、漏れやすいEventArgs
抽象化を防ぐ@adabyronの回答のバージョンです。
まず、変更されたEventToCommandBehavior
クラス(現在は汎用抽象クラスであり、ReSharperコードクリーンアップでフォーマットされています)。新しいGetCommandParameter
仮想メソッドとそのデフォルトの実装に注意してください。
public abstract class EventToCommandBehavior<TEventArgs> : Behavior<FrameworkElement>
where TEventArgs : EventArgs
{
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null, OnEventChanged));
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(null));
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior<TEventArgs>), new PropertyMetadata(false));
private Delegate _handler;
private EventInfo _oldEvent;
public string Event
{
get { return (string)GetValue(EventProperty); }
set { SetValue(EventProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public bool PassArguments
{
get { return (bool)GetValue(PassArgumentsProperty); }
set { SetValue(PassArgumentsProperty, value); }
}
protected override void OnAttached()
{
AttachHandler(Event);
}
protected virtual object GetCommandParameter(TEventArgs e)
{
return e;
}
private void AttachHandler(string eventName)
{
_oldEvent?.RemoveEventHandler(AssociatedObject, _handler);
if (string.IsNullOrEmpty(eventName))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetEvent(eventName);
if (eventInfo != null)
{
MethodInfo methodInfo = typeof(EventToCommandBehavior<TEventArgs>).GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
_handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo);
eventInfo.AddEventHandler(AssociatedObject, _handler);
_oldEvent = eventInfo;
}
else
{
throw new ArgumentException($"The event '{eventName}' was not found on type '{AssociatedObject.GetType().FullName}'.");
}
}
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (EventToCommandBehavior<TEventArgs>)d;
if (behavior.AssociatedObject != null)
{
behavior.AttachHandler((string)e.NewValue);
}
}
// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
private void ExecuteCommand(object sender, TEventArgs e)
{
object parameter = PassArguments ? GetCommandParameter(e) : null;
if (Command?.CanExecute(parameter) == true)
{
Command.Execute(parameter);
}
}
}
次に、DragCompletedEventArgs
を隠す派生クラスの例。一部の人々は、ビューモデルアセンブリにEventArgs
抽象化が漏れることに懸念を表明しました。これを防ぐために、重要な値を表すインターフェイスを作成しました。インターフェイスは、UIアセンブリのプライベート実装を使用して、ビューモデルアセンブリ内に配置できます。
// UI Assembly
public class DragCompletedBehavior : EventToCommandBehavior<DragCompletedEventArgs>
{
protected override object GetCommandParameter(DragCompletedEventArgs e)
{
return new DragCompletedArgs(e);
}
private class DragCompletedArgs : IDragCompletedArgs
{
public DragCompletedArgs(DragCompletedEventArgs e)
{
Canceled = e.Canceled;
HorizontalChange = e.HorizontalChange;
VerticalChange = e.VerticalChange;
}
public bool Canceled { get; }
public double HorizontalChange { get; }
public double VerticalChange { get; }
}
}
// View model Assembly
public interface IDragCompletedArgs
{
bool Canceled { get; }
double HorizontalChange { get; }
double VerticalChange { get; }
}
@adabyronの答えと同様に、コマンドパラメーターをIDragCompletedArgs
にキャストします。
私がすることは、InvokeCommandActionを使用してコントロールのロードされたイベントをビューモデルのコマンドにバインドし、Xamlのコントロールax:Nameを与えてCommandParameterとして渡し、次にロードされたコマンドフックビューモデルハンドラーで必要なイベントまでイベント引数を取得します。
@Mike Fuchsの回答を参考に、さらに小さなソリューションを紹介します。ボイラープレートの一部を減らすためにFody.AutoDependencyPropertyMarker
を使用しています。
クラス
public class EventCommand : TriggerAction<DependencyObject>
{
[AutoDependencyProperty]
public ICommand Command { get; set; }
protected override void Invoke(object parameter)
{
if (Command != null)
{
if (Command.CanExecute(parameter))
{
Command.Execute(parameter);
}
}
}
}
EventArgs
public class VisibleBoundsArgs : EventArgs
{
public Rect VisibleVounds { get; }
public VisibleBoundsArgs(Rect visibleBounds)
{
VisibleVounds = visibleBounds;
}
}
XAML
<local:ZoomableImage>
<i:Interaction.Triggers>
<i:EventTrigger EventName="VisibleBoundsChanged" >
<local:EventCommand Command="{Binding VisibleBoundsChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:ZoomableImage>
ViewModel
public ICommand VisibleBoundsChanged => _visibleBoundsChanged ??
(_visibleBoundsChanged = new RelayCommand(obj => SetVisibleBounds(((VisibleBoundsArgs)obj).VisibleVounds)));