WPF: Attached Property in XAML Markup (The object ‘…’ already has a child and cannot add ‘…’)

Let’s say you need an attached property to set an arbitrary header content to any DependencyObject, to let you use that value and populate the Header property of any HeaderedContentControl, creating the class directly in your WPF test application:

public static class HeaderManager
{
	public static readonly DependencyProperty HeaderProperty = DependencyProperty.RegisterAttached(
		"Header",
		typeof(object),
		typeof(HeaderManager),
		new PropertyMetadata(null));

	public static void SetHeader(DependencyObject element, object value)
	{
		element.SetValue(HeaderProperty, value);
	}

	public static object GetHeader(DependencyObject element)
	{
		return (object)element.GetValue(HeaderProperty);
	}
}

Then you want to attach that property to an UserControl that when injected in a TabItem can self declare it’s own header, and in first attempt you will end up trying to do this:

<UserControl x:Class="RadicalTabRegion.Presentation.FirstView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:s="clr-namespace:RadicalTabRegion.Presentation.Regions.Specialized"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">

    <s:HeaderManager.Header>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="First View"/>
            <CheckBox/>
        </StackPanel>
    </s:HeaderManager.Header>

    <Grid>
        <TextBlock Text="I'm the first view"/>
    </Grid>

</UserControl>

But if you try to compile this, you will get this error:

“The object ‘UserControl’ already has a child and cannot add ‘Grid’. ‘UserControl’ can accept only one child.”

This is because the compiler needs to know that HeaderManager.Header is an attached property, before compile xaml in baml, but because the HeaderManager class is declared in the same assembly of the xaml, it can’t. Actually I think this can be overcome by Microsoft, but never mind there’s a couple of solutions for that.

The first solution is to move the markup declaration after the first element in the UserControl content, and our case after the Grid:

<UserControl x:Class="RadicalTabRegion.Presentation.FirstView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:s="clr-namespace:RadicalTabRegion.Presentation.Regions.Specialized"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">

    <Grid>
        <TextBlock Text="I'm the first view"/>
    </Grid>

    <s:HeaderManager.Header>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="First View"/>
            <CheckBox/>
        </StackPanel>
    </s:HeaderManager.Header>

</UserControl>

The second solution is to move the HeaderManager class into a different class library, so in a different assembly, and this is the best approach because let you use the attached property more naturally, without incurring in that compile error even if you declare the property just before the UserControl.Content, that is more natural for an Header property. For instance if you add the class into a class library called “RadicalTabRegion.Windows.Presentation” you will end-up with this XAML that just works fine:

<UserControl x:Class="RadicalTabRegion.Presentation.FirstView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:s="clr-namespace:RadicalTabRegion.Windows.Presentation.Regions.Specialized;assembly=RadicalTabRegion.Windows.Presentation"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">

    <s:HeaderManager.Header>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="First View"/>
            <CheckBox/>
        </StackPanel>
    </s:HeaderManager.Header>

    <Grid>
        <TextBlock Text="I'm the first view"/>
    </Grid>

</UserControl>

This example is a part of an implementation I’m making for Radical to implement a TabControlRegion, that is able to inject a simple UserControl into a TabItem (generated at runtime) and let the developer deeply customize the TabItem.Header simply declaring the attached property directly into the UserControl. This is under development right now, and it is just one of the possible solutions, if I will like it, I will post the entire code, and pull a request for integrating the new region directly into Radical.

Advertisements

C#: Enable Cookie Container on System.Net.WebClient (for Authentication)

As I told on the previous post the WebClient can be easily extended to add more functionality such in this case the Cookie Container, and let use it to authenticate versus the web pages that are protected using an authenticated cookie:

public class CookieWebClient : WebClient
{
	public CookieContainer CookieContainer { get; private set; }

	/// <summary>
	/// This will instanciate an internal CookieContainer.
	/// </summary>
	public CookieWebClient()
	{
		this.CookieContainer = new CookieContainer();
	}

	/// <summary>
	/// Use this if you want to control the CookieContainer outside this class.
	/// </summary>
	public CookieWebClient(CookieContainer cookieContainer)
	{
		this.CookieContainer = cookieContainer;
	}

	protected override WebRequest GetWebRequest(Uri address)
	{
		var request = base.GetWebRequest(address) as HttpWebRequest;
		if (request == null) return base.GetWebRequest(address);
		request.CookieContainer = CookieContainer;
		return request;
	}
}

And then you can use it as follow:

using (var client = new CookieWebClient())
{
	var loginData = new NameValueCollection
	{
		{ "UserName", "TestUser" },
		{ "Password", "MyPassword" }
	};

	// Login into the website (at this point the Cookie Container will do the rest of the job for us, and save the cookie for the next calls)
	client.UploadValues("http://domain.com/Account/LogOn", "POST", loginData);

	// Call authenticated resources
	client.UploadString("http://domain.com/ProtectedArea/MyProtectedResource", "POST", "some data");
}

CookieWebClient takes also a CookieContainer as parameter, to let you control the cookie container outside, and passing over different CookieWebclient instantiation.

How to use a Local Storage Resource

The Windows Azure Managed Library provides classes for accessing the local storage resource from within code that is running in a role instance.

You will just need to retrieve the full path of a named local storage, and then you can store any file you want. To retrieve the full path, you simple need this line of code:

RoleEnvironment.GetLocalResource("MainLocalStorage").RootPath