views:

27

answers:

0

I have a list-type UserControl (like a ListBox). The items inside the control are another complex UserControl containing a few other controls (ComboBox, TextBox, etc). I'm wondering what the preferred or best method would be to override to draw/layout the child controls. I basically want to trigger this method any time the list changes.

I originally had a RedrawItems method that I just called whenever I needed to redraw which added or removed Controls from the Controls collection. But it was getting triggered too early in the lifecycle of the code from some of the designer code. Now I've switched to overriding OnLayout and doing my stuff there. I call PerformLayout when I want to trigger a redraw, such as when the DataSource property changes or when it fires a changed event. Is OnLayout the best place for this?

Here is the code:

[ComplexBindingProperties("DataSource")]
public partial class CustomList : UserControl
{
    private object _dataSource;
    private CustomListItem _newRow;
    public CustomList()
    {
        InitializeComponent();
    }

    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        _newRow = new CustomListItem();
        Controls.Add(_newRow);
    }


    public object DataSource
    {
        get { return _dataSource; }
        set {
            bool register = _dataSource != value;
            if (_dataSource != null && _dataSource != value)
            {
                UnregisterDataSource(_dataSource);
            }
            _dataSource = value;
            if (_dataSource != null)
                RegisterDataSource(_dataSource);
            PerformLayout();
        }
    }

    public CustomListItem ItemTemplate
    {
        get { return _newRow; }
    }

    protected override void OnLayout(LayoutEventArgs e)
    {
        base.OnLayout(e);

        int ctrlCount = this.Controls.AsEnumerable().OfType<CustomListItem>().Count();
        ctrlCount--; // subtract 1 for the add row
        var ds = this.DataSource as System.Collections.IList;
        int itemCount = ds == null? 0 : ds.Count;
        int maxCount = Math.Max(ctrlCount,itemCount);
        if (maxCount == 0)
            return;
        this.SuspendLayout();
        // temporarily remove the template
        Controls.RemoveAt(Controls.Count-1);
        for (int i = 0; i < maxCount; i++)
        {
            CustomListItem item;
            if (i >= itemCount)
            {
                Controls.RemoveAt(i);
            }
            else
            {
                if (i >= ctrlCount)
                {
                    item = ItemTemplate.Copy();
                    this.Controls.Add(item);
                    item.Location = new Point(0, item.Height * i);
                    item.TabIndex = i + 1;
                    item.ViewMode = true;
                }
                else
                {
                    item = (CustomListItem) Controls[i];
                }
                item.Data = ds[i];
            }
        }
        this.Controls.Add(ItemTemplate);
        ItemTemplate.Location = new Point(0, ItemTemplate.Height * maxCount);
        ItemTemplate.TabIndex = maxCount + 1;
        this.ResumeLayout(true);
    }

    private void RegisterDataSource(object dataSource)
    {
        IBindingList ds = dataSource as IBindingList;
        if (ds != null)
        {
            ds.ListChanged += new ListChangedEventHandler(DataSource_ListChanged);
        }
    }

    void DataSource_ListChanged(object sender, ListChangedEventArgs e)
    {
        switch (e.ListChangedType)
        {
            case ListChangedType.ItemAdded:
                PerformLayout();
                break;
            case ListChangedType.ItemChanged:
                break;
            case ListChangedType.ItemDeleted:
                PerformLayout();
                break;
            case ListChangedType.ItemMoved:
                PerformLayout();
                break;
            case ListChangedType.Reset:
                PerformLayout();
                break;
            default:
                break;
        }
    }

    private void UnregisterDataSource(object dataSource)
    {
        IBindingList ds = dataSource as IBindingList;
        if (ds != null)
        {
            ds.ListChanged -= new ListChangedEventHandler(DataSource_ListChanged);
        }
    }


}