Breaking MVVM: Change the background color of a control in a LightSwitch desktop app

A couple of months ago I was faced with the following hacking challenge:

One of our entities has pairs of properties: StreetName and StreetNameAdv, City and CityAdv, Name and NameAdv, …  For each of these pairs, the screen should clearly color the background of the xx property, if the value does not match the xxAdv property.

Or, put in pixels:
fieldhighlighting

If you happen to be familiar with the LightSwitch MVVM implementation, you’ll realize this is not a walk in the park, because the controls that form the View layer are tucked away in a LightSwitch theme extension.  Trust me, that’s one area you’ll want to stay away from unless you’re really experienced with LightSwitch extensibility.

So the easier way, in this case… Is to break our MVVM layering…

[Audience gasps]

Breaking MVVM: episode one: Finding ViewModel ContentItems bound to a particular property on the Model.

This code snippet contains an extension method to track all ContentItems that are displaying a particular property.  The first step is to retrieve an IScreenPresentationView from an IScreenObject.  We do this with the help of the ScreenViewService, a class intended to help out people with their custom shells.  From there, it’s a recursive walk down the children of this root, and find out who’s displaying what.


public static List<IContentItem> FindContentItems(this IScreenObject screen, IEntityProperty viewModel)
{
IPresentationScreenView screenView = VsExportProviderService
.GetServiceFromCache<IServiceProxy>().ScreenViewService.GetScreenView(screen) as IPresentationScreenView;
if (screenView == null)
{
throw new InvalidOperationException();
}
IContentItem currentView = screenView.ContentItemTree;
var alreadyFoundViews = new List<IContentItem>();
FindContentItems(currentView, viewModel, alreadyFoundViews);
return alreadyFoundViews;
}
private static void FindContentItems(IContentItem currentView, IEntityProperty viewModel, List<IContentItem> alreadyFoundViews)
{
if (currentView != null)
{
if (currentView.Details != null && currentView.Details.Equals(viewModel))
{
alreadyFoundViews.Add(currentView);
}
foreach (var child in currentView.ChildItems)
{
FindContentItems(child, viewModel, alreadyFoundViews);
}
}
}

Breaking MVVM: episode two: Finding ContentPresenters and their controls.

Once you have a ContentItem, it’s a walk in the park to get all associated presenters (the’View’ layer): call the GetAssociatedPresenters method.

Almost there.  What you have now is a bunch of IContentItemPresenters, which give you access to the Silverlight controls used by your selected LightSwitch theme via their .Visual property.  However, these Silverlight controls are usually a lot more complex than a mere ‘TextBox’ or ‘Label’ here and there.  Hence, you’ll have to take a recursive dive in the visual tree to find the real control you need to color:


public static IEnumerable<IContentItemPresenter> GetAssociatedPresenters(this IContentItem contentItem)
{
var internale = contentItem as IContentItemInternal;
if (internale == null)
return new List<IContentItemPresenter>();
return internale.AssociatedPresenters;
}
public static void SetBackground(this IContentItemPresenter presenter, Color color)
{
if (presenter.Visual != null)
{
Control el = presenter.Visual as Control;
//{System.Windows.Controls.Primitives.date}
TextBox tb = FindControlByType<TextBox>(el);
if (tb != null)
{
Border tbContent = FindControlByType<Border>(tb);
if (tbContent != null)
{
tbContent.Background = new SolidColorBrush(color);
}
}
if (el is DateTimePickerVisual)
{
TimeUpDown tu = FindControlByType<TimeUpDown>(el);
TextBox tbo = FindControlByType<TextBox>(tu);
Grid gr = FindControlByType<Grid>(tbo);
Border bo = FindControlByType<Border>(gr);
bo.Background = new SolidColorBrush(color);
}
}
}
private static T FindControlByType<T>(DependencyObject container) where T : DependencyObject
{
return FindControlByType<T>(container, null);
}
private static T FindControlByType<T>(DependencyObject container, string name) where T : DependencyObject
{
T foundControl = null;
//for each child object in the container
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
{
//is the object of the type we are looking for?
if (VisualTreeHelper.GetChild(container, i) is T && (VisualTreeHelper.GetChild(container, i).GetValue(FrameworkElement.NameProperty).Equals(name) || name == null))
{
foundControl = (T)VisualTreeHelper.GetChild(container, i);
break;
}
//if not, does it have children?
else if (VisualTreeHelper.GetChildrenCount(VisualTreeHelper.GetChild(container, i)) > 0)
{
//recursively look at its children
foundControl = FindControlByType<T>(VisualTreeHelper.GetChild(container, i), name);
if (foundControl != null)
break;
}
}
return foundControl;
}

view raw

ColorBackground

hosted with ❤ by GitHub

Mission accomplished!  Well, these code snippets are really only the ‘helper methods’ to help you break your MVVM layering.  Depending on your needs, you’ll need some additional code.  I have a sample that demonstrates the entire thing in action.  Feel free to grab it from my skydrive!  

Keep rocking LS!

Jan

4 thoughts on “Breaking MVVM: Change the background color of a control in a LightSwitch desktop app

  1. Pingback: LightSwitch Community & Content Rollup–January 2014 - Beth Massi - Sharing the goodness - Site Home - MSDN Blogs

  2. Hello Jan,
    I’m trying to implement this functionality but I cannot find class “IContentItemInternal”.
    I suspect that it’s into “Microsoft.LightSwitch.Presentation.Internal” but I can’t find it into my project.

    Best Regards
    Denis

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s