SauravBhattacharya
SauravBhattacharya

Reputation: 643

How to capture all user actions in a WPF application to store them in a log file?

Introduction

I have a WPF application with code written in C#, developed in Visual Studio 2013. This is a GUI solution and has many interdependent projects, complete with buttons, fields, menus, checkboxes and text.

Logging user actions

My goal is to create a log file (a txt file will do) of all actions that an end user takes while using this application. Example actions could be a button-click, writing in a blank text field, (de)select a check-box, close an activity/tab, etc. I want to capture all such user interactions in the application UI, and have those actions stored as summarized one line descriptions of the action in a text log file.

Possible solutions

Brute force method

One way to do this could be to update the event handlers of all the aforementioned elements (button, textfield, ...) with the code to write a summary of the action to append to a txt log file. For example, in an accept button click handler:

 private void buttonAccept_Click(object sender, EventArgs e)
 {
     // TODO: default button click handler code...

     // TODO: ...and the code to write the event to a txt log file         
 }

But this strategy will be very painful to accomplish, having to update each and every event handler (and even write event handlers for elements which don't have any currently).

Better way(s)

I am looking for a simpler way in which this could be achieved. Some ideas that I have are:

One way is changing the event handler base class (that all other event handlers inherit from?) to contain the log file writing code. This way the log file code will need to be written only once, and it will be called whenever any event handler is called, which is what we want (There needs to be a distinction made between a user generated events and an app generated event, which can prove difficult in case of event chaining. So this could be a challenge).

Another way is using some "sniffer" which works like a debugger and stores the stack trace of an application's methods, as the user moves through it. This would give us only a partial idea of the user's workflows.

    private void buttonAccept_Click(object sender, EventArgs e)
    private bool IsValidName(string userName)
    private void buttonCancel_Click(object sender, EventArgs e)
    private void Close()
    ...

Adding a time stamp to the message would be useful...

    3/1/2015 18:19 private void buttonAccept_Click(object sender, EventArgs e)
    3/1/2015 18:19 private bool IsValidName(string userName)
    3/1/2015 18:21 private void buttonCancel_Click(object sender, EventArgs e)
    3/1/2015 18:21 private void Close()
    ...

... because, deleting the messages which have a time stamp difference from that of the previous message below a suitable threshold, would leave only the methods that were directly called by the user actions. Assuming that methods signatures are descriptive enough, this message list could work as the desired log file.

    3/1/2015 18:19 private void buttonAccept_Click(object sender, EventArgs e)
    3/1/2015 18:21 private void buttonCancel_Click(object sender, EventArgs e)
    ...

However, this will only work for elements that have event handlers associated with them. Still, it's better than the brute force method.

Suggestion(s)?

I am having some difficulty in coming up with a way to implement these ideas. Can anyone elaborate on how they could be implemented? Or if you can come up with better ways to make the log file, please share your ideas and also how it can be developed. Thank you!

Upvotes: 9

Views: 6890

Answers (3)

Tengiz
Tengiz

Reputation: 8389

So, it's WPF; and if correctly written, it's also MVVM. Then you can decorate your viewmodels and capture all the bindings that change. The below example will demonstrate this. For button clicks, you can use the similar approach, except you will be intercepting the ICommand instances and listening to their events.

Example

For example, let's say you have your regular viewmodel defined (pseudo code) that has two-way property called SimpleProperty:

class SimpleViewModel : IViewModel
{
    public string SimpleProperty { get; set; }
}

To add logging, define a decorator as shown below (again pseudo code):

class LoggerViewModel : IViewModel
{
    private readonly IViewModel _originalVM;
    private readonly static Logger _logger = new Logger(); 

    public LoggerViewModel(IViewModel originalVM) 
    {
        _originalVM = originalVM;
    }

    public string SimpleProperty 
    {
        get 
        {
            _logger.Log("Page requested SimplePriperty and value is " + _originalVM.SimpleProperty);
            return _originalVM.SimpleProperty;
        }
        set
        {
            _logger.Log("Page changed SimpleProperty and value is " + _originalVM.SimpleProperty);
            _originalVM.SimpleProperty = value;
        }
    }
}

Obviously, the IViewModel interface looks like this:

interface IViewModel
{
    string SimpleProperty { get; set; }
}

Upvotes: 4

user3079266
user3079266

Reputation:

The most simple and straightforward way would be intercepting all of your application's windows messages and storing the information you need for the messages you need.

The most straightforward way to achieve this is to make a "message filter" (Application.AddMessageFilter). It looks something like this:

public class GlobalMessageLogger : IMessageFilter{

    private const int WM_LBUTTONDOWN = 0x201;

    public bool PreFilterMessage(ref Message message){
        if (message.Msg == WM_LBUTTONDOWN) {
            //Log message
        }
        return false;
    }
}

Then you can do this:

Application.AddMessageFilter(new GlobalMessageLogger());

You can use the Message struct members (of the message parameter) to get more info on a specific message. You'd have to read up on specific Windows messages, their codes (stored in the Msg param), and their wParam and lParam values. To find out what part of your application sent a specific message, you can use the HWnd property.

This is a pretty low-level approach, but it gives you great flexibility (you're not limited to UI elements that actually have event handlers), and makes changes for logging purposes hihgly localized.

Upvotes: 1

John Saunders
John Saunders

Reputation: 161773

Look into Microsoft UI Automation to see if it meets your needs. It includes the ability to be notified of events, which looks like what you want.

Upvotes: 2

Related Questions