Silverlight Brass Tacks

Bill Reiss' Silverlight Ramblings
My upcoming Silverlight book for beginners Hello! Silverlight 2 with Dave Campbell, available online now!



Pages

Recent posts

Navigation

Archive

Blogroll

Tampa Divorce Lawyer

North of Tampa in Lutz, Florida. A Tampa Divorce Lawyer focusing on family, divorce, and real estate law.

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

Storing an object in the Tag property in Silverlight

Source code: http://www.bluerosegames.com/objecttags.zip

I've seen a few questions recently about the Tag property in FrameworkElements in Silverlight 2. The Tag property is defined to accept an object, but if you try to give it anything but a string it doesn't work. From what I understand, this will work with objects at some point, but there were some complexities that kept objects from being supported in this release.

Let's start with why do you care? Actually it can be very useful to associate an object with a FrameworkElement, I've used the Tag property on TreeViews or ListViews in Windows Forms to be able to easily reference the object that was used to populate the values in the node. You also may want to access a parent (or grandparent, or other ancestor) FrameworkElement in an event, like a mouse click would do something to another element. It can also be useful in games to associate a visual element with an object that holds its position and other data.

So if the Tag property only supports strings, how can we do this? My solution is to keep a dictionary of keys and associated objects, and then store the key in the Tag property.

First we need a dictionary with a string key.

using System;
using System.Windows;
using System.Collections.Generic;
 
public static class ObjectTagger
{
    static Dictionary<string, object> tagCollection = new Dictionary<string, object>();
}

Then to make it easier to set and retrieve the tag objects, I decided to use extension methods. Extension methods are a relatively new feature, introduced with the .Net Framework 3.0. Extension methods are a way for you to add methods to any class, even if it's not a partial class, and even if you don't have access to its code. So let's add a SetTagObject and GetTagObject method to the FrameworkElement class.

To declare an extension method, you declare a static method in any static class, make it public, and precede the first argument with the this keyword. The type of the first argument should be the type that you want to attach these extension methods to. Then the remaining arguments, if any, become the arguments to the method. Let's see how this looks in action. Since we already have a static class called ObjectTagger, we can put the extension methods in there:

 

using System;
using System.Windows;
using System.Collections.Generic;
 
namespace ObjectTags
{
    public static class ObjectTagger
    {
        static Dictionary<string, object> tagCollection = new Dictionary<string, object>();
 
        public static string SetTagObject(this FrameworkElement ele, object tag)
        {
            if (ele.Tag != null)
            {
                string oldTag = ele.Tag.ToString();
                if (tagCollection.ContainsKey(oldTag))
                {
                    tagCollection.Remove(ele.Tag.ToString());
                }
            }
            if (tag == null) return "";
            string s = Guid.NewGuid().ToString();
            tagCollection.Add(s, tag);
            ele.Tag = s;
            return s;
        }
 
        public static object GetTagObject(this FrameworkElement ele)
        {
            if (ele.Tag != null && tagCollection.ContainsKey(ele.Tag.ToString()))
            {
                return tagCollection[ele.Tag.ToString()];
            }
            return null;
        }
    }
}

In the SetTagObject method, we look to see if a Tag has already been set, and if so, we remove that entry from the dictionary. Then we generate a new GUID to create a unique key and add an entry to the dictionary. We then set the Tag property on the FrameworkElement to this key value.

To retrieve a Tag object, we simply looks to see if any key in the dictionary matches the Tag property of the FrameworkElement. If so, we'll return the object associated with the key. If not, we'll return null.

One important note. When you want to get rid of a FrameworkElement, make sure to call SetTagObject(null) to clear the entry in the dictionary before you get rid of it, otherwise you'll be leaking objects.

Now to test this out. First let's create a test class to use for the objects that will go in the object tags:

 
namespace ObjectTags
{
    public class TestObject
    {
        public string Value
        {
            get;
            set;
        }
    }
}

 

Then we need FrameworkElements to set the tag objects of. Here is the Page.xaml:

<UserControl x:Class="ObjectTags.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas x:Name="LayoutRoot" Background="White">
        <Rectangle Fill="Red" Width="100" Height="100" x:Name="rect" MouseLeftButtonUp="rect_MouseLeftButtonUp"/>
        <Rectangle Fill="Blue" Margin="200,0,0,0" Width="100" Height="100" x:Name="rect2" MouseLeftButtonUp="rect_MouseLeftButtonUp"/>
    </Canvas>
</UserControl>

 

Then for the code behind:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
 
namespace ObjectTags
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            TestObject t = new TestObject();
            t.Value = "some value";
            rect.SetTagObject(t); TestObject t2 = new TestObject();
            t2.Value = "some value 2";
            rect2.SetTagObject(t2);
        }
 
        private void rect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            TestObject t = ((sender as FrameworkElement).GetTagObject()) as TestObject;
            System.Windows.Browser.HtmlPage.Window.Alert(t.Value);
        }
    }
}

 

In the constructor, we set the tag object on the two Rectangles ro two different objects to show that we're accessing the correct one. Then in the MouseLeftButtonUp event, we'll access the tag object of the FrameworkElement and display its value.

When Silverlight gets true support for Tag objects, you'll be able to easily swap out these method calls with the corresponding property access.

Posted: Jul 18 2008, 15:30 by Bill Reiss | Comments (2) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under:

Related posts

Comments

geekswithblogs.net said:

pingbackPingback from geekswithblogs.net

Silverlight Cream for July 21, 2008 -- #330

# July 21 2008, 07:20

enetlive.net said:

pingbackPingback from enetlive.net

Silverlight news | Enetlive.net- Rich Internet Applications Blog

# July 21 2008, 18:11

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

said:

# July 03 2009, 22:16