Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

It took some time but Slick Code Search is now finally published on codeplex. There is a binary release available on codeplex and the source is available via Google's subversion hosting: http://slickcodesearch.googlecode.com/svn/trunk/

So what is this thing? Well it is a small WPF application that lets you index and search through your source files (C# only for now), in short a Google desktop for your local code.

slickcode3

When you start it up you get this floating textbox where you can type in a Lucene search query. Your can search for both type and method names. Currently you need to press the enter key to execute the search and update the result list.

Example Lucene queries:

  • t:ISessi*   Searching for a type beginning with "ISessi"
  • m:Get*     Searching for all types that have a method beginning with "Get"

For a complete description of the Lucene syntax: http://lucene.apache.org/java/docs/queryparsersyntax.html

The result list looks like this:

slickcode4

You can navigate the result list by using the up/down keys. To expand/collapse an item just press the left/right keys. Once an item is expanded the type's methods will be listed and you can now navigate those with the up/down keys. If you press right when a method is highlighted a code window will appear with that method in focus.

You can also press the enter key while having a result item highlighted, this will open that file in a program you can specify in the options dialog.

slickcode5

The above screenshot shows how an item looks while it is expanded. If anyone has any good ideas for new features then please add them in the issue tracker on codeplex. I think this app needs something more to be really useful.

The code for this app is a little strange. It started as a "learn WPF demo" app that grew to something more. Then I tried a model view presenter pattern with castle windsor integration just for fun, but this refactoring is more like an afterthought and it shows in the code.

I actually started this app along time ago after seeing a screencast by Ayende where he developed a code search engine. Some of the basics of what he did in that cast can be found in Slick Code Search (a SharpDevelop library that handles the CSharp file parsing and Lucene.NET for the text searching).

I have been working on a WPF application lately and today I came across a rather common scenario where I needed to do stuff in a background thread (in order to not lock the UI). In this scenario I needed to do indexing, during the indexing I want the UI to be updated with progress (what file is being index, how many has been indexed, etc). I handled this by having the indexing service expose some progress events that the UI could subscribe to, like this:

private void OnStartIndexing()
{
    var indexer = new FileIndexer();
    indexer.IndexingFile += IndexingFileCallback;
    indexer.IndexingCompleted += IndexingCompletedCallback;
    indexer.SavingIndexStarted += IndexerSavingIndexStarted;

    ThreadPool.QueueUserWorkItem(x => { indexer.Run(); });
}

protected void IndexingCompletedCallback()
{
    View.HideInfoText();
}

The problem here is that the function IndexingCompletedCallback is not being called in the UI thread and will throw an exception. To solve this you have to use the Dispatcher like this:

Action action = delegate()
{
    Views.HideInfo();
};
Dispatcher.BeginInvoke(DispatcherPriority.Normal, action);

When I saw this I thought that this could be handled in a more general declarative way by using function attributes and AOP. So I tried to get this to work:

[UseDispatcher]
protected virtual void IndexingCompletedCallback()
{
    View.HideInfoText();
}

The fun part was that it was very easy to do, since I already used Castle Windsor for my presenters it was just a matter of configuring my base presenter class with an interceptor and in the interceptor it was just a matter of calling the invocation.Proceed() in a delegate passed to the Dispatcher.

Just for completeness here is the code for the base presenter:
[Interceptor(typeof(PresenterInterceptor))]
public abstract class Presenter<T>
{
    public T View { get; private set; }
    
    public void Wireup(T view)
    {
        View = view;
        Initialize();
    }

    protected abstract void Initialize();
}
And here is the interceptor:
public class PresenterInterceptor : IInterceptor
{
    private IDispatcher dispatcher;

    public PresenterInterceptor(IDispatcher dispatcher)
    {
        this.dispatcher = dispatcher;
    }

    public void Intercept(IInvocation invocation)
    {
        object[] attributes = invocation.Method.GetCustomAttributes(typeof (UseDispatcherAttribute), true);
        
        if (attributes.Length == 0)
        {
            invocation.Proceed();
            return;
        }

        dispatcher.RunInUI(invocation.Proceed);
    }
}

Okey so after all that I now discovered that the BackgroundWorker might have been a good choise for this scenario since it has progress event that seems to automatically be executed in the UI thread, I am not sure yet. I will have to try it and do another blog post about it. But I still feel that this solution is nice and easy, and if you use Castle Windsor it is a quick thing to implement.

Ok, maybe a little to much code for a first blogpost, but I named the blog slickcode so why not :)