Click here to Skip to main content
Click here to Skip to main content

XamlVerifier - check or auto correct binding path at compile and design time

By , 9 Jan 2012
 

XamlVerifier - check or auto correct binding path at compile and design time

Table of content

Introduction

Do you remember the time you loose everytime you make a spelling mistake on a binding path in your xaml files ?

You code, hit F5, which, for SL applications, deploy on Cassini or IIS after 10 or 20 seconds just to see that you have inverted 2 letters in a binding path.

I've developped a Genuilder extension to fix most of your spelling mistake automatically at compile and design time (when you save your xaml file). This is a beta, use it at your own risks.

Under the hood I use a generated pattern matcher lexer with Antlr, and NRefactory AST visitor to populate the types table... after showing how to use it, I will explain the implementation.

How to use it ?

First, install genuilder as explained here (and vote for it ;)).

Then in your solution add a new Genuilder project. (New Project/Visual C#/Genuilder)

Modify the program.cs file of your Genuilder project to install the XamlVerifierExtension:

static void Main(string[] args)
{
	foreach(var project in Projects.InSubDirectories("../..").ExceptForThisAssembly())
	{
		var ex = new ExtensibilityFeature();
		ex.AddExtension(new XamlVerifierExtension());
		project.InstallFeature(ex);
		project.Save();
	}
}

Run the Genuilder project, and reload your project.

Now let's add a PersonViewModel, in our project.

public class PersonViewModel
{
	public string Name
	{
		get;
		set;
	}
	public string Address
	{
		get;
		set;
	}
}

If you want to bind it in your MainWindows1.xaml here is the code with a spelling mistake.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock Text="{Binding Naem}"></TextBlock>
        <TextBlock Text="{Binding Addres}"></TextBlock>
    </Grid>
</Window>

You will notice such error only at run time. It breaks your flow and reduce your feedback. XamlVerifier can spot such error for you when you save your xaml file, just add a Start Verify comment like this :

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- Start Verify : PersonViewModel -->
        <TextBlock Text="{Binding Naem}"></TextBlock>
        <TextBlock Text="{Binding Addres}"></TextBlock>
    </Grid>
</Window>

Compile or save and you will see the error with some suggestions

But if you are a very busy person, you don't care about errors and just want them to be fixed.

For this, you just have to modify the Program.cs of your Genuilder project and run it one more time. (AutoCorrect property)

static void Main(string[] args)
{
	foreach(var project in Projects.InSubDirectories("../..").ExceptForThisAssembly())
	{
		var ex = new ExtensibilityFeature();
		ex.AddExtension(new XamlVerifierExtension()
		{
			AutoCorrect = true
		});
		project.InstallFeature(ex);
		project.Save();
	}
}

Save your XAML file (or compile your project) and just in front of your eyes XamlVerifier fix everything for you.

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- Start Verify : PersonViewModel -->
        <TextBlock Text="{Binding Name}"></TextBlock>
        <TextBlock Text="{Binding Address}"></TextBlock>
    </Grid>
</Window>

XamlVerifier is smart enough to correct every identifier in a binding path (for example : Contatc.Addrses.ZpiCodee, will be fixed in Contact.Address.ZipCode). And it can also fix the type name in the Start Verify comment ! :)

Now imagine every PersonViewModel have a list of ContactViewModel, you just have to use a new Start Verify and use End Verify to go back in the PersonViewModel context :

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- Start Verify : PersonViewModel -->
        <TextBlock Text="{Binding Name}"></TextBlock>
        <ItemsControl ItemsSource="{Binding Contacts}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <!-- Start Verify : ContactViewModel -->
                    <TextBlock Text="{Binding Mail}"></TextBlock>
                    <!-- End Verify -->
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <TextBlock Text="{Binding Address}"></TextBlock>
    </Grid>
</Window>

Now let's talk about the limitations of XamlVerifier.

Limitations

These limitations, depending on the demand, will be addressed in future release.

Cannot fix path on referenced types

This means that if the path return a type that is not present in your WPF/Silverlight project (ie FileInfo), then XamlVerifier will not fix it.

This limitation come from the implementation of XamlVerifier : to resolve identifier in path, it looks in a type table only populated by code files of your project.

Slow on large projects

By default, XamlVerifier will parse every code file in your project everytime you save or compile a xaml file.

You can fix that by setting the XamlVerifierExtension.CodeFiles property.

For example, in this example I will parse only ViewModel.cs files.

foreach(var project in Projects.InSubDirectories("../..").ExceptForThisAssembly())
{
	var ex = new ExtensibilityFeature();
	ex.AddExtension(new XamlVerifierExtension()
	{
		AutoCorrect = false,
		CodeFiles = new FileQuery()
			.SelectInThisDirectory(true)
			.Like(".*ViewModel.cs").ToQuery()
	});
	project.InstallFeature(ex);
	project.Save();
}

You can do the same to filter XAML files.

Implementation

The implementation took me very few line of code, I use Antlr to parse Xaml files, NRefactory to parse code files, Levenshtein distance to partial match property name and class name.

The integration of XamlVerifier to Visual studio is done with Genuilder.Extensibility. (XamlVerifierExtension)

Antlr, pattern matcher with lexer

This part was easy once I managed to install Antlr properly in my project, I just had to define a Lexer to fetch Start Verify, End Verify and Binding token along with identifiers.

lexer grammar XamlVerifierLexer;

options {
    language=CSharp3;
    TokenLabelType=CommonToken;
	k=10;
	filter=true;
}

@namespace{Genuilder.Extensions.XamlVerifier}

fragment F_WHITESPACE
	:	(' ' | '\t' | '\v' | '\f')*;

fragment F_PATH
	:	('a'..'z' | 'A'..'Z' | '_')
		('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*('.'('a'..'z' | 'A'..'Z' | '_')
		('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*)*;

STARTVERIFY
	:	'<!--' F_WHITESPACE 'Start Verify' F_WHITESPACE ':' F_WHITESPACE c=F_PATH F_WHITESPACE '-->' { YieldStartVerify($c); };
ENDVERIFY
	:	'<!--' F_WHITESPACE 'End Verify' F_WHITESPACE '-->';
BINDING
	:	'{Binding' F_WHITESPACE 'Path='? F_WHITESPACE c=F_PATH { YieldBinding($c); };

A "fragment" token is a private token of the Lexer and will not output when I will call XamlVerifierLexer.NextToken().

Note that filter=true; means that the lexer will just ignore characters when no token match input, it's called lexer pattern matching.

{ YieldStartVerify($c); } will call the YieldStartVerify method of the lexer with the $c token as parameter (here c=F_PATH).

I use these methods to keep track of binding'path and type's name. F_Path is a fragment, so I will not be able to get it by calling XamlVerifierLexer.NextToken().

These methods are in a partial class of the lexer :

public int index;
public IToken id;
public IToken path;
public Location idLocation;	

void YieldStartVerify(IToken id)
{
	this.id = id;
	idLocation = new Location(id.Line, id.CharPositionInLine + 1);
	index = id.StartIndex;
}
void YieldBinding(IToken path)
{
	this.path = path;
	idLocation = new Location(path.Line, path.CharPositionInLine + 1);
	index = path.StartIndex;
}	

To transform these ANTLR tokens into the following strongly typed one, I use XamlVerifierReader.

The implementation just instanciate a Lexer, take one antlr token after another and transform them in strongly typed ones. (XamlVerifierNode).

public class XamlVerifierReader
{
	public XamlVerifierReader(Stream stream)
		: this(new StreamReader(stream))
	{
	}

	public XamlVerifierReader(string content)
		: this(new StringReader(content))
	{
	}


	XamlVerifierLexer _XamlLexer;
	public XamlVerifierReader(TextReader reader)
	{
		_XamlLexer = new XamlVerifierLexer(new ANTLRStringStream(reader.ReadToEnd()));
	}


	public IEnumerable<XamlVerifierNode> ReadAll()
	{
		while(true)
		{
			var node = Read();
			if(node == null)
				yield break;
			yield return node;
		}
	}
	public XamlVerifierNode Read()
	{
		var token = _XamlLexer.NextToken();
		if(token.Type == XamlVerifierLexer.EOF)
			return null;

		var location = new Location(token.Line, token.CharPositionInLine + 1);

		if(token.Type == XamlVerifierLexer.STARTVERIFY)
		{
			return new StartVerify(_XamlLexer.id.Text, location, _XamlLexer.idLocation, _XamlLexer.index);
		}
		else if(token.Type == XamlVerifierLexer.ENDVERIFY)
		{
			return new EndVerify(location);
		}
		else if(token.Type == XamlVerifierLexer.BINDING)
		{
			return new XamlBinding(_XamlLexer.path.Text, location, _XamlLexer.idLocation, _XamlLexer.index);
		}
		else
			throw new NotSupportedException("Not supported token at " + location.ToString());
	}
}

XamlVerifierEvaluator will iterate on these nodes and output binding path/type name errors and suggestions.

But, in order to know whether a type or property exists or not, I must be able to parse code files. NRefactory was the way to go.

NRefactory, building type and property table

As you can see in the following class diagram, the XamlVerifierEvaluator will output an enumerable of XamlVerifierError and there is two types of error, each one with one suggestion to fix them.

XamlVerifierPropertyError, will give you errors about wrong binding path.

XamlVerifierTypeError, will give you errors about wrong type name in Start Verify comments.

XamlFiles are parsed with the XamlVerifierReader as seen just before.

CodeFiles are a list of CompilationUnit. (ASTs of code files parsed by NRefactory)

When XamlVerifierEvaluator will find a new Start Verify node or a new Binding path node it will seek the type in a lookup table, and for each binding path, in a table lookup of properties.

With the help of NRefactory and the visitor pattern, constructing these tables is not a problem. And as you can see in the code of _AllErrorsCore(), I use the TypeResolver type.

private IEnumerable<XamlVerifierError> _AllErrorsCore()
{
	TypeResolver resolver = new TypeResolver();
	foreach(var codeFile in CodeFiles)
	{
		resolver.VisitCompilationUnit(codeFile, null);
	}

The TypeResolver class just build these lookup tables by overriding some Visit* methods of AbstractAstVisitor.

public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
	nsStack.Push(namespaceDeclaration);
	try
	{
		return base.VisitNamespaceDeclaration(namespaceDeclaration, data);
	}
	finally
	{
		nsStack.Pop();
	}
}

public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
	TypeDecl typeDecl = GetOrCreateTypeDecl(typeDeclaration);
	stackTypes.Push(typeDecl);
	try
	{
		return base.VisitTypeDeclaration(typeDeclaration, data);
	}
	finally
	{
		stackTypes.Pop();
	}
}

public override object VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, object data)
{
	if(stackTypes.Count == 0)
		return null;
	var typeDecl = stackTypes.Peek();
	typeDecl.Properties.Add(new PropertyDecl(propertyDeclaration));
	return null;
}

Then for each XamlFile I use a XamlVerifierContext to keep track of current contextual type when I must resolve a binding path.

Each nodes, StartVerify, EndVerify and Binding, will update the current context, and/or output XamlVerifierErrors.

foreach(var xamlFile in XamlFiles)
{
		XamlVerifierReader reader = new XamlVerifierReader(File.ReadAllText(xamlFile));
		XamlVerifierContext context = new XamlVerifierContext();
		foreach(var node in reader.ReadAll())
		{
			IEnumerable<XamlVerifierError> errors = node.Visit(resolver, context);
			if(errors != null)
			{
				foreach(var error in errors)
				{
					error.File = xamlFile;
					yield return error;
				}
			}
		}
	}
}

StartVerify will update the context and check if a type really exists.

internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
{
	var result = resolver.FindType(Type);
	context.Types.Push(Type);
	if(!result.Success)
		yield return new XamlVerifierTypeError()
		{
			Suggestion = result.Suggestion,
			TypeName = context.Types.Peek(),
			Location = IdLocation,
			Index = Index
		};
}

EndVerify just pop the current Type out of the context.

internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
{
	if(context.Types.Count > 0)
		context.Types.Pop();
	yield break;
}

XamlBinding will check if the property exists, and for each component of the path, will try to resolve the component.

internal override IEnumerable<XamlVerifierError> Visit(TypeResolver resolver, XamlVerifierContext context)
{
	if(context.Types.Count == 0)
		yield break;


	var type = context.Types.Peek();
	int index = Index;
	for(int i = 0; i < Paths.Length; i++)
	{
		if(type == null)
			yield break;
		var result = resolver.FindProperty(type, Paths[i]);
		if(!result.Success)
		{
			yield return new XamlVerifierPropertyError()
			{
				PropertyName = Paths[i],
				Suggestion = result.Suggestion,
				ClassName = type,
				Location = IdLocation + new Location(0, i == 0 ? 0 : Paths[i - 1].Length + 1),
				Index = index
			};
		}
		type = result.TypeName;
		index += Paths[i].Length + 1;
	}
}

Traditionally, type and property lookups are implemented with hash table. In my case, I need to support partial matching, so I use FuzzyCollection, as you can see the use in TypeResolver methods.

FuzzyResult<PropertyDecl> GetProperty(string typeName, string propertyName, bool exact = false)
{
	var type = types.GetClosest(new TypeDecl(typeName)).FirstOrDefault();
	if(type == null || (exact && type.Distance != 0.0))
		return null;
	var property = type.Value.Properties.GetClosest(new PropertyDecl(propertyName)).FirstOrDefault();

	if(property == null && type.Value.BaseType != null)
	{
		return GetProperty(type.Value.BaseType, propertyName, true);
	}
	return property;
}

internal ResolveResult FindProperty(string typeName, string propertyName)
{
	var property = GetProperty(typeName, propertyName);

	if(property == null)
	{
		return new ResolveResult()
		{
			Success = false
		};
	}
	else
	{
		var tn = property.Value.Declaration.TypeReference.ToString();
		if(property.Distance == 0)
			return new ResolveResult()
			{
				TypeName = tn
			};
		else
			return new ResolveResult()
			{
				Success = false,
				Suggestion = property.Value.Name,
				TypeName = tn
			};
	}
}

internal ResolveResult FindType(string typeName)
{
	var type = types.GetClosest(new TypeDecl(typeName)).FirstOrDefault();
	if(type == null)
		return new ResolveResult()
		{
			Success = false
		};
	if(type.Distance == 0)
		return new ResolveResult();

	var closest = new FuzzyCollection<string>(Metrics.LevenshteinDistance);
	closest.Add(type.Value.Name);
	closest.Add(type.Value.FullName);
	var c = closest.GetClosest(typeName).First();

	return new ResolveResult()
	{
		Success = false,
		Suggestion = c.Value
	};
}

FuzzyCollection, implementing partial matching with metrics

GetClosest return an ordered list of closest matchs determined by the metric (distance calculator) given in the constructor.

FuzzyCollection have a O(n) time complexity so its fine for small collection of at most 500 elements, but clever algorithm like kD tree can make it down to O(log n). (k nearest neighbour problem)

The "distance" between two properties is the distance of their property's name (using Levenshtein distance)

public TypeDecl(string name, string ns)
{
	Namespace = ns;
	Name = name;
	_Properties = new FuzzyCollection<PropertyDecl>((a, b) => Metrics.LevenshteinDistance(a.Name, b.Name));
}

And the distance between two type declarations is the minimum distance between their name and full name (Person and MyProject.Person for exemple).

types = new FuzzyCollection<TypeDecl>((a, b) =>
{
	return Math.Min(Metrics.LevenshteinDistance(a.Name, b.Name), Metrics.LevenshteinDistance(a.FullName, b.FullName));
});

For this reason, when you specify <!-- Start Verify : Person --> or <!-- Start Verify : ConsoleApplication1.Person -->, both will be accepted, since both type are distance 0 from ConsoleApplication1.Person of the TypeResolver.

Nothing special in the implementation of FuzzyCollection.

public class FuzzyCollection<T>
{
	Func<T, T, int> _Metric;
	public FuzzyCollection(Func<T, T, int> metric)
	{
		_Metric = metric;
	}

	public List<FuzzyResult<T>> GetClosest(T source)
	{
		var results = _objs.Select(o => new FuzzyResult<T>
		{
			Distance = _Metric(source, o),
			Value = o
		}).ToList();
		results.Sort((a, b) => a.Distance.CompareTo(b.Distance));
		return results;
	}

	List<T> _objs = new List<T>();

	public T Add(T obj)
	{
		_objs.Add(obj);
		return obj;
	}
}

Genuilder Extensibility, plugging everything with MSBuild

All this code was very easy to unit test with very few line of code.

Now I need to get xaml files and CodeCompileUnits' code files from MSBuild, and output these errors in visual studio's error window. For this reason I create a new IExtension in with Genuilder.Extensibility called XamlVerifierExtension.

You can see the documentation of Genuilder.Extensibility on codeplex.

AutoCorrect will fix error with suggestions when encounter a new one.

CodeFiles and XamlFiles are FileQuery object, that will select what code file and xaml file you want to parse (to use in large project). All by default.

StopAfterFirstError will stop after seeing one error.

public void Execute(ExtensionContext extensionContext)
{
	var xamlFiles = XamlFiles ?? new FileQuery().SelectInThisDirectory(true).All().ToQuery();
	var codeFiles = CodeFiles ?? new FileQuery().SelectInThisDirectory(true).All().ToQuery();

	var xamlFilesByName = extensionContext.GenItems
		.GetByQuery(xamlFiles)
		.Where(i => i.SourceType == SourceType.Page)
		.ToDictionary(o => o.Name);

	if(xamlFilesByName.Count == 0)
		return;

	XamlVerifierEvaluator evaluator = new XamlVerifierEvaluator();
	evaluator.XamlFiles.AddRange(xamlFilesByName.Keys);

	foreach(var compilationUnitExtension in extensionContext.GenItems
		.GetByQuery(codeFiles)
		.Where(i => i.SourceType == SourceType.Compile)
		.Select(i => i.GetExtension<CompilationUnitExtension>()))
	{
		compilationUnitExtension.ParseMethodBodies = false;
		if(compilationUnitExtension.CompilationUnit != null)
			evaluator.CodeFiles.Add(compilationUnitExtension.CompilationUnit);
	}

This part just take all files recursively in the project directory if XamlFiles and CodeFiles are not set.

XamlVerifierEvaluator.XamlFiles is populated with xaml GenItems selected by XamlVerifierExtension.XamlFiles.

XamlVerifierEvaluator.CodeFiles is populated with code files CodeCompileUnit (without parsing methods body) thanks to the object extension CompilationUnitExtension.

Then it iterates on all errors and print them in the output error windows, or correct them.

XamlCorrector corrector = AutoCorrect ? new XamlCorrector()
{
	Evaluator = evaluator
} : null;

var errors = corrector == null ? evaluator.AllErrors() : corrector.AllErrors();

foreach(var error in errors)
{
	if(AutoCorrect)
		continue;

	var item = xamlFilesByName[error.File];
	item.Logger.Error(ToString(error), error.Location);

	if(StopAfterFirstError)
	{
		break;
	}
}

if(corrector != null)
	corrector.SaveCorrections();

XamlCorrector output the same errors as XamlVerifierEvaluator, but, before returning them, create corrections of the xaml files with the help of suggestions.

XamlCorrector.SaveCorrections(), replace actual xaml files by the corrections.

Here is how I output errors in error's window.

private string ToString(XamlVerifierError error)
{
	var propertyError = error as XamlVerifierPropertyError;
	if(propertyError != null)
	{
		return propertyError.PropertyName + " does not exist in " + propertyError.ClassName + Suggestion(error);
	}
	var classError = error as XamlVerifierTypeError;
	if(classError != null)
	{
		return classError.TypeName + " does not exist" + Suggestion(error);
	}
	return error.ToString();
}

private string Suggestion(XamlVerifierError error)
{
	if(error.Suggestion == null)
		return "";
	return ", do you mean " + error.Suggestion + " ?";
}

Conclusion

All this design is entirely streamed, which means that the ANTLR parser moves at the same speed as you iterate on errors.

This is a beta, use AutoCorrect at your own risks, I don't take any responsability if the sky falls on you... ;)

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Nicolas Dorier
Software Developer Freelance
France France
Member
I develop to make you forget what's between you and your work.
 
I teach and, with delight, you'll see that the best castles are done with the dumbest concepts.

Curiosity is the best teacher.
 
If you are interested for working with me, this way Smile | :)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionCan't get it to workmembershogedal24 Oct '12 - 20:30 
Do you think that it might not work together with ReSharper?
shogedal

AnswerRe: Can't get it to workmemberNicolas Dorier24 Oct '12 - 22:05 
Hi,
 
I don't think it come from resharper try loading visual studio without it with.
devenv.exe /SafeMode
 
It might also come from the limitation I documented : Cannot fix path on referenced types.
 
Can you let me know if safemode made it work ?
GeneralCool!memberVincent196813 Apr '12 - 0:30 
Very clever and cool enhancement! I think I saw the effect of this warning when using the XamlResourceExtension?
Quote:
Warning 15 URI-CLR namespace mapping is not yet supported, contact me on genuilder that URL-CLR mapping is not supported when using the
.codeplex.com to make it implemented.

My resource looks like this:
<Window.Resources>
  <DataTemplate x:Key="{x:Static myns:MyGlobalNames.DragTemplate}">
...
Therefore the generated partial class file in obj/x86/Debug does not compile:
public DataTemplate {x:Static myns:MyGlobalNames.DragTemplate}
{
    get
    {
        return (DataTemplate)App.Current.Resources["{x:Static myns.MyGlobalNames.DragTemplate}"];
    }
 
}
 
But I realized I don't need the indirect string reference (x:Static myns.MyGlobalNames.DragTemplate -> "DragTemplate") anymore because the XamlResourceExtension is here! So I changed my xaml back to this much nicer and cleaner form:
<Window.Resources>
  <DataTemplate x:Key="DragTemplate">
...
And now the generated code compiles:
public DataTemplate DragTemplate
{
    get
    {
        return (DataTemplate)App.Current.Resources["DragTemplate"];
    }
}
 
Was the above mentioned warning refering to this problem I met?
 
Thanks!
GeneralRe: Cool! [modified]memberNicolas Dorier13 Apr '12 - 2:05 
Hi,
 
Glad you like it, can you confirm your warning message ? you should have three lines,the full warning is :
 
"URI-CLR namespace mapping is not yet supported, contact me on genuilder.codeplex.com to make it implemented.
As a workaround, just add the using namespace declaration of the desired type in <codeBehindFile>.
To disable this warning set XamlResourceExtension.WarnForUnsupported to false."
 
This warning is thrown when types like DataTemplate is used in xaml.
When you check the default xmlns (the one you use for DataTemplate), you will see that there is a URL instead of clr-namespace + assembly name.
 
For the code to compile correctly in the generated file, the clr-namespace is needed. (because I add it in usings of the generated file)
 
So the workaround, if it does not compile, is to add the using diectly in the code behind, the generated file will copy these namespace.
If you did that, you don't need the warning message anymore so, in the genuilder project disable this warning by setting XamlResourceExtension.WarnForUnsupported to false."
 
This is a warning of the XamlResourceExtension (like you saw), and not the XamlVerifierExtension.

modified 13 Apr '12 - 8:26.

GeneralRe: Cool!memberVincent02613 Apr '12 - 3:31 
Confirmed, it is indeed this three line warning. This is how the namespaces myns and x are declared:
xmlns:myns="clr-namespace:MyCLRNamespace"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
So, the warning probably targets the namespace x?
 
But is the eventual problem not just caused by the text within the quotes, with all these curly braces and white spaces?:
<DataTemplate x:Key="{x:Static myns.MyGlobalNames.DragTemplate}">
This obviously leads to this ill-formed C# property:
public DataTemplate {x:Static myns.MyGlobalNames.DragTemplate}
   get
   {...
But, as I mentioned, the problem is gone since I don't use the complex Key value anymore. Now it is just Key = "DragTemplate".
GeneralRe: Cool!memberNicolas Dorier13 Apr '12 - 3:39 
I think the warning taget the default xmlns (DataTemplate) check xmlns="..."
Yes, with XamlResourceExtension, you have to use a simple string key for your resource or it will not compiled as you saw.
 
But I don't think the warning came from your old key, it is gone once you compiled the second time, because if Genuilder see that you recompile your project but no file (xaml or codebehind) changed, it does not regenerate the generated file, it takes the one from the previous build. (So I save build time)
 
But the warning does not mean you have done something wrong, it just give you a possible reason why the generated file cannot compile (because of namespace). If it compiles fine, no problem just remove the Warnings in the Genuilder project.
QuestionCoolmemberCIDev18 Jan '12 - 10:04 
A well written article and a useful tool.
Just because the code works, it doesn't mean that it is good code.

AnswerRe: CoolmemberNicolas Dorier18 Jan '12 - 10:22 
Thanks, my 30th 5 vote, my record ! :')
GeneralMy vote of 5memberAnkur\m/16 Jan '12 - 21:30 
Excellent work man!
GeneralRe: My vote of 5memberNicolas Dorier18 Jan '12 - 10:23 
Thanks \o/
GeneralGood articlememberWonde Tadesse16 Jan '12 - 13:57 
Good article. You have my 5.Keep it up
Wonde Tadesse
MCTS

GeneralRe: Good articlememberNicolas Dorier16 Jan '12 - 19:52 
Thanks Smile | :)
GeneralMy vote of 5memberMihai MOGA13 Jan '12 - 7:06 
Great article. Please keep it up!
GeneralRe: My vote of 5memberNicolas Dorier13 Jan '12 - 22:28 
Thanks Smile | :)
QuestionMy vote of 5memberGandalf - The White12 Jan '12 - 20:17 
Truly! This is really useful tool. A productive thought. Keep up the good work. Thumbs Up | :thumbsup:
Believe Yourself™

AnswerRe: My vote of 5memberNicolas Dorier12 Jan '12 - 21:08 
Thanks Smile | :)
GeneralMy vote of 5mvpMarcelo Ricardo de Oliveira10 Jan '12 - 10:48 
Excellent as always, Nicolas! Great contribution to XAML community, thanks for sharing!
 
cheers,
marcelo
There's no free lunch. Let's wait for the dinner.
 
Take a look at Windows Phone Labyrinth here in The Code Project.

GeneralRe: My vote of 5memberNicolas Dorier10 Jan '12 - 12:04 
thanks for reading marcelo Smile | :)
QuestionExcellent Work - My Vote of 5 :-)memberWhiteKnight10 Jan '12 - 8:43 
Interesting Nicolas! Keep up the good work!
Peter J. Santiago
White Knight Consulting, Inc.
www.whiteknightinc.com

AnswerRe: Excellent Work - My Vote of 5 :-)memberNicolas Dorier10 Jan '12 - 8:52 
Thanks Peter !
QuestionExtremely UsefulmvpDave Kerr9 Jan '12 - 22:46 
This is a seriously useful tool, thanks for sharing it!

AnswerRe: Extremely UsefulmemberNicolas Dorier10 Jan '12 - 0:40 
Thanks, and the more you use it, the more I will improve it ! Big Grin | :-D
GeneralMy vote of 5mvpPaulo Zemek9 Jan '12 - 9:59 
Excelent! This one is really useful! I'll use it!
GeneralRe: My vote of 5memberNicolas Dorier9 Jan '12 - 11:15 
"This one" Paulo said :'(
Thanks for using it, and don't forget to report me the bugs you find !
 
When no one is reporting bugs, there is two possibilities :
 
-I'm a code machine,
-No one is using it,
 
I opt for the second one
GeneralRe: My vote of 5mvpPaulo Zemek10 Jan '12 - 1:44 
This one... yes... I have immediate use for it.
Genuilder is great but I don't have immediate use for it.
I am starting a new game in WPF now... and one of the things I am starting to use a lot are those bindings. Checking them will be great.
Do you want to create a new programming language?
Do you want to know how to create a virtual machine?
Are at least interested on how they work?
So, see my article: POLAR

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 9 Jan 2012
Article Copyright 2011 by Nicolas Dorier
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid