Better MEF composition error diagnostics in .NET 4.5

Many of us suffered with obscure composition error messages of Managed Extensibility Framework. The most common error is ImportCardinalityMismatchException: No exports were found that match the constraint, but there are others. Much of the obscurity is caused by a feature called Stable Composition. It silently tries to compose all of your parts even if some of the imports are not satisfied. This leads to the actual errors to be buried deep into the composition graph, not to be seen in the thrown exception.

However, starting from .NET Framework 4.5, you can greatly improve the error messages by using new CompositionOptions enumeration, DisableSilentRejection option in particular. For example:


var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection);

HSL Color for WPF, and Markup Extension to Lighten/Darken Colors in XAML

When developing WPF applications with custom visual design, it is very convenient to have a way to specify lighter or darker shades of the same base color. One obvious application of this is setting colors for hovered and pressed states of a button. Here is my solution to this problem.

First, we need to a way to separately control HSL components of color. Here is HslColor class:


public class HslColor
{
	public readonly double h, s, l, a;

	public HslColor(double h, double s, double l, double a)
	{
		this.h = h;
		this.s = s;
		this.l = l;
		this.a = a;
	}

	public HslColor(System.Windows.Media.Color rgb)
	{
		RgbToHls(rgb.R, rgb.G, rgb.B, out h, out l, out s);
		a = rgb.A / 255.0;
	}

	public System.Windows.Media.Color ToRgb()
	{
		int r, g, b;
		HlsToRgb(h, l, s, out r, out g, out b);
		return System.Windows.Media.Color.FromArgb((byte)(a * 255.0), (byte)r, (byte)g, (byte)b);
	}

	public HslColor Lighten(double amount)
	{
		return new HslColor(h, s, Clamp(L * amount, 0, 1), a);
	}

	private static double Clamp(double value, double min, double max)
	{
		if (value < min)
			return min;
		if (value > max)
			return max;

		return value;
	}

	// Convert an RGB value into an HLS value.
	static void RgbToHls(int r, int g, int b,
		out double h, out double l, out double s)
	{
		// Convert RGB to a 0.0 to 1.0 range.
		double double_r = r / 255.0;
		double double_g = g / 255.0;
		double double_b = b / 255.0;

		// Get the maximum and minimum RGB components.
		double max = double_r;
		if (max < double_g) max = double_g;
		if (max < double_b) max = double_b;

		double min = double_r;
		if (min > double_g) min = double_g;
		if (min > double_b) min = double_b;

		double diff = max - min;
		l = (max + min) / 2;
		if (Math.Abs(diff) < 0.00001)
		{
			s = 0;
			h = 0;  // H is really undefined.
		}
		else
		{
			if (l <= 0.5) s = diff / (max + min);
			else s = diff / (2 - max - min);

			double r_dist = (max - double_r) / diff;
			double g_dist = (max - double_g) / diff;
			double b_dist = (max - double_b) / diff;

			if (double_r == max) h = b_dist - g_dist;
			else if (double_g == max) h = 2 + r_dist - b_dist;
			else h = 4 + g_dist - r_dist;

			h = h * 60;
			if (h < 0) h += 360;
		}
	}

	// Convert an HLS value into an RGB value.
	static void HlsToRgb(double h, double l, double s,
		out int r, out int g, out int b)
	{
		double p2;
		if (l <= 0.5) p2 = l * (1 + s);
		else p2 = l + s - l * s;

		double p1 = 2 * l - p2;
		double double_r, double_g, double_b;
		if (s == 0)
		{
			double_r = l;
			double_g = l;
			double_b = l;
		}
		else
		{
			double_r = QqhToRgb(p1, p2, h + 120);
			double_g = QqhToRgb(p1, p2, h);
			double_b = QqhToRgb(p1, p2, h - 120);
		}

		// Convert RGB to the 0 to 255 range.
		r = (int)(double_r * 255.0);
		g = (int)(double_g * 255.0);
		b = (int)(double_b * 255.0);
	}

	private static double QqhToRgb(double q1, double q2, double hue)
	{
		if (hue > 360) hue -= 360;
		else if (hue < 0) hue += 360;

		if (hue < 60) return q1 + (q2 - q1) * hue / 60;
		if (hue < 180) return q2;
		if (hue < 240) return q1 + (q2 - q1) * (240 - hue) / 60;
		return q1;
	}
}

Then, we need a markup extension to lighten and darken a specified color:


[MarkupExtensionReturnType(typeof(SolidColorBrush))]
public class LightenExtension : MarkupExtension
{
	public SolidColorBrush Source { get; set; }
	public double Amount { get; set; }

	public override object ProvideValue(IServiceProvider serviceProvider)
	{
		return new SolidColorBrush(new HslColor(Source.Color).Lighten(Amount).ToRgb());
	}
}

Here is how to use it to lighten background on hover:


<SolidColorBrush x:Key="BackgroundBrush" Color="Red" />
...
			<Trigger Property="Border.IsMouseOver" Value="true">
				<Setter TargetName="Border" Property="Background" Value="{color:Lighten Source={StaticResource BackgroundBrush}, Amount=1.3}" />
			</Trigger>

You can also darken colors by providing a value less than 1 for amount, e.g.


{color:Lighten Source={StaticResource BackgroundBrush}, Amount=0.7}

A similar technique can be used to lighten and darken data bound colors by introducing a value converter:


public class LightenConverter : IValueConverter
{
	public double Amount { get; set; }

	public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
	{
		var source = (SolidColorBrush)value;
		return new SolidColorBrush(new HslColor(source.Color).Lighten(Amount).ToRgb());
	}

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

And the usage is:


<Resources>
    <color:LightenConverter x:Key="DarkenConverter" Amount="0.65" />
</Resources>

....

<... Foreground="{Binding SomeProperty, Converter={StaticResource DarkenConverter}}">

RGB to HSL conversion credits go to this article.