Posts Tagged XAML

Silverlight VisualState changes using DataBinding not code behind


Download
I hate the fact that I have to keep using code behind to trigger VisualState changes in my view.  Frequently I just want to change a visual state of a control based on the properties of the object to which the control is bound – such as making additional options visible or invisible, changing the mode of the display based on information stored in the model etc.

So I’ve come up with a control that helps!  The VisualStateController can be placed in the XAML for a control and be bound to a property of the databound object using its StateMode dependency property. 

  • If the property is a bool then two further properties OnState and OffState are used to determine the visual state of the control based on the value of the property.
  • If the property is an enum then the visual state will be set the the text string name of the enum value.
  • If the property is a string it will be used to directly set the visual state

Now changes causing normal binding updates will automatically trigger the visual state changes.

<VisualStateCtrls:VisualStateController StateMode="{Binding IsSelected}"
             OnState="Selected"
             OffState="Unselected"/>

<VisualStateCtrls:VisualStateController StateMode="{Binding IsResizable}"
             OnState="Sizers"
             OffState="NoSizers"/>

The VisualStateController binds automatically to the UserControl (or Control) that it is a child of.  You can put it inside any panel within the body of your UserControl’s XAML.

You can find the project and source code here.

If you want to achieve the same thing using triggers and a slightly more wordy approach, you can use blend and triggers – see here for details.

, , , , , , ,

5 Comments

Debugging Silverlight “element is already the child of another element”!


DownloadThe problem

 I know how many people are having a problem with the catch-all XAML error “Element is already the child of another element” – it’s been a popular post on this blog and I come across it all of the time.  The real problem is that the exception gives no indication of the area causing the failure, you are forced into playing with the source file and banging your head against the wall.  Well, help is at hand!  

I have written a program that helps identify which part of the XAML is going wrong and tells you about it!  Now one caveat – it can only tightly identify a single error that is causing this problem – if more than one thing throws the exception then the best you can hope for is for it to point out the XAML container that contains the code that is failing – such as VisualStateManager.   In most cases it will be more specific, because only one element will have changed and cause the exception to be thrown. This ability is helping me a lot, hopefully it can help you too…  

The solution

 So here’s what I have done.  I have written a XAML validator that reads in the file and then attempts to process it.  When the file has a problem it starts to prune out nodes in the XAML one XML node at a time (I mean it puts back nodes that didn’t solve the problem and tries the next in sequence).  It does this starting with the nodes that have no children, then the ones with 1, 2 and so on, until it has to remove the entire body – hopefully the error is found long before this.  Using this technique the algorithm tries to give you the most tightly isolated area of the XAML to focus on.  

When it identifies a node, it checks whether removing any attribute helps and returns this in addition to the offending XAML if it resolves the issue.  

The result is a string describing the XAML and any offending attribute.  

This is a debugging tool only – it really slows down the application using it, so don’t call it when you don’t have a problem – but it massively improves your debugging time.  I normally instantiate in the constructor of the failing class and Debug.WriteLine() its result.  It is VITAL that you call it from the assembly that contains the failing XAML – it won’t work if you don’t. (See the discussion below if you want to know why!)  

You can download the project from here.  Feel free to use it as you see fit.  

Here’s an example of using it: 

  

public HtmlTextBox()
        {
            var s = ValidateXaml.GetXamlErrors(new System.Uri("/Alchemy.extension;component/Components/HtmlTextBox.xaml", System.UriKind.Relative));
            Debug.WriteLine(s);
            InitializeComponent();
       }   

Caveats

This isn’t going to work too well if removing a node that fails just causes the problem to propagate to a part of the XAML that was previously valid – the only way I can think about solving that requires far more recursion and taking out multiple nodes at each level.  I’d be interested in anyone elses view on this and any other strategies…

You may have a problem with project references, if you do then see the discussion below, hopefully I’ve accounted for most circumstances.

Discussion

Having had the initial idea about this approach I ran into a couple of stumbling blocks which are worth exploring for those who are interested.  

Firstly, when you call InitializeComponent in a UserControl etc – it uses Application.LoadComponent to parse the XAML.  I can’t use that approach because it takes a URI and I need to pass in a string which is the XAML with an element removed.  For this reason my code uses XamlReader to parse the file – the problem there is that it always throws an exception for XAML that contains an x:Class attribute.  My first job then is to strip that out.  

The next attribute problem was much harder to solve.  UserControl xmlns attributes are used to provide reference to components stored in other assemblies. The attribute provides the namespace and the assembly.  The assembly is omitted for other controls that are contained in the same assembly as the problem control.  LoadComponent has no problems with resolving this reference, but XamlReader throws our favorite exception if it isn’t there – this took me a LONG time to figure out.  

So I have to add an “assembly=” extension to the xmlns references that don’t contain one (except the xlmns:x) to ensure that XamlReader will parse a correct file.  I do this by enquiring about the calling assembly and adding its name to the attribute.  

Sounds sensible right?  Well it was a bit more complicated than that – if I just modify the attribute while it is connected to the document, some behind the scenes magic changes all of the things that are referred to by that namespace! Guess what the error is? Yep, “element is already the child of another element” 🙂  

So now I remove the attribute from the document, modify it and put it back – presto!  No unwanted changes and the algorithm finally gets to have a go at parsing the file.

If this attribute modification causes you a problem, you could remove the code and explicitly qualify your project references, this does work, I’ve tried it and would account for a situation in which I accidentally update an xmlns that doesn’t need touching – but this does mean that you won’t be using the VS2010 automatically generated references.  I hope that this won’t normally be a problem.

UPDATE: I’ve modified the source to allow you to pass a parameter that turns off the xmlns modifications – see above comment about explicit assembly references if you set this.  It defaults to “true” which enables the automatic update of references.

, , , , , , ,

7 Comments