How to make Multilanguage supported Category and Description Attributes?

If you have written any .NET components you must be familiar with Category and Description attributes. We use Category attribute to identify the property’s category and Description attribute is for the short description of that property. But because of the nature of Attributes in .NET we cannot pass any parameter to these attributes for Multilanguage support.

[Category(Properties.Resources.MyCategory)]
[Description(Properties.Resources.MyDescription)]
public int MyProperty { … }

This kind of usage is not allowed for .NET, because we are only allowed to give constant values to the attributes constructor.  So how can we use these attributes as Multilanguage?

Solution is simple;
Deriving two new attributes from Category and Description attributes will handle our problem. Because PropertyGrid uses PropertyDescriptors which resolves properties description and category from these two attributes, so if we derived our new attributes from DescriptionAttribute and CategoryAttribute this will also continue to work for PropertyGrid and all kind of grids which uses PropertyDescriptors.

Our new attributes will be;

[AttributeUsage(AttributeTargets.All)]
internal class SRDescriptionAttribute :
DescriptionAttribute
{
    bool ps = false
;

    public SRDescriptionAttribute(string
description) : base(description)
    {    }

    public override string
Description
    {
       
get
       
{
            if
(!ps)
            {
                ps = true
;
                // Here we read the multilanguaged text from the resources
                // by using given Description text as Key
                base.DescriptionValue = Properties.Resources.ResourceManager.GetString(base
.Description);
            }
            return base
.Description;
        }
    }
}


[AttributeUsage(AttributeTargets.All)]
internal sealed class SRCategoryAttribute :
CategoryAttribute
{
    public SRCategoryAttribute(string
category) : base(category)
    {  }

    protected override string GetLocalizedString(string
value)
    {
        // Here we read the multilanguaged text from the resources
        // by using given Category text as Key
        return Properties.Resources
.ResourceManager.GetString(value);
    }
}



Now we can use these attributes. The only thing that we have to do is giving the resource names as the description and category to the attributes.

Usage:
[SRCategory(“Cat_MyCategory”)]
[SRDescription(“Des_MyProperty”)]
public int MyProperty { … }
* Cat_MyCategory and Des_MyProperty are the names of resources in the resource file.

Actually as you see, for the Category attribute GetLocalizedString method and for the Description attribute Description property were both was written as virtual, which means that derived classes can override them (They wish Smile so).


How to create TFS WorkItems programmatically

If you are using Team Foundation Server with Visual Studio, it will be useful for you to integrate your projects bug system with the TFS Work Items system. You can write a simple module for your program which will handles the exceptions and collects needed information and send them to the TFS.

So how can we do something like that? First you have to write an error management module which will handles exceptions, errors, etc. and generates understandable information from them. After generating this information lastly you have to send this information to the TFS.

In addition to this it will be helpful if you create your error information title unique in some cases to check that is this bug already entered as a Work Item Bug or not? For example you can combine type of the exception and last stack frame(s) which this error occurred in.

Anyway let us write a simple code which will enter our error as Work Item Bug to the TFS by programmatically. The code is so simple; you can also find more complicated codes on the web. 

const string USERNAME = "XXXX";
const string PASSWORD = "XXXX";
const string DOMAIN = "MyDomain";
const string TFS_SERVER = "http://mytfsserver:8080";
const string PROJECT = "LANCET";

protected void Page_Load(object sender, EventArgs e)
{
 string title = Request.QueryString["Title"];
 string description = Request.QueryString["Description"];
 string email = Request.QueryString["Email"];
 string force = Request.QueryString["Force"];
 if (string.IsNullOrEmpty(force))
  force = "1";

 try
 {
  NetworkCredential account = new NetworkCredential(USER_NAME, PASSWORD, DOMAIN);
  TeamFoundationServer server = new TeamFoundationServer(TFS_SERVER, account);
  Project project = null;

  server.Authenticate();
  WorkItemStore store = new WorkItemStore(server);
  project = store.Projects[PROJECT];
  if (project == null)
   throw new Exception("Project could not found");
  string wiql = "SELECT [System.Id], [System.Title], [System.Description] " +
     "FROM WorkItems " +
     "WHERE [System.TeamProject] = '" + PROJECT + "' " +
     "AND [System.Title] = '" + title + "'";
   
  WorkItemCollection collection = store.Query(wiql);
  StringBuilder tmp = new StringBuilder();
  tmp.Append(title);
  if (!string.IsNullOrEmpty(description))
  {
   tmp.Append("\r\n");
   tmp.Append("-----------------------------------------------------------------");
   tmp.AppendLine();
   tmp.AppendLine(description);
  }
  if (!string.IsNullOrEmpty(email))
  {
   tmp.AppendLine();
   tmp.Append("-----------------------------------------------------------------");
   tmp.AppendLine();
   tmp.AppendLine(email);
  }
  if (collection.Count == 0 || force == "1")
  {
   WorkItem item = new WorkItem(project.WorkItemTypes["bug"]);
   item.Title = title;
   item.AreaPath = @"CPM\Inbox";
   item.State = "Active";
   item.Description = tmp.ToString();
   item.Save();
  }
  else
  {
   WorkItem item = collection[0];
   item.Open();
   item.State = "Active";
   item.History += "\r\n\r\n" + DateTime.Now.ToString()
     + " ----------------------------------------------------\r\n" + tmp.ToString();
   item.Save();
  }
  Response.Write("SUCCESS - Bug successfully inserted to the TFS");
 }
 catch (Exception ex)
 {
  Response.Write("ERR-" + ex.Message);
 }
}

As you see here, first we try to connect to the TFS server with the given network credentials, then we search for the project which will own this bug. After that we are trying to search that is there any Work Item which has the same title (which is unique for us). If we find an existing entry than we re-activate that task and add a history to it. If we don’t find any entry for this title then we add a new Bug case for this. Actually as I said before the code is so simple, you can change it according to your necessity.

Finally we have to talk about one more thing. What if the machine which our program runs on and generates exception does not have any access privileges to the TFS server machine? Because of some security reasons you may don’t want to open your system. In this case you can use a Website to solve this problem. Create a local web page which takes needed information from Request and runs the code above. So you can call this web page from your program and everything will be worked fine.


Xenocode Postbuild is the powerful, reliable, and easy-to-use code protection and deployment solution for .NET developers.

Search

Calendar

<<  August 2008  >>
MonTueWedThuFriSatSun
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
View posts in large calendar

Disclaimer

© 2007 - 2008
Ozcan DEGIRMENCI
All rights reserved. The content can be used elsewhere given that the source is properly acknowledged.