1. 程式人生 > 實用技巧 >使用者可分類的列表框

使用者可分類的列表框

介紹 此控制元件允許使用者使用拖放方法對列表框項重新排序。它支援標準的Items屬性(帶有字串和自定義物件) 和DataSource屬性,如果資料來源實現了IList介面(即。BindingList)。 背景 web上有幾個允許使用者對列表框進行排序的程式碼片段,但大多數程式碼片段只支援字串列表(或任何其他硬編碼的列表)。 我的方法基於BFree在StackOverflow上的回答:http://stackoverflow.com/a/805267/540761。 為什麼本地的方法不正確? 只要我們只對字串(或者,更一般地,對一種特定型別)使用Items屬性,上面的連結中的程式碼就很簡單也很好。 有時,對字串以外的項使用資料來源(例如,對許多控制元件使用相同的項列表)是很好的。這也是一個好主意 獨立於專案型別的控制元件。為了實現這一點,我們將建立一個UserSortableListbox類,它繼承自System.Windows.Forms.ListBox。 讓事情變得簡單 我決定假設兩件重要的事情來保持程式碼的簡單: UserSortableListbox始終是使用者可排序的(沒有可能禁用拖放),唯一支援的選擇模式是SelectionMode. one。 這兩種功能都可以很容易地實現,稍後將對此進行描述。 拖拽 為了允許使用者使用拖放重新排序項,我們必須處理三個事件:重新排序的開始(MouseDown事件), 移動元素(拖放)和拖放專案(拖放)。 我們在MouseDown事件之後開始拖放機制: 隱藏,複製Code

protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    if (SelectedItem == null)
    {
        return;
    }
    sourceIndex = SelectedIndex;
    OnSelectedIndexChanged(e); //(*)
    DoDragDrop(SelectedItem, DragDropEffects.Move);
}

sourceIndex簡單地定義在類的某個地方: 隱藏,複製Code

private
int sourceIndex = -1;

這裡唯一需要解釋的是onselectedindexchange(e)。我們需要這個是因為 當處理MouseDown時,selectedindexchange不會啟動。 處理物品的移動很簡單: 隱藏,複製Code

protected override void OnDragOver(DragEventArgs e)
{
    base.OnDragOver(e);
    e.Effect = DragDropEffects.Move | DragDropEffects.Scroll;
}

現在,最有趣的部分。在發生拖放事件之後,我們就可以完成這項工作,並將拖放的專案移動到適當的位置。 下面是OnDragDrop方法的第一個版本: 隱藏,複製Code

protected override void OnDragDrop(DragEventArgs e)
{
    base.OnDragDrop(e);
    //(1)
    Point point = PointToClient(new Point(e.X, e.Y));
    int index = IndexFromPoint(point); //destination index
    //(2)
    if (index < 0) index = Items.Count - 1;
    
    //(3a)
    if (index > sourceIndex)
    {
        Items.Insert(index + 1, Items[sourceIndex]);
        Items.RemoveAt(sourceIndex);
    }
    //(3b)
    else
    {
        Items.Insert(index, Items[sourceIndex]);
        Items.RemoveAt(sourceIndex + 1);
    }
    //(4)
    SelectedIndex = index;
}

本程式碼的一些註釋: 我們沒有像OnMouseDown方法那樣的簡單方法來指示被刪除項的新索引。但是,我們可以使用繼承的IndexFromPoint方法, 這就得到了我們想要的東西。唯一要記住的是對e進行變換。X和e。Y座標到客戶端座標。在這一行中,我們必須決定如何處理將專案拖放到列表框中最後一個元素下面的情況(因為您不能將專案拖出列表框,這是唯一的情況 當用戶將一個條目放到最後一個條目下面時,IndexFromPoint將返回-1)。處理這種情況最直觀的方法是將目標索引設定為 列表中的最後一個索引。當我們有一個源和目標索引時,我們可以移動項。首先,我們通過在item中再次插入item [sourceIndex]來複制一個項, 然後我們去掉原來的那個。如果目標索引大於(低於)源,從sourceIndex刪除將影響目標索引, 所以我們插入的是index + 1。同樣,當目標索引小於(上面)源時,在位置索引處插入也會產生影響 sourceIndex,所以我們必須移除sourceIndex + 1。我們刪除了先前選中的項,所以是時候在它的新位置重新選擇它了。 我們重新建立了基本解決方案。惟一的優點是程式碼中不再有e.Data.GetData()。幸運的是,添加了資料來源支援 現在真的很簡單。我們只需要為DataSource和Items欄位找到一個通用的類(或介面),這樣我們就可以操作它的元素, 特別是提供Count、Insert和RemoveAt方法。Items具有ObjectCollection型別, 它實現了IList、ICollection和IEnumerable。因為IList介面是精確的 我們要搜尋的內容,我們可以假設資料來源將實現它,我們將建立一個名為items和replace的變數 OnDragDrop方法中的所有條目,它將完成任務並允許我們在UserSortableListbox中使用資料來源。 隱藏,複製Code

IList items = DataSource != null ? DataSource as IList : Items;

更多的功能 為了讓控制元件更有用,我們可以新增一個Reorder事件,當用戶移動一個專案時觸發該事件: 隱藏,複製Code

public class ReorderEventArgs : EventArgs
{
    public int index1, index2;
}
public delegate void ReorderHandler(object sender, ReorderEventArgs e);
public event ReorderHandler Reorder;

index1和index2是移動項的源和目標索引。這是完整的OnDragDrop方法, 包括資料來源支援和重新排序事件: 隱藏,複製Code

protected override void OnDragDrop(DragEventArgs e)
{
    base.OnDragDrop(e);
    Point point = PointToClient(new Point(e.X, e.Y));
    int index = IndexFromPoint(point);
    IList items = DataSource != null ? DataSource as IList : Items;
    if (index < 0) index = items.Count - 1;
    if (index != sourceIndex)
    {
        if (index > sourceIndex)
        {
            items.Insert(index + 1, items[sourceIndex]);
            items.RemoveAt(sourceIndex);
        }
        else
        {
            items.Insert(index, items[sourceIndex]);
            items.RemoveAt(sourceIndex + 1);
        }
        if (null != Reorder)
            Reorder(this, new ReorderEventArgs() { index1 = sourceIndex, index2 = index });
    }
    SelectedIndex = index;
}

保持實現簡單 正如我上面提到的,我們假設不能禁用拖放,只能禁用SelectionMode。在使用控制元件時,其中一個是可用的,因此我們應該隱藏AllowDrop 和SelectionMode,並在建構函式中設定正確的值: 隱藏,複製Code

[Browsable(false)]
new public bool AllowDrop
{
    get { return true; }
    set { }
}
[Browsable(false)]
new public SelectionMode SelectionMode
{
    get { return SelectionMode.One; }
    set { }
}
public UserSortableListBox() //this is the constructor
{
    base.AllowDrop = true;
    base.SelectionMode = SelectionMode.One;
}

當然,我們可以新增對那些剛剛禁用的屬性的支援。如果您想允許禁用移動項,您只需要進行檢查 OnMouseMove和do或don () DoDragDrop()開頭的AllowDrop(或另一個新屬性)。 支援其他選擇模式比較複雜,但仍然很簡單。而不是移動一個項並擁有一個sourceIndex,我們將擁有 新增一個sourceIndex[]陣列,該陣列將從OnMouseDown中的SelectedIndices複製,而primarySourceIndex將包含 單擊的專案(也是在OnMouseDown中,它可以從IndexFromPoint獲得,而不需要轉換座標)。 然後,在OnDragDrop方法中,我們使用(primarySourceIndex - index)位置:item移動所有專案 在sourceIndex[i]將被移動到sourceIndex[i] + primarySourceIndex -索引位置。 使用的程式碼 使用這個控制元件就像使用標準列表框一樣簡單。Reorder事件在設計器中可用,可以輕鬆處理。 隱藏,複製Code

userSortableListBox1.Reorder += 
  new synek317.Controls.UserSortableListBox.ReorderHandler(this.optionsListBox_Reorder);
void optionsListBox_Reorder(object sender, UserSortableListBox.ReorderEventArgs e)
{
  //moved index is at e.index2 position
  //or simply at userSortableListBox1.SelectedIndex
  //previously it was at e.index1 position
}

在下載部分,我包含了一個帶有已編譯控制元件的.dll檔案,因此您可以將其新增到您的專案引用中並開箱即用。 的興趣點 我不確定為什麼selectedindexchange不啟動時MouseDown事件被用於列表框。但是,我使用了一個變通方法,我的控制元件只是簡單地啟動 從滑鼠向下的selectedindexchangeevent。 本文轉載於:http://www.diyabc.com/frontweb/news343.html