Replaceable resources in Silverlight

by John Rayner 28. July 2011 23:21

Some time ago I faced a requirement where I needed to be able override my Silverlight resources at runtime, and here’s how it got implemented.  All the resources that we wanted to be able to override got moved into a specific ResourceDictionary, which was brought into the App.xaml resources through a MergedDictionary.  Then at runtime, the following code would be executed:

public void LoadResourcesFromServer()
{
    DownloadStyle(new Uri("../ClientBin/brand/CustomizableResources.xaml", UriKind.Relative));
}

private void DownloadStyle(Uri downloadUri)
{
    WebClient wc = new WebClient();
    wc.DownloadStringCompleted += ParseAndAddStyles;
    wc.DownloadStringAsync(downloadUri);
}

This is just a pretty standard way of downloading a file.  It starts to get interesting when we process the downloaded XAML file (exception-handling code elided for clarity):

private void ParseAndAddStyles(object sender, DownloadStringCompletedEventArgs e)
{
    if (e.Error == null)
    {
        var loaded = (ResourceDictionary)XamlReader.Load(e.Result);
        MergeResources(loaded);
    }
}

private void MergeResources(ResourceDictionary resourceDictionary)
{
    foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
    {
        if (dictionary.Source.OriginalString == "CustomizableResources.xaml")
        {
            Application.Current.Resources.MergedDictionaries.Remove(dictionary);
            break;
        }
    }
    Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
}

This code parses the XAML into a ResourceDictionary, and then replaces the CustomizableResources dictionary that has been compiled into the XAP file with the one downloaded.

As you can imagine, there are a large number of failure cases which need to be considered in real-life (e.g. the web server gives a 404 or other error, the downloaded XAML fails parsing, or isn’t a ResourceDictionary, etc, etc) but this code is at the heart of our solution to the requirement.

Categories: Silverlight

Silverlight: Cross-domain, but not cross-zone

by John Rayner 16. March 2011 21:54

A colleague approached me to solve a Silverlight issue where a cross-domain web service call was generating a SecurityException.  We checked that clientaccesspolicy.xml was in the correct place and should allow the call and then we ran Fiddler.  What we noticed was that Silverlight was not even attempting the request for the clientaccesspolicy.xml file!

After some investigation and supposition, we eventually traced it down to an issue with Internet Security Zones.  The Silverlight hosting page was being accessed through a fully qualified domain name and was in the Internet zone, while the service was being accessed via computer name and was in the Intranet zone.  The solution was to setup a full DNS entry for the service and use that from our SL application, because then the service host was also in the Internet zone and so the call was allowed.

Now it definitely does make sense that an Internet site should not be able to make cross-domain calls into your Intranet (and kudos to the Silverlight team for taking security this seriously) but it can be a tricky problem to spot when it’s happening in your dev environment!

Categories: Silverlight

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

by Andrew Clancy 22. September 2010 09:58

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

Tags:
Categories: Linq | WPF | Silverlight | C# | MVVM

An MVVM marquee control (in Silverlight and WPF)

by John Rayner 21. September 2010 09:15

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!