Monday, October 26, 2009

XamlParseException when no AssemblyInfo.cs file available

In my project I have an exe and a dll assembly. The App.xaml in the exe references a ResourceDictionary in the dll assembly. This had worked for a long time, but all of a sudden it stopped working. The following very frustating error showed up during runtime:

System.Windows.Markup.XamlParseException: Could not load file or assembly 'MyAssembly.MyResource, Version=0.0.0.0, Culture=neutral, PublicKeyToken=1cf5186c6493e5b0' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040) Error in markup file 'MyAssembly.MyResource;component/MyResource.xaml'. ---> System.IO.FileLoadException: Could not load file or assembly 'Pulsim.UnitConversions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=1cf5186c6493e5b0' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

Strange thing is, that my compiled MyAssembly gets version 1.0.0.125.

So what this error basically says is: “I’m not able to load my self, because the version is incorrect.”

After several attempts to resolve this issue, I finally got to my build procedure which is based upon msbuild. I use CommunityTasks in combination with an automatic generation of AssemblyInfo.cs. This way makes it possible to include the SVN revision number in the assembly and file version. How to do that can be found here: http://rod.blogsome.com/2008/02/21/compiling-solution-in-visual-studio-with-generating-project-version-based-on-svn-revision/.

That article states that the following addition (among others) must be made to the .csproj file.

   1: … cut …
   2: <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   3: <Import Project="$(RootPath)\Common.proj" />

I finally switched line 2 and 3 in order to get the AssemblyInfo.cs file generated before the XAML parser/compiler kicks in. And guess what: it worked!

Summary: Be sure that an AssemblyInfo.cs file exists and is referenced within your project if you want to use XAML ResourceDictionaries!

Monday, July 13, 2009

Why are my WPF MenuItems grayed out

Well not all of them, but only those bound to WPF Commands.

After a 2 hour search, I finally found that a control that had focus, became invisible because it was not necessary anymore. Upon that event, my menuitems became grayed out (IsEnabled became false). Focussing a visible control on the screen resolved my issue.

So it seems that WPF Commands can not be executed when no focussed control is available.

Thursday, May 28, 2009

Databinding and LostFocus (HOWTO Force SourceUpdate for any WPF FrameworkElement)

Today I faced a bug that was not so easy to handle. While editing a value within a WPF textbox I pressed CTRL+S to save my work. Unfortunately my edited data was not saved, but the previous data was instead. How did that happen.

Well my WPF textbox was data bound to my dataobject. The dataobject (source) normally gets updated upon a Lostfocus of the textbox (that’s the default). However pressing CTRL+S (or even selecting the save menuitem under the file menu) does not result in the textbox losing focus.

OK. The short way to handle this problem is to get the binding expression of the textbox on de Text Property. Upon this binding expression it’s possible to call UpdateSource() explicitly and the value gets stored in the bound dataobject.

However I searched for a more generic approach because I do not have textboxes only. And it’s not always the Text Property that is being bound. So searching on the internet, I found a reaction on Nigel Spencer's blog: http://blog.spencen.com/2008/05/02/how-to-get-a-list-of-bindings-in-wpf.aspx#comment-1015806[^].

I modified the code a little bit to fit my needs and here is the result:

using System; 
using System.Windows;
using System.Windows.Data;
namespace MyNamespace
{
internal static class WPFHelper
{
internal static void ActionAllBindings(FrameworkElement targetElement, Action<BindingExpression> action)
{
WalkElementTree(targetElement, action);
}

private static void WalkElementTree(object obj, Action<BindingExpression> action)
{
var dependencyObject
= obj as FrameworkElement;
// Sometimes leaf nodes aren’t DependencyObjects (e.g. strings)
if (dependencyObject == null)
{
return;
}

// Recursive call for each logical child
ActionBindings(dependencyObject, action);
foreach (var child in LogicalTreeHelper.GetChildren(dependencyObject))
{
WalkElementTree(child, action);
}
}

private static void ActionBindings(DependencyObject target, Action<BindingExpression> action)
{
var localValueEnumerator
= target.GetLocalValueEnumerator();
while (localValueEnumerator.MoveNext())
{
var current
= localValueEnumerator.Current;
var binding
= BindingOperations.GetBindingExpression(target, current.Property);
if (binding != null)
{
action(binding);
}
}
}
}
}


Using the helper is extremely simple:



public void ForceSourceUpdateActiveElement()    
{
var focusedElement
= Keyboard.FocusedElement as FrameworkElement;
if (focusedElement != null)
{
MyNamespace.ActionAllBindings(focusedElement, bindingExpression
=> bindingExpression.UpdateSource());
}
}