Getting the name attribute of an MVC Html-helper output

It seems odd there is no standard way of getting the name of a control rendered with the Html helper extensions in MVC3 Razor. I saw some proposed solutions, but none of the actually worked e.g. in partial views or list binding.

This solution gets it right, but is nonetheless just another hack. You can easily modify the regular expression to get the id instead of the name.

@using System.Text.RegularExpressions
@{var regex = new Regex(
      @"\sname\=""(?'val'[^""]+?)""",
      RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.ExplicitCapture); }
:
@Html.TextBoxFor(m => m.Value)
<span data-valmsg-for="@(regex.Match(Html.TextBoxFor(m => m.Value).ToHtmlString())
                              .Groups[1]
                              .Value)"></span>

ProcessItems UX pattern

Here’s a general implementation for the common UX scenario where a items in a list should be processed, and that processing might fail, so reprocessing might be needed for those items. This used e.g. when copying a bunch of files or collecting data from loosely connected distributed systems.

/// <summary>
/// General UX process pattern that processes some items sequencially, while
/// reporting progress. Should items fail processing, the caller can retry
/// processing those.
/// </summary>
/// <typeparam name="T">Item type</typeparam>
/// <param name="items">Collection of items to process</param>
/// <param name="processItem">Callback to process an item. The second parameter is for reporting 
/// progress in completion percentage for that item.</param>
/// <param name="askRetry">Callback to determine if failed items should be reprocessed</param>
/// <param name="progress">Callback to report progress in percentage</param>
/// <returns></returns>
IEnumerable<T> ProcessItems<T>(
    IList<T> items,
    Action<T, Action<int>> processItem,
    Func<IEnumerable<KeyValuePair<T, Exception>>, bool> shallRetry,
    Action<int> progress)
{
    int count = items.Count;
    var completed = new HashSet<T>();
    bool eject = false;
    do
    {
        progress(0);
        var failures = new Queue<KeyValuePair<T, Exception>>();
        for (int i = 0; i < count; i++)
        {
            T item = items[i];
            if (!completed.Contains(item))
            {
                try
                {
                    processItem(item, p => progress(p / count + i * 100 / count));
                    completed.Add(item);
                }
                catch (Exception e)
                {
                    failures.Enqueue(new KeyValuePair<T, Exception>(item, e));
                }
            }
            progress((i + 1) * 100 / count);
        }
        if (failures.Any())
        {
            eject = !shallRetry(failures);
        }
    }
    while (completed.Count < count && !eject);
    return completed;
}

Generic Iterator

I just generalized the iterator implemented in my previous blog post to non-recursively iterate any object tree.

static IEnumerable<T> Iterate<T>(T rootNode, Func<T, IEnumerable<T>> getChildren)
{
    var stack = new Stack<T>();
    stack.Push(rootNode);
    while (stack.Count > 0)
    {
        T node = stack.Pop();
        yield return node;
        IEnumerable<T> childNodes = getChildren(node);
        foreach (var childNode in childNodes)
            stack.Push(childNode);
    }
}

Custom DropDown Control

Extensibility is not the strongest feature of Windows Forms. I recently required a custom drop down control like the common color picker or date picker control. I started out with http://www.logresource.com/Question/1080206/showall/ and modified it to properly handle usercontrols and keyboard control.

// based on http://www.logresource.com/Question/1080206/showall/
internal class PopupComboBox : ComboBox, IMessageFilter
{
    // http://www.woodmann.com/fravia/sources/WINUSER.H
    const int WM_KEYDOWN = 0x0100;
    const int WM_SYSKEYDOWN = 0x0104;
    const int WM_LBUTTONDOWN = 0x0201;
    const int WM_LBUTTONDBLCLK = 0x0203;

    const int VK_UP = 0x26;
    const int VK_DOWN = 0x28;
    const int VK_ESC = 0x1b;

    readonly Form m_containerForm = new Form()
    {
        FormBorderStyle = FormBorderStyle.FixedToolWindow,
        ShowInTaskbar = false,
        ControlBox = false,
        StartPosition = FormStartPosition.Manual,
    };
    Control m_popupControl;

    public PopupComboBox()
    {
        Items.Add(string.Empty);
        SelectedIndex = 0;
        DropDownStyle = ComboBoxStyle.DropDownList;
        m_containerForm.Deactivate += (sender, e) => HideUserControl();
    }        

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Localizable(false)]
    public new ComboBox.ObjectCollection Items // hide from serializer
    {
        get { return base.Items; }
    }

    public Control PopupControl
    {
        get
        {
            return m_popupControl;
        }
        set
        {
            if (m_popupControl != null)
            {
                m_popupControl.Parent = null;
            }
            m_popupControl = value;
            if (m_popupControl != null)
            {
                m_popupControl.Dock = DockStyle.Fill;
                m_containerForm.Controls.Add(m_popupControl);
            }
        }
    }

    protected override void WndProc(ref Message m)
    {
        if (PopupControl == null)
            base.WndProc(ref m);
        if ((m.Msg == WM_LBUTTONDOWN) || (m.Msg == WM_LBUTTONDBLCLK))
        {
            if (DroppedDown)
                HideUserControl();
            else
                ShowUserControl();
        }
        else if (m.WParam.ToInt32() == VK_DOWN // DOWN KEY PRESSED
                && (m.Msg == WM_SYSKEYDOWN
                && m.LParam.ToInt32() == 0x21500001
                || m.Msg == WM_KEYDOWN
                && m.LParam.ToInt32() == 0x1500001)
            )
        {
            if (!DroppedDown)
                ShowUserControl();
        }
        else
        {
            base.WndProc(ref m);
        }
    }

    public bool PreFilterMessage(ref Message m)
    {
        if (PopupControl == null)
            return false;
        // intercept ESC key or ALT+UP
        else if (m.Msg == WM_KEYDOWN
            && m.WParam.ToInt32() == VK_ESC
            && m.LParam.ToInt32() == 0x10001
            || m.Msg == WM_SYSKEYDOWN
            && m.WParam.ToInt32() == VK_UP
            && m.LParam.ToInt32() == 0x21480001)
        {
            HideUserControl();
            return true;
        }
        else
        {
            return false;
        }
    }

    protected virtual void ShowUserControl()
    {
        if (!Visible || m_containerForm.Visible)
            return;
        m_containerForm.BackColor = BackColor;
        m_containerForm.Font = Font;
        m_containerForm.ForeColor = ForeColor;
        m_containerForm.Bounds = new Rectangle(
            Parent.PointToScreen(new Point(Left, Bottom)),
            new Size(Math.Max(PopupControl.Width, Width), PopupControl.Height));
        m_containerForm.Show(this);
        OnDropDown(EventArgs.Empty);
        Application.AddMessageFilter(this);// start listening for clicks
    }

    protected virtual void HideUserControl()
    {
        if (!m_containerForm.Visible)
            return;
        Application.RemoveMessageFilter(this);
        OnDropDownClosed(EventArgs.Empty);
        m_containerForm.Hide();
        Items[0] = PopupControl.Text;
        Focus();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing && PopupControl != null)
        {
            m_containerForm.Dispose();
        }
        base.Dispose(disposing);
    }
}

VirtualBox Config Workaround

I have been having some trouble with VirtualBox since I upgraded some time ago. Every time I close a VM VirtualBox places an invalid character (for an XML-parser anyway) in the config file, rendering the file unreadable for VirtualBox the next time I want to start it.
So I did this naïve Python script to fix it.

filename = "/home/user/VirtualBox VMs/MyMachine/MyMachine.vbox"
istr = '"/VirtualBox/GuestAdd/VersionEx"'
jstr = 'value="'
kstr = '"'

fi = open(filename)
s = fi.read()
fi.close()

i = s.index(istr) + len(istr)
j = s.index(jstr, i) + len(jstr)
k = s.index(kstr, j)
t = s[:j] + s[k:]

fi = open(filename, "w+")
fi.write(t)
fi.close()
Follow

Get every new post delivered to your Inbox.