C# 如何跟踪WPF命令?

C# 如何跟踪WPF命令?,c#,wpf,C#,Wpf,在WPF应用程序中,我希望有一个用户跟踪系统来统计用户使用应用程序的方式。换句话说,我正在寻找一种方法来跟踪正在执行的命令以及用户如何触发这些命令(通过单击工具栏按钮、使用键盘快捷键等)。到目前为止,我还没有找到一种在使用WPF命令模式时实现这一点的好方法 对于如何在不覆盖应用程序中使用的每个控件的情况下实现/设计这样的功能,您有什么想法/建议吗 为了便于讨论,我创建了一个非常基本的WPF应用程序,其中包含一个带有单个保存按钮的工具栏、一个文本框和一个列表框。我还添加了一个键绑定,以在按下CTR

在WPF应用程序中,我希望有一个用户跟踪系统来统计用户使用应用程序的方式。换句话说,我正在寻找一种方法来跟踪正在执行的命令以及用户如何触发这些命令(通过单击工具栏按钮、使用键盘快捷键等)。到目前为止,我还没有找到一种在使用WPF命令模式时实现这一点的好方法

对于如何在不覆盖应用程序中使用的每个控件的情况下实现/设计这样的功能,您有什么想法/建议吗

为了便于讨论,我创建了一个非常基本的WPF应用程序,其中包含一个带有单个保存按钮的工具栏、一个文本框和一个列表框。我还添加了一个键绑定,以在按下CTRL+S时触发Save命令

第一个挑战是确定使用哪个设备(鼠标或键盘)触发命令

第二个挑战是确定用于触发命令的控件(命令源)。我不想知道触发命令时哪个控件具有键盘焦点,我想知道触发命令时使用了什么控件(通常是按钮、超链接、上下文菜单中的菜单项等)

main window.xaml

<Window x:Class="TrackingCommands.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" x:Name="Me" Height="480" Width="600">
    <Window.CommandBindings>
        <CommandBinding Command="Save" Executed="OnSaveCommandExecuted" CanExecute="OnSaveCommandCanExecute" />
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Command="Save" Gesture="CTRL+S"/>
    </Window.InputBindings>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ToolBarTray Grid.Row="0">
            <ToolBar>
                <Button Command="Save" Content="Save"/>
            </ToolBar>
        </ToolBarTray>
        <TextBox Grid.Row="1" TextWrapping="Wrap" AcceptsReturn="True"/>
    </Grid>
</Window>
编辑

我意识到我最初的问题有点模糊,我道歉。我会尽量提供更多信息,并提出更准确的问题

我知道存储已执行命令的列表非常简单。这里的挑战是检索最初用于触发命令的设备:鼠标还是键盘

通过将跟踪逻辑放入“已执行”处理程序中,此时无法确定用户是通过鼠标单击按钮、按按钮上的Enter键还是使用键盘快捷键来触发命令。在我的示例中,可以通过单击工具栏按钮或按键盘上的CTRL+S来触发相同的命令。如何跟踪这些将触发同一命令的单独操作


这可以在ViewModel层中实现吗?当我们到达命令处理程序时,已经太晚了:我们已经丢失了这些信息。我们真正知道所用设备的唯一位置是视图本身。如何将此信息传递给命令处理程序?这样做的唯一方法是重写按钮控件以截获单击和按键事件,以便为命令处理程序提供额外的上下文吗?

如果使用MVVM模式,则命令将从视图绑定到视图模型中的命令实例。您可以使用创建一个ICommand实现,该实现在执行时提供一个事件,并提供有关其自身的一些详细信息。可以使用命令提供程序/工厂/任何东西来创建每个命令,并将其连接到记录器/跟踪器。

创建一个具有属性的单例类或
静态类,并将对该类的引用传递到
窗口(或最好是查看模型)。当然,您应该使用一些典型的
AddCommand
RemoveCommand
方法来封装
Stack
对象。然后,每当调用
ICommand
时,
将其推入
堆栈中

然而,您需要在单独的类中定义
ICommand
s,或者最好使用在线找到的形式。下面是一个例子:

private ActionCommand deleteCommand = new ActionCommand(action => DeleteCommand(AudioTrack),
    canExecute => CanDelete(AudioTrack)); 

public override ICommand Delete
{
    get { return deleteCommand; }
}

private void DeleteCommand(AudioTrack audioTrack)
{
    // Do work then add to Stack in CommandManager
    CommandManager.AddCommand(deleteCommand);
}

private bool CanDelete(AudioTrack audioTrack)
{
    return audioTrack != null;
}
我不太清楚你的第二个问题是什么意思,因为
ICommand
s被设置为相关控件的
Command
属性的值,因此你应该已经知道它们是什么控件,例如:

<MenuItem Header="Delete track" Command="{Binding Delete}" 
    CommandParameter="{Binding Release.ThinDiscs.CurrentItem}">
    <MenuItem.Icon>
        <Image Source="pack://application:,,,/App;component/Images/Delete.png" />
    </MenuItem.Icon>
</MenuItem>


构建一个类,该类将保存您需要的所有数据。这个主题比您意识到的要复杂一些。。。然而,像的AOP方法使它变得更容易,并且您应该能够通过免费版本(使用方法拦截)实现您想要的。谢谢您的回答。我编辑了我的原始问题。即使使用我自己的ICommand实现,我也不知道它最初是由鼠标单击或按下按钮上的Enter键触发的。。。
<MenuItem Header="Delete track" Command="{Binding Delete}" 
    CommandParameter="{Binding Release.ThinDiscs.CurrentItem}">
    <MenuItem.Icon>
        <Image Source="pack://application:,,,/App;component/Images/Delete.png" />
    </MenuItem.Icon>
</MenuItem>