2012-10-03 77 views
3

問題:單擊並拖動多選複選框

我的應用程序需要用戶能夠通過一個複選框列以選擇在DataGrid多個條目。期望的行爲是,當您單擊列中的複選框時,它的行爲就像一個普通的複選框,但如果在鼠標左鍵關閉的情況下拖動它,則其選擇狀態將變爲與之前相反。

我迄今爲止嘗試:

我試圖子類CheckBox和處理OnMouseEnter在,但被點擊的第一個複選框,似乎捕捉鼠標所以沒有其他複選框觸發OnMouseEnter在事件。

我試着實現了拖放式黑客,用戶點擊選擇一個複選框,然後拖過那個複選框,以便其他人收到一個DragOver事件,並可以切換狀態。這個解決方案導致光標在拖放過程中沒有經過另一個複選框時顯示爲一個帶斜槓的圓圈,這對於此應用程序是不可接受的。

我想什麼:

我想的方法實現具有我所描述,最好是在一個XAML樣式或子類,我可以重複使用的功能的複選框,因爲需要在多個這種功能放在我的應用程序中。

有沒有一種優雅的方式來達到這種效果?

回答

3

我在我的應用程序中做了這個,當你必須選擇30個複選框時非常方便。
爲此,我自己處理預覽鼠標事件:PreviewMouseLeftButtonDown,PreviewMouseMove,PreviewMouseLeftButtonUp。

在PreviewMouseLeftButtonDown:我得到相對於控件的鼠標位置。
在PreviewMouseMove中:如果我距離firstPoint足夠遠,我會從開始到當前位置繪製一個矩形。然後我在複選框中迭代,看看他們是否與矩形相交,並且如果是的話突出顯示它們(所以用戶知道哪些chexboxes將交換)
在PreviewMouseLeftButtonUp中:我爲相交CheckBoxes做交換。

如果它可以幫助你,這裏是我使用的代碼。它不是MVVM(:-)),但工作正常,它可能會給你想法。它是從vb.net代碼自動翻譯。

爲了使它工作,你需要在你的CheckBoxes之上有一個Canvas(例如在同一個網格單元格內),屬性IsHitTestVisible =「False」。
在這個Canvas中,使用適當的填充和描邊放置一個Rectangle nammed「SelectionRectangle」,但使用0.0 Opacity。

// '' <summary> 
// '' When Left Mouse button is pressed, remember where the mouse move start 
// '' </summary> 
private void EditedItems_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { 
    StartPoint = Mouse.GetPosition(this); 
} 

// '' <summary> 
// '' When mouse move, update the highlight of the selected items. 
// '' </summary> 
private void EditedItems_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e) { 
    if ((StartPoint == null)) { 
     return; 
    } 
    PointWhereMouseIs = Mouse.GetPosition(this); 
    Rect SelectedRect = new Rect(StartPoint, PointWhereMouseIs); 
    if (((SelectedRect.Width < 20) 
       && (SelectedRect.Height < 20))) { 
     return; 
    } 
    // show the rectangle again 
    Canvas.SetLeft(SelectionRectangle, Math.Min(StartPoint.X, PointWhereMouseIs.X)); 
    Canvas.SetTop(SelectionRectangle, Math.Min(StartPoint.Y, PointWhereMouseIs.Y)); 
    SelectionRectangle.Width = Math.Abs((PointWhereMouseIs.X - StartPoint.X)); 
    SelectionRectangle.Height = Math.Abs((PointWhereMouseIs.Y - StartPoint.Y)); 
    foreach (CheckBox ThisChkBox in EditedItems.Children) { 
     object rectBounds = VisualTreeHelper.GetDescendantBounds(ThisChkBox); 
     Vector vector = VisualTreeHelper.GetOffset(ThisChkBox); 
     rectBounds.Offset(vector); 
     if (rectBounds.IntersectsWith(SelectedRect)) { 
      ((TextBlock)(ThisChkBox.Content)).Background = Brushes.LightGreen; 
     } 
     else { 
      ((TextBlock)(ThisChkBox.Content)).Background = Brushes.Transparent; 
     } 
    } 
} 

// '' <summary> 
// '' When Left Mouse button is released, change all CheckBoxes values. (Or do nothing if it is a small move --> 
// '' click will be handled in a standard way.) 
// '' </summary> 
private void EditedItems_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { 
    PointWhereMouseIs = Mouse.GetPosition(this); 
    Rect SelectedRect = new Rect(StartPoint, PointWhereMouseIs); 
    StartPoint = null; 
    SelectionRectangle.Opacity = 0; 
    // hide the rectangle again 
    if (((SelectedRect.Width < 20) 
       && (SelectedRect.Height < 20))) { 
     return; 
    } 
    foreach (CheckBox ThisEditedItem in EditedItems.Children) { 
     object rectBounds = VisualTreeHelper.GetDescendantBounds(ThisEditedItem); 
     Vector vector = VisualTreeHelper.GetOffset(ThisEditedItem); 
     rectBounds.Offset(vector); 
     if (rectBounds.IntersectsWith(SelectedRect)) { 
      ThisEditedItem.IsChecked = !ThisEditedItem.IsChecked; 
     } 
     ((TextBlock)(ThisEditedItem.Content)).Background = Brushes.Transparent; 
    } 
} 

編輯:我在用戶控件中使用該代碼。此控件將布爾值列表和字符串列表(標題)作爲參數,並構建(使用WrapPanel)具有正確標題的CheckBox的數組。因此,您可以選擇/取消選擇矩形,並且還有兩個按鈕可以全部選中/取消全選。我也試圖保持良好的列/行比,以處理7到200布爾值和良好的列/行平衡的選擇。

An exemple of use of the BooleanEdit User Control within a Window

+0

感謝您的幫助。雖然這不是我尋找的優雅解決方案,但它比我想出的任何東西都要好。 – lambdaman

+0

很高興我能幫忙:-)看到我的編輯。 – GameAlchemist

0

這個主題是一個幾個月大的,但我想我有你正在尋找優雅的答案。

因爲你描述拖動和檢查作爲兩個獨立的行爲,首先設置你的DataGrid的MouseDown處理程序...

yourdatagrid.MouseDown += DragCheck_MouseDownHandler; 

這將允許從DataGrid背景(但不是在網格控制牽引起動。 )

private void DragCheck_MouseDownHandler(object sender, MouseEventArgs e) 
    { 
     if (e.Button != MouseButtons.Left) return; 
     Control dgrid = sender as Control; 
     foreach (CheckBox box in dgrid.Controls.OfType<CheckBox>()) 
     { 
      box.Tag = null; 
     } 
     dgrid.MouseMove += DragMove_MouseMoveHandler; 
    } 

這將使用作爲checkbox.Tag一個通僅拖動當鼠標向下撥動。如果您將CheckBox標籤用於別的東西,我相信您可以找到自己的方式來識別這些框。該datagrid.MouseMove設置爲下一個處理....

private void DragMove_MouseMoveHandler(object sender, MouseEventArgs e) 
    { 
     Control dgrid = sender as Control; 

     Point now = dgrid.PointToClient(Cursor.Position); 
     if (e.Button == MouseButtons.Left) 
     { 
      Control under = dgrid.GetChildAtPoint(now); 
      if (under != null && under.GetType() == typeof(CheckBox)) 
      { 
       //if the point has a valid CheckBox control under it 
       CheckBox box = under as CheckBox; 
       if (box.Tag == null)// not yet been swiped 
       { 
        box.Checked = !box.Checked; 
        box.Tag = true; 
       } 
      } 
     } 
     else 
     { 
      //if MouseButtons no longer registers as left 
      //remove the handler 

      dgrid.MouseMove -= DragMove_MouseMoveHandler; 

     } 
    }