我正在使用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类 Here
最后:
绑定命令效果很好,MessageBoxes显示
我的问题:
如何在导航事件发生时将 NavigationEventArgs 作为命令参数传递?
答案 0 :(得分:60)
不容易支持。这里是an article,其中包含有关如何将EventArgs作为命令参数传递的说明。
您可能希望查看使用MVVMLight - 它直接支持命令中的EventArgs;你的情况看起来像这样:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding NavigatedEvent}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
答案 1 :(得分:38)
我尽量将依赖关系保持在最低限度,所以我自己实现了这个,而不是使用MVVMLight的EventToCommand。到目前为止对我有用,但欢迎提供反馈。
的Xaml:
<i:Interaction.Behaviors>
<beh: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)
{
// ...
}
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);
}
}
}
答案 2 :(得分:19)
我总是回到这里寻找答案,所以我想做一个简单的简单的去。
有多种方法可以做到这一点:
添加命名空间:
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
的代码:Source
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 Package。
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
}
答案 3 :(得分:12)
我知道这是一个相当古老的问题,但我今天遇到了同样的问题并且对引用所有MVVMLight并不太感兴趣,所以我可以使用带有事件参数的事件触发器。我过去使用过MVVMLight,它是一个很棒的框架,但我不想再将它用于我的项目了。
我为解决此问题所做的是创建 ULTRA 最小,极其自适应自定义触发器操作,该操作允许我绑定到命令并提供事件参数转换器将args传递给命令的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);
}
}
}
}
此类有两个依赖项属性,一个允许绑定到视图模型的命令,另一个允许您在事件args转换期间需要时绑定事件源。如果需要,您还可以提供文化设置(它们默认为当前的UI文化)。
此类允许您调整事件参数,以便视图模型的命令逻辑可以使用它们。但是,如果您只想逐字传递事件args,则不要指定事件args转换器。
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的答案,但它使用事件触发器而不是行为。此解决方案还提供了事件args转换功能,而不是@adabyron的解决方案也不能这样做。我真的没有任何理由为什么我更喜欢触发行为,只是个人选择。 IMO的任何一种策略都是合理的选择。
答案 4 :(得分:5)
添加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>
答案 5 :(得分:3)
我认为你不能轻易地使用InvokeCommandAction
- 我会看一下MVVMLight中的EventToCommand
或类似内容。
答案 6 :(得分:1)
使用Blend for Visual Studio 2013中的“行为和操作”,您可以使用InvokeCommandAction。我尝试使用Drop事件,尽管在XAML中没有指定CommandParameter,但令我惊讶的是,Execute Action参数包含DragEventArgs。我认为这会发生在其他事件中,但尚未对它们进行测试。
答案 7 :(得分:0)
我所做的是使用InvokeCommandAction将控件加载的事件绑定到视图模型中的命令,给出控件ax:Xaml中的Name并传递为CommandParameter,然后在所述加载的命令钩子视图模型处理程序中直到事件在哪里我需要得到事件args。
答案 8 :(得分:0)
以下是@ adabyron的答案版本,可以防止漏洞EventArgs
抽象。
首先,修改后的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; }
}
将命令参数转换为IDragCompletedArgs
,类似于@ adabyron的答案。
答案 9 :(得分:0)
作为@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)));
答案 10 :(得分:0)
如果未设置InvokeCommandAction
,则默认情况下,Prism的CommandParameter
将传递事件args。
这里是一个例子。请注意,使用prism:InvokeCommandAction
代替i:InvokeCommandAction
。
<i:Interaction.Triggers>
<i:EventTrigger EventName="Sorting">
<prism:InvokeCommandAction Command="{Binding SortingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
答案 11 :(得分:0)
我知道现在有点晚了,但是,微软已经将其Xaml.Behaviors开源了,现在只需要一个名称空间就可以轻松使用交互性。
然后像这样使用它,
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim row = DataGridView1.CurrentCell.RowIndex
Dim col = DataGridView1.CurrentCell.ColumnIndex
GetDataFillGrid()
If DataGridView1.Rows.Count > row And DataGridView1.Columns.Count > col Then
DataGridView1.CurrentCell = DataGridView1.Rows(row).Cells(col)
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
GetDataFillGrid()
Timer1.Start()
End Sub
Private Sub GetDataFillGrid()
Dim conString = "YourConnectionString"
Dim Sql = "YourQueryString"
Dim dt = New DataTable
Try
Using con As New SqlConnection(conString)
con.Open()
Using cmd As New SqlCommand(Sql, con)
Using da As New SqlDataAdapter(cmd)
da.Fill(dt)
DataGridView1.DataSource = dt
End Using
End Using
End Using
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End Sub
PassEventArgsToCommand =“ True”应该设置为True,并且实现的RelayCommand可以将RoutedEventArgs或对象作为模板。如果将对象用作参数类型,则将其强制转换为适当的事件类型。
该命令将如下所示
<Button Width="150" Style="{DynamicResource MaterialDesignRaisedDarkButton}">
<behaviours:Interaction.Triggers>
<behaviours:EventTrigger EventName="Click">
<behaviours:InvokeCommandAction Command="{Binding OpenCommand}" PassEventArgsToCommand="True"/>
</behaviours:EventTrigger>
</behaviours:Interaction.Triggers>
Open
</Button>
命令方法如下所示
OpenCommand = new RelayCommand<object>(OnOpenClicked, (o) => { return true; });
“参数”将为路由事件对象。
还有日志,以防您好奇
2020-12-15 11:40:36.3600 | INFO | MyApplication.ViewModels.MainWindowViewModel | RoutedEventArgs
您可以看到记录的TypeName是RoutedEventArgs
RelayCommand的实现可以在这里找到。
PS:您可以绑定到任何控件的任何事件。类似于Window的Closing事件,您将获得相应的事件。