FlagsEditor: Suport for <Enum> : uint

If an enum's underlying type is uint, FlagsEditor threw an invalidcast exception. I have been fixed it and upload new version of the FlagsEditor to the Server. You can download it from here...


How to permit multiple selections for Enum properties?

PropertyGrid will use default editor for the properties which’s type is enum. This default editor does not allow multiple selections even the Enum has Flags attribute.
I have been prepared a new Editor for this kind of properties. You can download the source codes from here

Download Source Codes


Our editor’s code is as follows;
internal class FlagsEditor : UITypeEditor
{
       FlagsEditorControl editor = null;

       public FlagsEditor()
       { }

       // our editor is a DropDown editor
       public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
       {
             return UITypeEditorEditStyle.DropDown;
       }

       public override object EditValue(ITypeDescriptorContext context, 
                                        IServiceProvider provider, object value)
       {
             // if value is not an enum than we can not edit it
             if (!(value is Enum))
                    throw new Exception("Value doesn't support");

             // try to figure out that is this a Flags enum or not ?
             Type enumType = value.GetType();
             object[] attributes = enumType.GetCustomAttributes(typeof(FlagsAttribute), true);
             if (attributes.Length == 0)
                    throw new Exception("Editing enum hasn;t got Flags attribute");

             // check the underlying type
             Type type = Enum.GetUnderlyingType(value.GetType());
             if (type != typeof(byte) && type != typeof(sbyte)
                    && type != typeof(short) && type != typeof(ushort)
                    && type != typeof(int)&& type != typeof(uint))
                    return value;

             if (provider != null)
             {
                    // use windows forms editor service to show drop down
                    IWindowsFormsEditorService edSvc = provider.GetService(
                                    typeof(IWindowsFormsEditorService))
                                  as IWindowsFormsEditorService;
                    if (edSvc == null)
                           return value;
                    if (editor == null)
                           editor = new FlagsEditorControl(this);
                    // prepare list
                    editor.Begin(edSvc, value);
                    // show drop down now
                    edSvc.DropDownControl(editor);
                    // now we take the result
                    value = editor.Value;
                    // reset editor
                    editor.End();
             }
             return Convert.ChangeType(value, type);
       }

}


In this code FlagsEditorControl is a UserControl which PropertyGrid hosts it in a drop down during the edit operation.
You can change your design by changing this Control.

To use this editor for a property, we have to write Editor Attribute to that property as the below;
// set editor of this property to our FlagsEditor
[Editor(typeof(FlagsEditor), typeof(UITypeEditor))]
public FileAttributes FlagsAttribute
{
       get { return _FlagsAttribute; }
       set { _FlagsAttribute = value; }
}

Something about CollectionEditor

Today I wrote a simple example which shows how we can use CollectionEditor of the .NET. In addition to this I also made a simple example which shows how to implement custom CollectionEditor which allows us to add more than one item to the collection.

Download Example Sources

Download Example

public class ExampleComponent : Component{
       ArrayListCollection _ArrayListItems = new ArrayListCollection();
       List<Button> _ListItems = new List<Button>();
       List<Control> _MultipleItems = new List<Control>();

 
      public ExampleComponent()
       { }

       [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
       [Description("Contains only Control objects in an ArrayList")]
       [Category("Behaviour")]
       public ArrayListCollection ArrayListItems
       {
             get { return _ArrayListItems; }
       }

       [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
       [Description("Contains only Button objects in a List<Button> collection")]
       [Category("Behaviour")]
       public List<Button> ListItems
       {
             get { return _ListItems; }
       }

       // custom editor attribute
       [Editor(typeof(CustomCollectionEditor), typeof(UITypeEditor))]
       [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
       [Description("Contains ListView, Button and CheckBox items in a List<Control> collection")]
       [Category("Behaviour")]
       public List<Control> MultipleItems
       {
             get { return _MultipleItems; }
       }

       public class ArrayListCollection : ArrayList
       {
             // CollectionEditor searches collection type's Index property and finds its property type.
             // It uses this type to add new items
             public new Control this[int index]
             {
                    get { return base[index] as Control; }
             }
       }

       // Custom collection editor.
       // by using this editor we can add ListView, Button and
       // CheckBoxes to the same collection
       public class CustomCollectionEditor : CollectionEditor
       {
             public CustomCollectionEditor()
                    : base(typeof(List<Control>))
             {}

             // override this method if you have to do custom initializing
             // operations. For example, if you have to use a constructor which
             // takes arguments you can create your own items here ...
             // or may be you have to do some initializing operations ...
             protected override object CreateInstance(Type itemType)
             {
                    if (itemType == typeof(CheckBox))
                    {
                           CheckBox checkbox = new CheckBox();
                           checkbox.Text = "My Text, hede";
                           return checkbox;
                    }
                    Control control = base.CreateInstance(itemType) as Control;
                    control.Text = "Other, hede";
                    return control;
             }

             // if you want to use a custom collection editor form,
             // you have to override this method and return your form here
             protected override CollectionEditor.CollectionForm CreateCollectionForm()
             {
                    return base.CreateCollectionForm();
             }

             // here you can return a text which will be appeared in the list
             // for the given item
             protected override string GetDisplayText(object value)
             {
                    Control control = value as Control;
                    return string.Format("{0} - {1}", control.GetType().Name, control.Text);
             }

             // we allow 3 types can be added to our collection
             protected override Type[] CreateNewItemTypes()
             {
                    return new Type[] { typeof(ListView), typeof(Button), typeof(CheckBox) };
             }
       }

}


Extending PropertyGrid: Adding custom PropertyTab and PropertyDescritors

Property Tab Example  Download Example
Most of the programmers who uses Visual Studio .NET knows PropertyGrid which is the main control in the Properties Window of the Visual Studio.

PropertyGrid’s default PropertyTab (System.Windows.Forms.PropertyGridInternal.PropertiesTab) shows properties and their current values of the given object. If we want to make some customizations we can use Browsable, DefaultValue, Description, Category, Localizable etc. attributes. For example you can hide a Property by adding Localizable(false) attribute to that property.

If you want to make more customization which you cannot do with these attributes you can create your own PropertyTab and add it to the PropertyGrid.  I made an example which filters properties according to my custom attribute(OzcanPropertyAttribute). Withal it shows hidden properties as a read-only property in the PropertyGrid. You can download example with the source codes here.

To create you custom PropertyTab for the PropertyGrid, you have to derive you class from PropertyTab class. Then you have to override its GetProperties method.
You can add your custom PropertyTab to your PropertyGrid like;

private class CustomPropertyTab : PropertyTab
{
    public
CustomPropertyTab()
    { }

    public override PropertyDescriptorCollection GetProperties(object component,
                                            Attribute
[] attributes)
    {
        PropertyInfo[] properties = component.GetType().GetProperties(
                                            BindingFlags.Instance | BindingFlags
.Public);
        List<PropertyDescriptor> filtereds = new List<PropertyDescriptor
>();
        PropertyDescriptorCollection propertyDescriptions = TypeDescriptor
.GetProperties(
                                                                component, attributes);
        Type myAttributeType = typeof(OzcanPropertyAttribute
);

        List<string> ignoreds = new List<string
>();
        for (int
i = 0; i < properties.Length; i++)
        {
            if (properties[i].IsDefined(myAttributeType, true
))
            {
                filtereds.Add(propertyDescriptions[properties[i].Name]);
            }
            else
            {
                ignoreds.Add(properties[i].Name);
            }
        }

        // now add a custom property descriptor to show hidden properties
        PropertyDescriptorCollection coll = new PropertyDescriptorCollection
(
                                                filtereds.ToArray());
        if (FormMain
.Instance._Settings.ShowHiddenProperties)
            coll.Add(new
CustomPropertyDescriptor(ignoreds.ToArray()));
        return
coll;
    }

    public override
Bitmap Bitmap
    {
        get
        {
            return
Properties.Resources.finance_16;
        }
    }

    public
override string TabName
    {
        get { return "Ozcan Properties Tab"
; }
    }
}

If you want to make more customization you can create your own PropertyDescriptors and create your own PropertyDescriptorCollection which contains these PropertyDescriptors. Check example source codes for how to implement custom PropertyDescriptors.

I think this is enough for now (it is 1:14 AM, i still have some other jobs to finish), in the future we can make more complicated example about this subject.


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.


Search

Calendar

<<  November 2008  >>
MonTueWedThuFriSatSun
272829303112
3456789
10111213141516
17181920212223
24252627282930
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.