Jeff's Blog

The day to day today

We found ourselves in need of text that was vertical. No, not rotated 90°, but rather top-to-bottom.

So without further adieu, here is some code to accomplish the task. I’ll also upload a sample project that uses the two extension methods and shows it in action.

using System;
using System.Drawing;
 
namespace VText
{
    public static class VTextExtensions
    {
        public static void DrawVerticalString(this Graphics graphics, string text, Font font, Brush textBrush, StringFormat format, Rectangle layoutRectangle)
        {
            if (!string.IsNullOrEmpty(text))
            {
                SizeF theSize = graphics.MeasureVerticalString(text, font);
                Rectangle rect = new Rectangle(layoutRectangle.X, layoutRectangle.Y, layoutRectangle.Width, font.Height);
                if (format.LineAlignment == StringAlignment.Far)
                {
                    rect.Y = layoutRectangle.Y + (layoutRectangle.Height - (int)Math.Ceiling(theSize.Height));
                }
                else if (format.LineAlignment == StringAlignment.Center)
                {
                    rect.Y = layoutRectangle.Y + (layoutRectangle.Height / 2) - (int)Math.Ceiling((theSize.Height / 2));
                }
                for (int i = 0; i < text.Length; i++)
                {
                    SizeF size = graphics.MeasureString(text.Substring(i, 1), font);
                    rect.Height = (int)Math.Ceiling(size.Height);
                    graphics.DrawString(text.Substring(i, 1), font, textBrush, rect, format);
                    rect.Y += rect.Height;
                }
            }
        }
 
        public static SizeF MeasureVerticalString(this Graphics graphics, string text, Font font)
        {
            SizeF size = new SizeF(0, 0);
            if (!string.IsNullOrEmpty(text))
            {
                for (int i = 0; i < text.Length; i++)
                {
                    SizeF tmp = graphics.MeasureString(text.Substring(i, 1), font);
                    size.Height += (int)Math.Ceiling(tmp.Height);
                    size.Width = (int)Math.Ceiling(Math.Max(size.Width, tmp.Width));
                }
            }
            return size;
        }
    }
}

This is what it should look like when you run it:

Vertical Text Demo

Vertical Text Demo

Here is the sample project: VText Solution


Tags: ,
category Development

Part I

An example

For any of you out there that have done any significant amount of work building winforms with Visual Studio, you have probably realized that the designer is pretty smart when you have a component with a property of a given type that exists on the design surface. Let’s trump up an example.

Let’s say that you have some component on your design surface called a Worker. Consider that worker does some amount of measurable work and while it is doing said work it has the ability to update a ProgressBar. Therefore, Worker might look something like this:

public partial class Worker : Component
{
    //Generated code has been removed.  Download sample solution for full listing
    public ProgressBar ProgressBar
    {
        get;
        set;
    }
 
    public void DoWork()
    {
        if (ProgressBar != null)
        {
            ProgressBar.Minimum = 0;
            ProgressBar.Maximum = 100;
        }
 
        for (int i = 0; i &lt;= 100; i++)
        {
            if (ProgressBar != null)
                ProgressBar.Value = i;
            System.Threading.Thread.Sleep(100);
            Application.DoEvents();
        }
    }
}
Toolbox

Figure 1 - The Worker Component in the Toolbox

Now, since Worker is a subclass of Component it will show up in the Visual Studio Toolbox [Figure 1]. Keep in mind that Components do not have a visual representation in the designer. Instead, they will be placed on the designer surface in the components area at the bottom of the Design screen [as shown in Figure 2].

DesignScreen
Figure 2 – The Design Surface and Property Grid

Now that Worker is on the design surface, it may be treated like any other component. It is now subject to having it’s public properties localized, edited by Visual Studio’s property grid, and being instantiated and initialized in the beloved InitializeComponent() method. We can also see in Figure 2 that the ProgressBar property on Worker has been assigned to progressbar1. As you have probably guessed, progressBar1 is in instance of the progress bar seen on the design surface just under the button. In this instance, Visual Studio was nice enough to know what we wanted and only showed us the instances of ProgressBar on the design surface when we pressed the drop-down to choose.

Now we simply hook up the button to call Worker’s DoWork() method and we have a working example.

    public partial class Form1 : Form
    {
        //generated code has been removed.
 
        /*
         * This is generated by the designer when you use the events panel to handle
         * the button's click event.  You need only fill in the body.
         */
        private void doWorkButton_Click(object sender, EventArgs e)
        {
            worker.DoWork();
        }
    }

DesignerExample-Part-1.zip

Part II

A problem

In Part I we saw a nice easy example of how to make a component that contains a reference to a single instance of some object on the design surface and make use of it. That is great, but I find that many times in development and in life that one is not enough.

DesignMoreProgressBars

Figure 3 - More ProgressBars added to the design surface

Continuing with our Worker/ProgressBar example lets make a small change. Lets say for whatever reason we have several ProgressBars on our design surface. Perhaps we have a tab control or some other hidden elements that will also show progress at various times. To keep it simple we will simply add a few more Progress Bars onto our form’s design surface. This is easy enough to accomplish with a couple of clicks.

Obviously, this isn’t enough. Our Worker class can only handle one instance of ProgressBar, yet we new have four instances that we want to reflect the worker’s progress. Simple enough, we will simply change the property in Worker from a single instance of ProgressBar to an array of them.

Now our listing for Worker looks like this:

public partial class Worker : Component
{
    //Generated code has been removed.  Download sample solution for full listing
 
    public ProgressBar[] ProgressBars
    {
        get;
        set;
    }
 
    public void DoWork()
    {
        if (ProgressBars != null)
        {
            foreach (ProgressBar bar in ProgressBars)
            {
                bar.Minimum = 0;
                bar.Maximum = 100;
            }
        }
 
        for (int i = 0; i &lt;= 100; i++)
        {
            if (ProgressBars != null)
                foreach(ProgressBar bar in ProgressBars)
                    bar.Value = i;
 
            System.Threading.Thread.Sleep(100);
            Application.DoEvents();
        }
    }
}

Alright, now all that is left to do is go back to the design surface, click on our instance of worker and use the property grid to assign our four new progress bars. Unfortunately this next part is where most people lose their steam and give up on building designer-compatible controls.

As seen in Figure 4, we no longer have a drop-down for our ProgressBars property on our worker. Now we have a button with an ellipsis on it. Clicking this button results in a dialog box that may be familiar. This dialog is the default array/collection editor. Used for things like the columns in a ListView or the default items in a TreeView. This editor allows you to make new instances of objects and sets your property with the results.

CollectionEditor

Figure 4 - The default collection editor

This is exceptionally handy when your peg is square and the hole is sqare. Great tool for using the designer to add items to a component that will automatically participate in localization and the like. In our example however, we don’t want to make new progress bars. We want to use the four that we have sitting right there on our design surface.

DesignerExample-Part-2.zip

Part III

A solution

It is clear now that we can’t use the default collection editor as the means to our end.  Therefore we must replace it with something a bit more custom.  That being the case, let us make a custom editor for selecting multiple progress bars that exist on our design surface.

Undoubtedly you have seen various custom editors already.  They range from the anchor editor, to the dock editor, to the color editor.  This list goes on.  All of these editor are subclasses of the UITypeEditor class.  This base class provides us with the methods to override to make a visual editor that works inside visual studio.

Our requirements tell us that we need to select multiple things from a list.  It seems to me that an old fashoned ListBox would work quite nicely for such a task.  We’ll keep this in mind as we build our custom type editor.

I’m a pretty big fan of descriptive naming, so I’m going to call it ProgressBarArrayEditor.  So, we will make a class called ProgressBarArrayEditor and make sure it is a subclass of UITypeEditor [System.Drawing.Design].  For this example, we will only be overriding two methods.  They are GetEditStyle(…) and EditValue(…).

The method GetEditStyle(…) is used to tell visual studio what kind of custom editor you are making.  Times such as this one we will be using the DropDown style.  This is not to be confused with a ComboBox.  This simply means that a “down-arrow-button” will be displayed when getting ready to edit the property.  When this button is clicked, your custom editor will be sized and positioned directly under (or over depending on space) the property value in the grid control.

Another possible value is Modal.  This value is used when your custom type editor is more complex and will be hosted as a window that requires user input to close.  The default collection editor is an example of this, as is the ImageTypeEditor.  Using this value will result in a button with an ellipsis on it instead of the down-arrow-button.  Clicking this button will launch your custom editor.

public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
    return UITypeEditorEditStyle.DropDown;
}

The other method we will concern ourselves with is the EditValue(…) method. This is where we actually edit the array of progress bars. I’m going to break this method into small steps, then present the final listing at the end.

Step 0. Initialize a ListBox to use as our custom editor

private void InitListBox()
{
    if (_listBox == null)
    {
        _listBox = new ListBox();
        _listBox.Dock = DockStyle.Fill;
        _listBox.BorderStyle = BorderStyle.None;
        _listBox.DisplayMember = "Name";
        _listBox.SelectionMode = SelectionMode.MultiExtended;
    }
    else
    {
        _listBox.Items.Clear();
     }
}

Remember how I said that I thought a ListBox might do the trick? We’re going to use one here. However, you could use anything you want here. That is sort of the point of all this designer extension mumbo jumbo after all!

Step 1. Retrieve all the instances of ProgressBar on the design surface

InitListBox();
 
IReferenceService refService = (IReferenceService)provider.GetService(typeof(IReferenceService));
 
//this is where we get all the references to instances of ProgressBars that
//exist on our design surface
object[] objects = refService.GetReferences(typeof(ProgressBar));
 
_listBox.Items.AddRange(objects);

This step has now located any and all instances of ProgressBar that we had on our design surface. Then it added them all into our ListBox. It is easy enough to see how we could modify this code to find multiple types or objects with certain properties. Limitless possibilities here.

Step 2. Be sure to reflect a value that already exists

//this block of code is used to show which progress bars are currently assigned to
//the Worker in question.  It will set them to selected in the ListBox
if (value != null)
{
    ProgressBar[] currentValue = (ProgressBar[])value;
    foreach (ProgressBar pbar in currentValue)
    {
        int idx = _listBox.Items.IndexOf(pbar);
        if (idx &gt;= 0)
            _listBox.SetSelected(idx, true);
     }
}

Many times we will be modifying a collection of ProgressBars that already exists. Be sure to reflect what has already been selected. In this instance, we will have them pre-selected in the ListBox when our custom editor drops down.

Step 4. Trigger the custom control to open so we can edit

IWindowsFormsEditorService edSvc =
    (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
 
//this is similar to the ShowDialog(...) method on a form.
//the code below this line won't execute until after the drop-down is collapsed.
edSvc.DropDownControl(_listBox);

Here we have simply told the editor service that we have done our initialization and we are ready to select the new values. This causes the drop down to open. The drop down contains our ListBox which has been populated with our values and is ready for modification. Using the control-key and the mouse, one may select multiple instances from the list.

Step 5. Tell the designer that we are done

List
 progressBars = new List
();
foreach (object o in _listBox.SelectedItems)
    progressBars.Add(o as ProgressBar);
 
return progressBars.ToArray();

Now that we are done editing we will package the new list of progress bars into an array and send them back to visual studio so that it can generate the appropriate code in the InitializeComponent(…) method.

Full Listing

public class ProgressBarArrayEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.DropDown;
    }
 
    private ListBox _listBox = null;
 
    private void InitListBox()
    {
        if (_listBox == null)
        {
            _listBox = new ListBox();
            _listBox.Dock = DockStyle.Fill;
            _listBox.BorderStyle = BorderStyle.None;
            _listBox.DisplayMember = "Name";
            _listBox.SelectionMode = SelectionMode.MultiExtended;
        }
        else
        {
            _listBox.Items.Clear();
        }
    }
 
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        InitListBox();
 
        IReferenceService refService = (IReferenceService)provider.GetService(typeof(IReferenceService));
 
        //this is where we get all the references to instances of ProgressBars that
        //exist on our design surface
        object[] objects = refService.GetReferences(typeof(ProgressBar));
 
        _listBox.Items.AddRange(objects);
 
        //this block of code is used to show which progress bars are currently assigned to
        //the Worker in question.  It will set them to selected in the ListBox
        if (value != null)
        {
            ProgressBar[] currentValue = (ProgressBar[])value;
            foreach (ProgressBar pbar in currentValue)
            {
                int idx = _listBox.Items.IndexOf(pbar);
                if (idx &gt;= 0)
                    _listBox.SetSelected(idx, true);
            }
        }
 
        IWindowsFormsEditorService edSvc =
            (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
 
        //this is similar to the ShowDialog(...) method on a form.
        //the code below this line won't execute until after the drop-down is collapsed.
        edSvc.DropDownControl(_listBox);
 
        List
 progressBars = new List
();
        foreach (object o in _listBox.SelectedItems)
            progressBars.Add(o as ProgressBar);
 
        return progressBars.ToArray();
    }
}

We now have all that we need to appropriately edit our list of ProgressBar instances on our Worker instance. The very last thing to do is actually let the runtime know that we want to use a custom type editor. Luckily, this is a short and sweet attribute that can be placed on the property in question.

[Editor(typeof(ProgressBarArrayEditor), typeof(System.Drawing.Design.UITypeEditor))]
public ProgressBar[] ProgressBars
{
    get;
    set;
}
MultiProgressBarSelection

Figure 5 - The ability to select multiple ProgressBars

This is pretty self-explanatory. We are simply telling anybody that asks that we have a custom type editor that they should look for and use when offering visual access to this property.

Now with a rebuild, we should be able to go back into the designer and see the results similar to those showin in Figure 5.

Additionally we start it up, click “Do Work” and watch the progress bars the we selected go up, while the others stay inert. Download the zip file below and try out some variations. Try putting multiple workers on the design surface and hooking them up to different buttons. Try assigning the same progress bar to multiple workers. What happens? Are these types of issues avoidable? All things to think about when building these types of components.

DesignerExample Part 3

Going Forward

Hopefully this example can start to relieve some of the anxiety that comes along with extending designers and editors in Visual Studio. When built with care, Controls and Components can be very powerful tools for visually interacting with your code.

A well kept design surface will always be more maintainable than source code files and more and more of the user interface details (both visual and non-visual) can be peeled away from the logic underneath.

In the future, I hope to bring more posts about not only custom editors, but custom designers and interacting with the design surface itself. Again, powerful tools to visually construct an application and avoid the mess of hand-coding user interfaces.

If you have any questions or comments, I’d love to hear them!


Tags: , , , ,
category Visual Studio Designer
© 2009 "White Angles" theme designed by ATILLUS - Content: © 2009 Jeff Baker