How to track WPF commands? - c#

In a WPF application, I want to have a User Tracking System to keep statistics on the way users are using the application. In other words, I'm looking for a way to track what commands are being executed and how they have been triggered by the user (by clicking on the toolbar button, by using keyboard shortcuts, etc). So far, I haven't found a nice way to do this while using the WPF command pattern...
Do you have ideas/suggestions on how to achieve/design something like this without overriding every control used in the application?
For discussion purposes, I created a very basic WPF application containing a toolbar with a single Save button, a TextBox and a ListBox. I also added a KeyBinding to trigger the Save command when pressing CTRL+S.
The first challenge is to determine which device (mouse or keyboard) was used to trigger the command.
The second challenge is to determine what is the control used to trigger the command (the command source). I'm not interested to know which control had keyboard focus when the command was triggered, I would like to know what control was used to trigger the command (usually it's a button, an hyperlink, a MenuItem from a ContextMenu, etc.)
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;
}
}
EDIT
I realized my original question was a bit vague, I apologize. I will try to give more information and ask a more precise question.
I know it is simple enough to store a list of commands that have been executed. The challenge here is to retrieve which device was used to trigger the command initially: mouse or keyboard?
By putting the tracking logic in the "executed" handler, there is no way at this point to determine if the user triggered the command by clicking a button with the mouse, by pressing Enter on the button or if he used a keyboard shortcut. In my example, the same command can be triggered by clicking the toolbar button or by pressing CTRL+S on keyboard. How can I track these separate actions that will all trigger the same command?
Can this be achieve in the ViewModel layer? When we reach the command handler, it's already too late: we have lost this information. The only place we really know the device used is in the View itself. How to pass this information to the Command handler? Is the only way to do this is to override the Button control to intercept Click and KeyDown events in order to provide additional context to the command handler?

If you use the MVVM pattern then the Command would be bound from the View to a Command instance in the View Model. You could use create an ICommand implementation that provided an event when it was executed with some details about itself. Maybe use a command provider/factory/whatever to create each command and wire it up to a logger/tracker.

Create a Singleton or static class that has a Stack<ICommand> property and pass a reference to this class to your Windows (or preferably view models). You should of course encapsulate the Stack object using some typical AddCommand and RemoveCommand methods. Then, whenever an ICommand is called, Push it into the Stack.
However, you'll either need to define your ICommands in separate classes, or preferably use a form of the RelayCommand found online. Here's an example:
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;
}
I'm not exactly sure what your second question means, because the ICommands are set as the value to the Command property of the relevant control, so you should already know what controls they are, eg.:
<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>

Related

How to do loose coupling of button pressed and released events in Xamarin.Forms with Prism?

For my current project I need to capture button pressed and release events in Xamarin.Forms. But I want to keep things loosely coupled using Prism.
At first I used the Command property of the , like so:
<Button x:Name="ButtonForward" Command="{Binding MoveUpCommand}" />
But the Command property only fires when the button is released. To make seperate pressed and released actions I used the events in XAML:
<Button x:Name="ButtonForward" Pressed="ButtonForward_Pressed" Released="ButtonMove_Released"/>
And called the Commands manually in the event handlers in code behind:
private void ButtonMove_Released(object sender, System.EventArgs e)
{
var vm = BindingContext as DirectControlViewModel;
if (vm.MoveStopCommand.CanExecute(null))
vm.MoveStopCommand.Execute(null);
}
private void ButtonForward_Pressed(object sender, System.EventArgs e)
{
var vm = BindingContext as DirectControlViewModel;
if (vm.MoveUpCommand.CanExecute(null))
vm.MoveUpCommand.Execute(null);
}
The problem is that it isn't loosely coupled anymore, since the View now has to know its ViewModel.
Is there a way to have a button that does have seperate commands for pressed and released events, keeping the View and ViewModel loosely coupled? Any help would be appreciated.
Use the EventToCommandBehavior on the Button. This will allow you take advantage of any Event on anything you're working with and Execute a Command when the event is fired.
<Button>
<Button.Behaviors>
<prism:EventToCommandBehavior EventName="Pressed"
Command="{Binding PressedCommand}" />
<prism:EventToCommandBehavior EventName="Released"
Command="{Binding ReleasedCommand}" />
</Button.Behaviors>
</Button>
Note there are additional properties that you can utilize if you have some sort of Parameter that you would like to pass the Command which may be a property in the EventArgs, or something else entirely that you'd like to bind to or specify.

Is using command binding or event handler a better approach to implement Close, Minimize and Maximize Buttons in WPF?

I am making the close, maximize and minimize buttons in WPF C#. I tried two different methods and both seemed to work with me but I just want to know which approach in general is considered a better practice for this kind of implementation.
Method one:
in XAML:
<Window.CommandBindings>
<CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute_1" Executed="CommandBinding_Executed_1" />
<CommandBinding ... />
<CommandBinding ... />
</Window.CommandBindings>
<Button Command="{x:Static SystemCommands.CloseWindowCommand}" Content ="close"/>
<Button ... />
<Button .../>
in C#:
private void CommandBinding_Executed_1(object sender, ExecutedRoutedEventArgs e)
{
SystemCommands.CloseWindow(this);
}
...
Method two:
in XAML:
<Button Content="X" Click="CloseButton_Click" />
<Button .../>
<Button ... />
in C#:
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Close();
}
...
Since the close, maximize and minimize buttons are typically part of a control, or a template of a control, you could handle the Click event of the buttons directly. There is no need nor reason to introduce any command bindings just to be able to handle the click of a Button internally in a control.
You could either hook up the event handlers in XAML or, if the buttons are defined in a ControlTemplate of a custom control, you could override the OnApplyTemplate() method of the control and hook up the event handlers programmatically as suggested here: https://blog.magnusmontin.net/2013/03/16/how-to-create-a-custom-window-in-wpf/.
Button click handler is simple and fast (to implement). If you are sure you are going to use just this button to close your window - click handler is good enough. If however there is possibility you are going to need more than one place to close the window (for example - you are going to use "close" menu option) - then it's better to not spread the same logic over multiple places and use a command, because command has all handling logic in one place (CommandBinding_Executed_1 in your case). Also, command has CanExecute logic so you will be able to automatically disable your close button (and all other "close" controls) in case window cannot be closed right now (with click handler you will have to code this logic yourself). Also, with command you can close your window from any control down the tree, even if that control has no reference to your window at all. This applies to "command vs click handler" in general, not specifically to the case with closing window where some of the above might seem a bit contrieved.

Pass Event from Main Page to element in User Control

I have MainPage in WPF Project. This page contains sidebar, which I want to move in UserControl. But this sidebar also has some elements such as Button that has click events. In codebehind this event is related to viemModel that are binded to MainPage. So I don't want to bind this viewModel to UserControl.
What I want to do - pass this event to user control on MainPage and then this event bind to Button in UserControl.
Is it possible to do or it is bad idea? Thanks
For example :
SideBar has button :
<Button Grid.Column="1" Width="44" Height="44" Style="{StaticResource SearchBtn}" Click="Search_TextChanged" >
<Button.Content>
<Image Height="15" Width="15" Source="/Assets/icon-search.png"/>
</Button.Content>
</Button>
Click event is related to ViewModel
private void Search_TextChanged(object sender, EventArgs e)
{
_resultViewModel.Search_TextChanged(tbSearch.Text);
}
First of all, it is better not to use the code behind especially not the click events. I would suggest to use MVVM pattern inside you application This way you can use commands for the click events this can also bubble up on the stack.
And you can also share the ViewModel over the mainpage and maybe your usercontrol
I agree with Jordy, you should implement a MVVM pattern inside your application. You could look at MVVM Light https://mvvmlight.codeplex.com/ or get from the nuget package manager. With MVVM Light you will be able to handle button click events using Relay Commands and able to communicate between user controls with their built in Messenger.

RoutedEventArgs.Source give me wrong value? [duplicate]

I am new to WPF and am going through the examples of Professional WPF in .net 4.5. In the commands chapter, there is an example where multiple controls can send the same command. I am using a Button, CheckBox and MenuItem to trigger the New command.
The issue I am facing is that if MenuItem is pressed for the first time, the source shows correctly. However, after clicking the Button or CheckBox, then clicking MenuItem shows me the source of the last control Button or CheckBox, whichever was pressed. I couldn't find what was wrong with my code or why is this behavior shown by MenuItem in WPF.
Below is the code.
<Window x:Class="WpfApplication1.CommandSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandSample" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Command="New" MaxWidth="80" MaxHeight="30" Content="{x:Static ApplicationCommands.New}" />
<Menu MaxHeight="30" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>
<CheckBox Command="New"></CheckBox>
</StackPanel>
</Window>
namespace WpfApplication1 {
public partial class CommandSample: Window {
public CommandSample() {
InitializeComponent();
}
private void CommandBinding_Executed(object sender,ExecutedRoutedEventArgs e)
{
MessageBox.Show("New Command launched by " + e.Source);
}
}
}
Yes this is correct (or at least that's how it's designed). Routed commands start routing based on the CommandTarget you specify. If one isn't specified typically the object raising the event uses itself as the starting point (so the MenuItem in this case). So the routing starts with the MenuItem in this case as you might expect. Nothing handles it there so the CommandManager goes up the parent chain. When it hits an element that is a FocusScope (like the Menu), it checks the FocusedElement of the "parent" FocusScope (e.g. the FocusScope of the parent of the Menu which in this case is the Window). If there is a FocusedElement (which there will be one once you have focused an element in the window's focus scope such as your button, checkbox, a textbox that you might put in that stackpanel, etc.) then the CommandManager starts routing the event from that element. When it does that it creates a new ExecutedRoutedEventArgs where the OriginalSource is that starting element (so the button, checkbox, textbox) and then continues routing up the tree.
So when you first ran the app, the FocusedElement of the Window (that's the root focus scope in your example) is null so there is no re-routing needed so the CommandManager just kept going up the parent chain past the Menu and that is why MenuItem appeared as the Source & OriginalSource. When you clicked on the Button you gave that keyboard focus and as part of it also became the logically focused element of its focus scope (i.e. the FocusedElement of its containing FocusScope). So when the MenuItem was subsequently clicked and the CommandManager ultimately reached the Menu, it then re-routed over to the Button (or whatever you focused in the window's focusscope) and started routing up from there. I say this is expected because with routed command you want the routing to go through the logically focused element so that for example, the Cut command of a menu item would trigger a cut of the TextBox in the Window that had focus.

How to manipulate a window object from another class in WPF

I'm new in WPF and C#. I know a lot of VB.NET and I'm used to the way when I call a form object like textboxes, etc. I'm calling it from another form. Now, I'm using WPF, I'm confused. Because I have a Main Window. And I want to add and item to a listbox in the Main Window from a Class. In VB.Net , its just like this.
IN FORM2
Form1.Textbox.Text = "";
Wherein I can't do it in WPF. Can someone please Help me. Thanks!
WPF windows defined in XAML have their controls publicly accessible from other classes and forms, unless you specifically mark them with the x:FieldModifier attribute as private.
Therefore, if you make an instance of your main window accessible in another class, be it a Window or anything else, you'll be able to populate controls from within this second class.
A particular scenario is when you want to update the contents of a control in your main window from a child window that you have opened on top of it. Is such a case, you may set the child window's Owner property to the current, main window, in order to access it while the child is visible. For instance, let's say you have defined these two windows:
// MainWindow
<Window x:Class="TestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="mainListBox" Height="250" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Button Content="Open Another Window" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="20" Click="OpenAnotherWindow_Click"/>
</Grid>
</Window>
and
// AnotherWindow
<Window x:Class="TestApplication.AnotherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AnotherWindow" Height="300" Width="300">
<Grid>
<Button Content="Add New Item to Main Window" HorizontalAlignment="Center" VerticalAlignment="Center" Click="AddNewItem_Click"/>
</Grid>
</Window>
each in its own XAML file.
In MainWindow's code behind, inside the button click handler, you show an instance of AnotherWindow as a dialog and set its Owner property to MainWindow's instance:
private void OpenAnotherWindow_Click(object sender, RoutedEventArgs e)
{
AnotherWindow anotherWindow = new AnotherWindow();
anotherWindow.Owner = this;
anotherWindow.ShowDialog();
}
Now, you can access the MainWindow's instance from AnotherWindow's Owner property, in order to add a new item to the ListBox control defined on it, in the button click handler in AnotherWindow's code behind:
private void AddNewItem_Click(object sender, RoutedEventArgs e)
{
MainWindow mainWindow = Owner as MainWindow;
mainWindow.mainListBox.Items.Add(new Random().Next(1000).ToString());
}
It simply adds a new random number to the ListBox, in order to show how the code accesses and modifies the control's data in MainWindow.
Pure WPF solution, but also may be easiest in your case, is using a Data Binding in WPF.
Every form's control is binded to some data on ModelView (pure MVVM approach) or to data (more or less like yuo can do it in WindowsForms). So the "only" thing you have to do is to read/write data binded to controls on UI of that form.
For example, you have TextBox on Windows and want to read a data from it.
This TextBox is binded to some string property of the class that is responsible for holding the data for the controls on that form (just an example, in real world could be 1000 other solutions, based on developer decisions). So what you need, is not to say: "window give textbox" and after read TextBox's content, but simply read binded string property.
Sure it's very simply description of a stuff. But just to give you a hint. Follow databinding link provided above to learn more about this stuff. Do not afraid of a lot of stuff there, it's after all is not a complicated idea and also pretty intuitive. To make that stuff to work in simply case you will not need to make huge efforts by me. The stuff becomes really complex when you end up into real world applications.
This will get all active windows:
foreach (Window item in Application.Current.Windows)
{
}

Categories