Wednesday, June 17, 2009

Creating an Ellipsis (...) TextBlock in Silverlight.

So I recently had the requirement to truncate text in a Silverlight TextBlock when it is too wide to fit, and suffix it with '...'. Easy I thought - hah hah you didn't bet on Silverlight though! I thought I could do some things around

1. MultiBindings - bind the actualwidth of the textblock, and the string property. Pass them into a IMultiValueConverter and work out how much text can be shown. Unfortunately SL doesn't support Multi Bindings. Pah.

2. Subclass TextBlock. I hate using inheritance to solve these sorts of problems - I don't want to force people to use my own version of TextBlock - what if for some reason TextBlock gets extended in future and I've killed the inheritance tree.

3. Explicity grab the TextBlock in the code-behind and have a helper function to set the text whenever it changes. I’m working against ViewModels and want to keep my code-behind empty so whilst this might work it’s not what I’m looking for.

4. Attached Properties - the old attached behaviour via attached property trick. That’ll save the day – and here’s how it works:

My first thought was an attached behaviour that would somehow get hold of the Binding, stash it, monitor it for changes, and create a new Text binding which would get the text with ellipsis where appropriate. SL doesn't allow you to get the underlying Binding though. Only set it. Pah.

So I decided Converters would be involved. Again, the problem was that I couldn't get the underlying Binding so I decided to make my one SL concession which was to ask the user of my new behaviour (EllipsisText) to pass me the Property they want to bind to as a string, instead of using a binding markup expression.

public static readonly DependencyProperty EllipsisTextPropertyNameProperty =
DependencyProperty.RegisterAttached(
"EllipsisTextPropertyName", typeof(string), typeof(EllipsisTextBoxBehaviour),
new PropertyMetadata(null, OnEllipsisTextChanged));



When this value is set I want to create a new Binding which will use a Custom Converter. The converter will need to know about the TextBlock though so it can get the maximum Width we have to play with.



private static void ConfigureEllipsis(TextBlock textBlock, string propertyName)
{
var binding = new Binding
{
Converter = new EllipsisTextConverter(textBlock),
Path = new PropertyPath(propertyName)
};
textBlock.SetBinding(TextBlock.TextProperty, binding);
}


Finally the converter – it is pretty simple. It takes the width of the TextBlock, then begins to measure how big the block would have to be to show the whole text. I do this by creating a new textblock, setting the font, etc to the same as the target one, setting the Text to the full string, then telling it to measure itself:



var textBlock = new TextBlock
{
Text = textToFit,
FontFamily = _textBlock.FontFamily,
FontSize = _textBlock.FontSize,
FontStretch = _textBlock.FontStretch,
FontStyle = _textBlock.FontStyle,
FontWeight = _textBlock.FontWeight
};



textBlock.UpdateLayout();


I can then begin to check the ActualWidth of this textblock against the width of the target one. Start chopping bits of the string (of course suffixed with ‘…’ when necessary) until the text fits in the allowed width. The TextBlock you are binding against needs an explicit Width set for this to work. The Layout pass hasn’t run when the converter fires and we need a Width to work with.



To use this all you have to do is:



<TextBlock Width="60" BehaviourExtensions:EllipsisTextBoxBehaviour.EllipsisTextPropertyName="BindingProperty" />



Full code:



public static class EllipsisTextBoxBehaviour
{
public static readonly DependencyProperty EllipsisTextPropertyNameProperty =
DependencyProperty.RegisterAttached(
"EllipsisTextPropertyName", typeof(string), typeof(EllipsisTextBoxBehaviour),
new PropertyMetadata(null, OnEllipsisTextChanged));

private static void OnEllipsisTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
var propertyName = e.NewValue as string;
if (textBlock != null && e.NewValue is string)
{
ConfigureEllipsis(textBlock, propertyName);
}
}

/// <summary>
///
Creates a new binding on the text property.
/// </summary>
private static void ConfigureEllipsis(TextBlock textBlock, string propertyName)
{
var binding = new Binding
{
Converter = new EllipsisTextConverter(textBlock),
Path = new PropertyPath(propertyName)
};
textBlock.SetBinding(TextBlock.TextProperty, binding);
}

public static void ClearEllipsisTextPropertyName(DependencyObject obj)
{
obj.ClearValue(EllipsisTextPropertyNameProperty);
}

public static string GetEllipsisTextPropertyName(DependencyObject obj)
{
return (string)obj.GetValue(EllipsisTextPropertyNameProperty);
}

public static void SetEllipsisTextPropertyName(DependencyObject obj, string text)
{
obj.SetValue(EllipsisTextPropertyNameProperty, text);
}

private class EllipsisTextConverter: IValueConverter
{
private readonly TextBlock _textBlock;

public EllipsisTextConverter(TextBlock textBlock)
{
_textBlock = textBlock;
}

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (_textBlock.Width == 0)
{
return value;
}

string textToFit = value as string;
if (textToFit != null)
{
var textBlock = new TextBlock
{
Text = textToFit,
FontFamily = _textBlock.FontFamily,
FontSize = _textBlock.FontSize,
FontStretch = _textBlock.FontStretch,
FontStyle = _textBlock.FontStyle,
FontWeight = _textBlock.FontWeight
};

int charsToChop = 0;
bool needsEllipsis = false;
do
{
textBlock.Text = textToFit.Substring(0, textToFit.Length - charsToChop) + (needsEllipsis ? "..." : "");
textBlock.UpdateLayout();

charsToChop++;
needsEllipsis = charsToChop > 0;

} while (
charsToChop < textToFit.Length &&
textBlock.ActualWidth > _textBlock.Width);

return textBlock.Text;
}
return value;

}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

}

Wednesday, January 21, 2009

Extending Asp.Net Dynamic Data

I recently had a requirement to quickly expose a database table via a web-page to allow some editing of data. Ah-ha I thought, lets see if we can get something for free. Being a .Net kind of bloke I decided the new Dynamic Data framework which ships with .Net 3.5 SP1 would be perfect.

Part of my requirement was not to allow inserts / deletes to the tables. Only updates. Pretty simple because you have access to the aspx / ascx templates and can literally remove the buttons / links and change the data sources to not allow inserts / deletes just for added protection to hand-crafted http posts.

You can create partials of the classes generated by Linq-Sql and attach attributes like MetadataType, or DisplayName to change the name of the table in the generated pages, or switch off columns from the dynamic scaffolding. You can also change columns in the linq-sql model to make them read-only. Nice :o) All is good in the world!

Not so fast though! The next requirement of course was to expose a table which absolutely must be inserted / deleted into. Damn! There didn't appear to be an in-built way to allow insert / delete for some tables but not others (not that I could see). But the answer was simple (so if there is an in-built way I don't have much code to change!!) and here's how I did it.

The template asp.net pages all have a MetaTable instance injected into it which amongst other things exposes an Attributes collection. These attributes are the Attributes from the table-model class, in my case the MetadataType / DisplayName attributes. So all you have to do is create another attribute, slap it on the partial classes, and pick it up from MetaTable.Attributes. In my case I created an attribute with two boolean properties, CanInsert and CanDelete. I also added an extension method to the MetaTable class, again CanInsert and CanDelete. The extension methods runs a quick query over the attributes to see if the attribute is there, and if so what the boolean value is. It looks something like this:


public static class MetaTableExtensions
{
  public static bool CanInsert(this MetaTable table)
  {
    var attribute = table.Attributes.OfType<TableExtensionAttribute>().FirstOrDefault();
    if (attribute == null)
    {
      return false;
    }
    return attribute.CanInsert;
  }
}


The asp.net template pages can now easily look for this attribute in for example the OnRowDataBound event of lists, and make a decision on whether or not to show a Delete button / Insert link.

Simple but effective.

Thursday, January 15, 2009

Hand-crafted IOC in Asp.Net MVC

IOC seems to be a bit of a buzzword again recently with frameworks like Asp.Net MVC popping up allowing the possibility of plugging custom Controller Factory objects directly in.

That's great, but sometimes you just don't want the baggage of a Container hanging around, especially if you are writing a small-ish solution. I faced that scenario today and stumbled upon what I think is a really elegant solution!

The problem... I want to inject dependencies into Controller classes, but don't want to use a container.
The solution... OK, bear with me it's easy!

In the global application_onstart method I have the opportunity to construct a custom ControllerFactory and pass it to Asp.Net. I won't cover that. Try here for more information.

My initial thought was to have a ControllerFactory expose a Register<T>(string name) where T:Controller method. That would let me register a Controller against a name, and then do an Activator.CreateInstance to create it. But of course in the real world the whole point of IoC is that the Controller will have dependencies it needs satisfying so this approach is kind of lacking!

One option I thought of was to have a Builder class for each Controller which would know how to create its Controller. I could RegisterBuilder<T>(Builder<T> builder) where T:Controller. Now my ControllerFactory can grab the appropriate builder and just ask it to Build(). The builder would be specific to a Controller so can create it concretely. It works, but it could lead to an explosion of Builder classes.

Then I remembered my old friend Func<>. It's nothing new really and is just a concrete implementation of a Delegate we could have written in .Net 2.0 - delegate T Func<T>() I think is the signature- don't have Reflector to hand. It allows me to do something like this:


ControllerFactory.Register<T>(Func<T> buildController) where T:Controller.


My global.asax can now look something like this


var provider = new DbConnectionProvider(Configuration.ConnectionStrings[....]);
var customerRepository = new CustomerRepository(provider);

var controllerFactory = new ControllerFactory();

controllerFactory.Register<HomeController>(() => new HomeController(customerRepository));


When a request comes in to the ControllerFactory it can say something like:


Dictionary<Type, Func<Controller> _controllerDictionary;

...

_controllerDictionary[controllerTypeToBuild]()


The syntax might look a bit weird, but essentially the dictionary contains functions which build a particular type. So when we grab something from the dictionary we just have to execute it hence the () call.

No container, no builder classes. Just everything wired up ready to go in my application start-up.

That might pose an architectural question of 'What do you mean by wiring up your application at start-up?' To answer than let me link you to Misko Hevery's blog - a google guy with some awesome posts on writing testable applications.

Sunday, January 11, 2009

Learning OLAP and Analysis

As a consultant I find myself working on different Customer sites doing a wide variety of different types of IT work. This might include Asp.Net, WPF, WCF, you name it, from the MS Technology Stack.

One technology I'll be working with over the next few months is OLAP. It's a technology that I've always seen as slightly Voodoo-ish, Black Magic like, akin to walking into a mysterious smoky room with a pointy bearded Wizard lurking over a Crystal Ball!

Over my next few blog posts I'll aim to break down some of the mystery and offer some laymans descriptions to OLAP - what it is, how it works, and how to work with it. For my first post I'll describe some of the key terms, along with some links that I've found useful on understanding it better.

Measure
This one is pretty simple. Whenever we analyse some data we are analysing a particular type of information. It's usually a numerical piece of data and some examples might be Sales Amount or margin, GST, VAT, etc.

Dimension
Starting with an example, lets I have some Sales Data against time. The total amount taken be a store per day. In this case my dimension is time! Simple huh? I might ask my OLAP database to show me the sales total against Time.

As another example my sales data might also be broken down against location. I can ask my OLAP database to show me sales data for 2004 in Australia. We've defined another dimension, location.

Cube
No more black magic. Think of a cube - it's a simple 3-dimensional object. Lets say I have a cube where the X-dimension are my 'measures' - sales amount, margin, units sold, etc, my Y-dimension is time, and my Z-dimension is location.

You should start to see how we can form a query to the cube along the lines of "Show me the total margin, in February 2008, in Western Australia". The Performance Point video I'll link to shows this beautifully, but take the cube and take the 'Slice' of the cube representing Margin. Then cut another slice of the same cube and get the WA slice. Finally cut a 3rd slice along the time dimension for 'February 2008'. What you'll end up with is one piece of the cube where these 3 slices intersect which happens to contain the data we're after. Simple huh?!

Enough for now. Next time we'll either look into more detail around the Cube, or take a look at some of the query language (MDX) we use to write the queries to it.

Nice MS video on cube basics

Wikipedia on OLAP

Nice introduction linked from Wikipedia

Updated 4:55pm removed the Slicing and Dicing quote. Not sure if that's what slicing and dicing is!!