【问题标题】:How to track WPF commands?如何跟踪 WPF 命令?
【发布时间】:2015-06-04 13:22:51
【问题描述】:

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

您对如何在不覆盖应用程序中使用的每个控件的情况下实现/设计类似的东西有想法/建议?

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

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

第二个挑战是确定用于触发命令的控件是什么(命令源)。我不想知道触发命令时哪个控件具有键盘焦点,我想知道用于触发命令的控件(通常是按钮、超链接、来自 ContextMenu 的 MenuItem 等)

MainWindow.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>

MainWindow.xaml.cs

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnSaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        e.Handled = true;
    }

    private void OnSaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
        e.Handled = true;
    }
}

编辑

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

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

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

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

【问题讨论】:

  • 构建一个类来保存你想要的所有数据。
  • 这个主题比你想象的要复杂一些......但是像PostSharp 这样的 AOP 方法让它变得容易多了,你应该能够用免费的方式完成你想要的版本(使用方法拦截)。

标签: c# wpf


【解决方案1】:

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

【讨论】:

  • 即使使用我自己的 ICommand 实现,我也不知道它最初是由鼠标单击还是按钮上的 Enter 键触发的......
【解决方案2】:

创建一个具有Stack&lt;ICommand&gt; 属性的单例或static 类,并将对该类的引用传递给您的Windows(或者最好是查看模型)。您当然应该使用一些典型的AddCommandRemoveCommand 方法封装Stack 对象。然后,每当调用ICommand 时,Push 就会将其转换为Stack

但是,您需要在单独的类中定义您的ICommands,或者最好使用网上找到的RelayCommand 形式。这是一个例子:

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;
}

我不确定你的第二个问题是什么意思,因为ICommands 被设置为相关控件的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>

【讨论】:

  • 感谢您的回答。我编辑了我原来的问题。
猜你喜欢
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 2022-07-09
  • 2019-03-08
  • 2010-12-05
  • 1970-01-01
  • 2011-03-04
  • 2017-07-06
相关资源
最近更新 更多