Debugging design time data with MefedMVVM

 

Sometimes design time data just won’t show, even though everything works at runtime. This is quite normal since Expression Blend, and the Visual Studio designer for that matter don’t instantiate our views and ViewModels in the context of an application.

MefedMVVM has a concept of design time data using the IDesignTimeAware interface, so we can provide an implementation of our ViewModel purely for use by the designers.

In my current case, despite having all my bindings correct, and things working at runtime, Blend refuses to show me the design time ListBoxItems…

image

So like all things code-FAIL related, we debug, simply set a break point on the DesignTimeInitialization method and attach to Expression Blend.

image

You will need to close and re-open the Xaml View (or do a build) so Blend re-initialises our ViewModel… and viola! We now know what the problem is.

image

In my case it was simply a service which was missing at design time. Allowing defaults allows the design time composition to succeed since we don’t actually need it until runtime.

image

image

Much better!

October 22 2010

Visual Studio Extensibility and MefedMVVM

 

Initially the project started out as a quick hack together to enable a single piece of functionality. However as functionality is growing its time for the codebase to grow up too, and this includes the UI elements. I’ve decided to use MefedMVVM for its design time blend features and easy to grok codebase.

If you try and use it out of the box with your MEF Component and debug, you might get the following error:

System.ComponentModel.Composition.ImportCardinalityMismatchException occurred
  Message=No valid exports were found that match the constraint '(((exportDefinition.ContractName == "RunOrDebugViewModel") etc…

Doh! Taking a look at the default runtime IComposer (what tells the container where to look for your assemblies) we can understand a little more of why it can’t find our assemblies.

private AggregateCatalog GetCatalog()
{
    var catalog = new AggregateCatalog();
    var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
    var extensionPath = String.Format(@"{0}\Extensions\", baseDirectory);
    catalog.Catalogs.Add(new DirectoryCatalog(baseDirectory));
    catalog.Catalogs.Add(new DirectoryCatalog(baseDirectory, "*.exe"));
    if (Directory.Exists(extensionPath))
        catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));
    return catalog;

}

It’s basing the location from the current AppDomain, which makes perfect sense in a normal application, and infact should work with a deployed VSIX.

However when debugging a VSIX it resolves to:

"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\"

Odd, looking there my assemblies aren’t deployed there at all, debugging to find out where they actually are I can see:

“C:/Users/naeem.khedarun/AppData/Local/Microsoft/VisualStudio/10.0Exp/Extensions/Naeem Khedarun/NBehave/0.5.0.0/NBehave.VS2010.Plugin.Editor.dll”

I see, it’s actually deployed into the experimental instance of visual studio. Luckily for us, MefedMVVM does have a mechanism for us to override the default discovery logic.

So the first thing to do, is at whichever the first visual studio provider initialises first, we need to configure the MefedMVVM bootstrapper:

LocatorBootstrapper.ApplyComposer(new VisualStudioRuntimeComposer());

 

This will override the default runtime composer with our own, so lets implement the IComposer interface and tell it to look in the appropriate place…

public class VisualStudioRuntimeComposer : IComposer
{
    public ComposablePartCatalog InitializeContainer()
    {
        return GetCatalog();
    }

    public IEnumerable<ExportProvider> GetCustomExportProviders()
    {
        return null;
    }

    private AggregateCatalog GetCatalog()
    {
        var location = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
                             where assembly == typeof (ServiceRegistrar).Assembly
                             select assembly.Location).First();

        var directory = Path.GetDirectoryName(location);

        var catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new DirectoryCatalog(directory));

        return catalog;
    }
}

 

We look in the current AppDomain as before, but this time look for our loaded assembly (one should be loaded or the bootstrapper wouldn’t have been called) and grabs its directory.

We can now resolve ViewModels in the experimental instance! I haven’t yet tried the new Composer with the normal instance, however if there are any issues I’ll make a follow up post.

October 20 2010

Fast, lightweight full-text search engine for objects in WPF and Silverlight controls

Here’s a neat filtering technique which will give you the ability to quickly and easily index your C# objects and give your users a single search box to search across entire records in any itemscontrol, eg. an autocompletebox or a datagrid.

AutoCompleteBox:

image

DataGrid

image

Features:

  • These screenshots should give you the idea, it indexes most basic types, eg. dates (including month names), decimals (including comma formatting) and would be fairly easy to handle other search use cases.
  • It’s super-easy to implement, you just need to mark up your model (or view-model) with [Index] attributes – no other modifications necessary.
  • It’s fast, using linq expression lamdas to generate compiled property-accessors for the indexed attributes.

I’ll attach a demo solution in WPF, but here’s the key bits of code.

The fast linq property accessor. In my solution, this is used by another method which uses normal reflection to find the properties marked with [Index] attributes, then these compiled lamdas are cached so this is only done once.

/// <summary> /// Get fast compiled property accessor using a compiled linq expression lambda /// </summary> private static Func<T, object> GetFastPropertyAccessor<T>(string property) { ParameterExpression t = Expression.Parameter(typeof (T), "t"); MemberExpression prop = Expression.Property(t, property); return Expression.Lambda<Func<T, object>>( Expression.Convert(prop, typeof (object)), t).Compile(); }

The search index method is pretty basic stuff but effective. Essentially, all we do is break the object down into distinct search tokens by combining all indexed fields in to one string, then split the result into a string array of tokens. We’ll do the same with the search text and match them up. In WPF we can AsParallel() it to make this very fast, but this works fine for approx 30k records on my current project.

public string[] IndexUsingFields(IEnumerable<object> fields) { fields = fields.Where(a => a != null).ToArray(); var decimals = fields.OfType<decimal>(); var ints = fields.OfType<int>(); var dateTimes = fields.OfType<DateTime>(); var exclude = decimals.OfType<object>().Concat(dateTimes); string indexString = string.Concat( fields.Except(exclude).StringJoin(" "), " ", dateTimes.Select(d => d.ToString("d M MMMM yy yyyy hh:mm:ss")).StringJoin(" "), " ", decimals.Select(d => d.ToString("#.########")).StringJoin(" "), " ", decimals.Where(d => d > 1000).Select(d => d.ToString("N8")).StringJoin(" "), " ", ints.Where(d => d > 1000).Select(d => d.ToString("N")).StringJoin(" ")) .ToLower(); return this._searchSplitRegex.Split(indexString) .Select(s => this._removeLeadingZerosRegex.Replace(s, string.Empty)) .Where(s => !string.IsNullOrEmpty(s)).Distinct().ToArray(); }

And then the search itself, again just a simple split to get the search tokens, then matching it up with the index using some basic linq:

private string[] GetSearchTokens(string searchText) { return _searchTextTokensRegex.Split(searchText); } public bool Matches<T>(T subject, string searchText) { string[] searchTokens = this.GetSearchTokensCached(searchText); return searchTokens.All( t => _searchIndexDict2[typeof(T)][subject].Any(s => s.Contains(t))); }

This is all a bit simplified and edited for the blog so I encourage anyone to check out the solution to see how it fits together. It could do with some (weak) event handlers to update the indexes on property changes, I’ve got some ideas on how to make it faster for over say 100k records (currently works well for up to around 30k) by getting rid of the duplication in the search index, and it’d be neat if we could get some form of highlighting textbox into the mix. Overall though, it’s a very simple, quick effective way to impress with a single-box style filter that users are getting very used to in the web/search engine world.

image
FullTextSearchForSilverlightAndWpf.zip

September 22 2010

An MVVM marquee control (in Silverlight and WPF)

Marquee controls have been around for a long time and have been abused for much of that time.  Their place in the user experience should really be very small, since they have a number of drawbacks – information isn’t static for the user to absorb, they typically only display a limited amount of information and they assume a certain reading speed (which may be too slow or too fast).  Nevertheless, there are times when marquee controls are useful, news or stock tickers for example.

So I needed a marquee to display a news ticker in Silverlight, and I didn’t want to have use any nasty code-behind.  Nothing seemed entirely appropriate, so I used the marquee control from Jobi Joy as a starting point and extended it:

  1. The control needed to work in an MVVM application.  This meant that it mustn't rely on code calling “ticker.StartAnimation()” or anything like that – it had to all work by binding to a view model.
  2. I changed the animation so that there is always as much content visible as possible, i.e. the first bit of content starts at the left-hand side, not at the right-hand side
  3. Just for kicks, I added a little bit of acceleration to the scrolling, so that it is not always a constant speed
  4. I got the marquee working in WPF as well as Silverlight

Some friends contributed here too – Marcin extracted the control template into generic.xaml, and Marlon fixed an issue where the marquee would zoom uncomfortably fast if there was too much content.  The result is that you can just use a few lines of XAML to achieve the effect:

<Controls:Marquee InitialPause="2"
                  FinalPause="2"
                  Duration="5"
                  Acceleration="0"
                  Height="20"
                  Grid.Row="0">
    <ItemsControl ItemsSource="{Binding Headlines}" ItemTemplate="{StaticResource HeadlineTemplate}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Controls:Marquee>

The control template for this uses a ScrollViewer to house a Canvas.  The content to be scrolled is placed on the canvas.

<ScrollViewer Width="Auto"
              Height="Auto"
              BorderThickness="0"
              HorizontalContentAlignment="Stretch"
              VerticalContentAlignment="Stretch"
              VerticalScrollBarVisibility="Disabled"
              Margin="0"
              Padding="0">
    <Canvas>
        <ContentPresenter x:Name="PART_Content"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Center" />
    </Canvas>
</ScrollViewer>

An animation gets calculated and setup to scroll the content from right to left:

Storyboard _storyboard = new Storyboard();
DoubleAnimationUsingKeyFrames _animation = new DoubleAnimationUsingKeyFrames();

double totalDuration = InitialPause + Duration * _contentPart.ActualWidth / ActualWidth;
_animation.Duration = new Duration(TimeSpan.FromSeconds(totalDuration));

// _animation.KeyFrames setup omitted here for clarity. Get the source code if you want it.

_storyboard.Duration = new Duration(TimeSpan.FromSeconds(value + FinalPause));

if (_storyboard.Children.Count == 0)
{
    _storyboard.Children.Add(_animation);
    Storyboard.SetTargetProperty(_animation, new PropertyPath("(Canvas.Left)"));
    Storyboard.SetTarget(_animation, _contentPart);

    _storyboard.RepeatBehavior = RepeatBehavior.Forever;
}

_storyboard.Begin();

The result should show up below, if you have Silverlight 4 installed on your machine.

The full source code and samples in WPF and Silverlight can be found in the SharpFellows Toolkit.  And just for practice I used the Reactive Framework in the sample to asynchronously download and display some news headlines from a website.  Enjoy!

September 21 2010
Older Posts