RSS Feed

Custom ListBox – using Attributes to create dynamic Controls

This is the Control I created for my mini project TimeIsMoney. It is a simple ListBox with a “complex item” creation logic . NewItemBox which is used to create new Item is modifying itself based on the Object type. The Control is used to create “TaskBins” which are containers of Tasks. The main TaskBin is our “UnsortedBin”. Here we have items waiting to be sorted. Then we can add multiple Bins which are used to store the sorted Items

What is it ?


The control contains 1 ListBox and 2 Buttons.

By clicking the add button a new NewItemBox Appears with dynamically created controls.

NewItemBox It analyzes the properties of the class and creates new controls. By creating a new property in TaskBin the Control will create a new control. By this behavior I don’t have to worry about changes.

How Does it Work ?

Everything is based on properties decorated by the attributes. Let’s take a look at TaskBin class

    [Serializable]
    public class TaskBin : IEditable;TaskBin;
    {
        [EditableDialogBox]
        public string Address { get; set; }
        [EditableTextBox]
        public string Name { get; set; }

        public string DisplayMember
        {
            get
            {
                return "Name";
            }
        }

        #region IEditable<TaskBin> Members

        public  TaskBin CreateFromString(string stringObject)
        {
            string[] datas = stringObject.Split(';');

            return new TaskBin() { Address = datas[1], Name = datas[3] };
        }

        #endregion
    }

As you can see Address and Name property are decorated by EditableDialogBox and EditableTextBox Attribute [This is a simple class inheriting from the Attribute class]

    public class EditableTextBox : Attribute
    {
    }

    public class EditableDialogBox : Attribute
    {
    }

Those are empty classes. You can add some logic and behaviors to them.I m just using them to mark the properties which will be represented as a TextBox in the NewItemBox. Every property with an [Editable…..] attribute will have a textbox and user will be able to edit it .The displayMember proeprty doesn’t have an Attribute so it wont available for the user. It is used for DataBinding to set the displayed property name on the ListBox].

To check if the property has a specific attribute you need to use the Reflection mechanism.

            foreach (PropertyInfo prop in type.GetProperties())
            {
                //Checking if property is decorated with EditableProperty Attribute
                if (prop.IsDefined(typeof(EditableTextBox), false))
                {
                    newBox.CreateTextBox(prop.Name);
                }
                else if(prop.IsDefined(typeof(EditableDialogBox),false))
                {
                    newBox.CreateFileDialogButton(prop.Name,"");
                }

            }

}

Using reflection we are iterating through the properties of the Object and checking if they are decorated by an Attribute. To perform this check i m using the IsDefined method. There are only two attributes right now. Each of them generates different logic in the NewItemBox.

      	  public void CreateTextBox(string name)
         {
            	this.Controls.Add(new Label() { Text = name, Location = new Point(0, y) });
            	this.Controls.Add(new TextBox() { Location = new Point(0, y + 20) });
            	y += 40;
          }

        public void CreateFileDialogButton(string name, string filter)
        {
            this.Controls.Add(new Label() { Text = name, Location = new Point(0, y) });

      	      TextBox dialogText = new TextBox();
      	      dialogText.Click += new EventHandler(dialog_Click);
      	      dialogText.Location = Location = new Point(0, y + 20);
      	      this.Controls.Add(dialogText);
      	      y += 40;
      	  }

Property with EditableTextBox attribute generates a simple Label with its name and TextBox. EditableDialogBox generates label, textbox and creates an Event Handler handled by the click Event. „dialog_Click” event summons the FileDialogBox which is used to set The TaskBin physical path.

We have a code to create a dynamic edit box but how to pass this data further to our main lists. To do this I created I mechanism which converts all the data from the controls to the string.

	     public string GenerateStringObject()
   	     {
               string returnString = String.Empty;

    	        foreach (Control c in Controls)
    	        {
   	             returnString += String.Format("{0};", c.Text);
   	         }

   	         return returnString;
  	      }

;Class which is on the list, implements an IEditable Interface.

   	     #region IEditable<TaskBin> Members

   	     public  TaskBin CreateFromString(string stringObject)
   	     {
  	          string[] datas = stringObject.Split(';');

  	          return new TaskBin() { Address = datas[1], Name = datas[3] };
 	       }

 	       #endregion

How can I use it ?


In MainForm initialize the control with DataSource and Object type. Control saves changes directly to the specified DataSource

this.complexListBox.SetData(set.Lists,typeof(TaskBin));

 


Reloading ListBox DataSource

My Little project TimeIsMoney is participating in a contest by Maciej Aniserowicz.

At the moment it’s really simple tool used to help me with collecting and sorting various tasks.

I’m doing this just for fun. This contest will be a great motivation and good way to promote this blog 😛

So what’s interesting in the code right now ?

1. ListBox DataSource Reloading

    public static class Extension
    {
        public static void eReloadDataSource(this ListBox listbox)
        {
            string s = listbox.DisplayMember;
            Object obj = listbox.DataSource;
            listbox.DataSource = null;
            listbox.DataSource = obj;
            listbox.DisplayMember=s;
        }
    }

This Extension method reloads the Data source of a ListBox. I know it looks stupid but it works :D.
Let’s take a look in Reflector …. hmm

public object DataSource
{
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    get
    {
        return this.dataSource;
    }
    set
    {
        if (((value != null) && !(value is IList)) && !(value is IListSource))
        {
            throw new ArgumentException(SR.GetString("BadDataSourceForComplexBinding"));
        }
        if (this.dataSource != value)
        {
            try
            {
                this.SetDataConnection(value, this.displayMember, false);
            }
            catch
            {
                this.DisplayMember = "";
            }
            if (value == null)
            {
                this.DisplayMember = "";
            }
        }
    }

So by assigning null value i m calling the SetDataConnection(,,,) which reloads the data. Also you can easily see that we have to reasssign DisplayMember value beacuase it is set to an Empty String at the end.

2. GlobalKeyHook

To catch the key press events i am using slightly modified code from link . I made a little tweak to enable the special keys combinations , so you can catch the alt+ctrl+b sequention.

That’s all for now ;] If you want to check the code it is on the GitHub.


Edytowalny ListBox

Dużo czasu spędzam ostatnio przy mini projekcicku [więcej info wkrótce]. Potrzebowałem kontrolki listboxa ale z edytowalnymi polami.
Jako że w .necie taka kontrolka nie jest dostępna standardowo to postanowiłem “wyciosać” własną. .Net daje nam spore pole do popisu jeżeli chodzi o tworzenie własnych “customowych” kontrolek.

By tego dokonać będziemy potrzebować dwóch nowych kontrolek.

  • TextBoxa  który będzie komunikował się z danymi ListBoxa
  • ListBoxa wykorzystującego tego TextBoxa

Read the rest of this entry »