Drag Drop Multiple Items in a FlowLayoutPanel
-
03-07-2021 - |
题
I have a WinForm application using a FlowLayoutPanel that displays .TIFF files that have multiple pages. The FlowLayoutPanel displays all the pages in a ThumbNails View.
I have implemented the Drag Drop Logic which works fine for individual items. Now, I would now like to change it to allow the user to select multiple thumbnails (using the CTRL or the Shift Key) and drag drop to a different spot.
//** Logic after each thumbnail is generated:
PictureBox thumb = new myProject.utility.PictureBox(pageNum);
thumb.Image = doc.getThumb(pageNum); //since we pre loaded, we won't stall the gui thread.
thumb.Click += new System.EventHandler(
(thumbSend, thumbEvent) =>
{
highLightThumb(thumb.getPage());
}
);
thumb.DoubleClick += new System.EventHandler(
(thumbSend, thumbEvent) =>
{
selectedDoc = thumb.getPage();
me.Visible = false;
}
);
thumbFlow.Controls.Add(thumb);
if (selectedDoc == pageNum)
highLightThumb(pageNum);
//** Highlight Methods
private void highLightThumb(int page)
{
//clear highlight
foreach (Control c in thumbFlow.Controls)
{
if (c is PictureBox)
{
((PictureBox)c).highlight = false;
}
}
//apply highlight
foreach (Control c in thumbFlow.Controls)
{
if (c is PictureBox)
{
PictureBox thumbFrame = (PictureBox)c;
if (page == thumbFrame.getPage())
thumbFrame.highlight = true;
}
}
}
Below is the existing drag drop logic.
//**********************//
//** Drag/Drop Events **//
//**********************//
private void thumbFlow_DragDrop(object sender, DragEventArgs e)
{
PictureBox data = (PictureBox)e.Data.GetData(typeof(PictureBox));
FlowLayoutPanel _destination = (FlowLayoutPanel)sender;
Point p = _destination.PointToClient(new Point(e.X, e.Y));
var item = _destination.GetChildAtPoint(p);
if (item == null)
{
p.Y = p.Y - 10;
item = _destination.GetChildAtPoint(p);
}
int index = _destination.Controls.GetChildIndex(item, false);
if (index < 0)
return;
_destination.Controls.SetChildIndex(data, index);
_destination.Invalidate();
}
private void thumbFlow_DragEnter(object sender, DragEventArgs e)
{
//apply/clear highlight
foreach (Control c in thumbFlow.Controls)
{
if (c is PictureBox)
{
PictureBox thumbFrame = (PictureBox)c;
if (thumbFrame == ActiveControl)
{
thumbFrame.highlight = true;
}
else
{
((PictureBox)c).highlight = false;
}
}
}
e.Effect = DragDropEffects.Move;
if (dragDropOccurred == false)
{
dragDropOccurred = true;
}
}
//** Scroll when user drags above or below the window object **//
private void thumbFlow_DragLeave(object sender, EventArgs e)
{
int BegY_ThumbFlow = this.thumbFlow.FindForm().PointToClient(this.thumbFlow.Parent.PointToScreen(this.thumbFlow.Location)).Y;
int thumbFlowBound_Y = this.thumbFlow.Height + BegY_ThumbFlow;
int mouseY = this.thumbFlow.FindForm().PointToClient(MousePosition).Y;
while (mouseY >= thumbFlowBound_Y)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value + DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
while (mouseY <= BegY_ThumbFlow)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value - DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
}
An option I am looking at would be change the highLightThumb method to check if the CTRL or Shift Key is selected and not disable the highlights
if (Control.ModifierKeys != Keys.Control)
//**
if (Control.ModifierKeys != Keys.Shift)
Then change the DragDrop DragEnter Routine. Any Help would be greatly appreciated.
解决方案
Here is what I ended up doing..
After the thumbnail is generated:
PictureBox thumb = new util.PictureBox(pageNum);
thumb.Image = doc.getThumb(pageNum); //since we pre loaded, we won't stall the gui thread.
thumb.SizeMode = PictureBoxSizeMode.CenterImage;
thumb.BorderStyle = BorderStyle.FixedSingle;
thumb.Click += new System.EventHandler(
(thumbSend, thumbEvent) =>
{
selectThumb(thumb);
}
);
thumb.DoubleClick += new System.EventHandler(
(thumbSend, thumbEvent) =>
{
selectedDoc = thumb.getPage();
me.Visible = false;
}
);
thumbFlow.Controls.Add(thumb);
if (selectedDoc == pageNum)
selectThumb(thumb);
Now the Thumbnail Selection Code.
In the Picturebox logic, I put now have a variable titled pageIndex. it is initialized with the Page Number. The purpose of this is because as you will see later is when I spin though my objects, if I change the childindex value, it seems to occur right away and if I drag items 2,3,4 it will skip item 3 and only move item 2 & 4. So later when I rearrange the thumbs I first order by the pageIndex.
//*************************************//
//** Thumbnail Selection Processing **//
//*************************************//
/// <summary>
/// Main Thumbnail selection area.
/// If the Shift key is held down, look for a search between.
/// If Ctrl key, do not clear selections as can select multiple.
/// </summary>
private void selectThumb(PictureBox _thumb)
{
if (Control.ModifierKeys == Keys.Shift)
if (SelectMultipleThumbs(_thumb.getPageIndex())) //** if another thumb is selected, select all in between, then exit **//
return;
if (Control.ModifierKeys != Keys.Control)
{
ClearAllSelections();
}
else
{
if (_thumb.IsSelected == true)
{
_thumb.IsSelected = false;
return;
}
}
_thumb.IsSelected = true;
}
/// <summary>
/// Check if there are other selected items. If there is, select all between the start and end.
/// </summary>
private bool SelectMultipleThumbs(int _pageindex)
{
//** Check if there are other objects that have been selected **/
int? otherSelPageIndex = GetPageIndexOfThumbSelected(_pageindex);
if (otherSelPageIndex != null)
{
ApplySelectionBetweenStartEndPageIndex(_pageindex, Convert.ToInt32(otherSelPageIndex));
return true;
}
return false;
}
/// <summary>
/// Apply Selection if between start and end Pages
/// </summary>
private void ApplySelectionBetweenStartEndPageIndex(int _val1, int _val2)
{
int startThumb = _val1;
int endThumb = _val2;
if (_val1 > _val2)
{
startThumb = _val2;
endThumb = _val1;
}
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>().OrderBy(si => si.pageIndex))
{
if (isBetween(thumbFrame.getPageIndex(), startThumb, endThumb))
thumbFrame.IsSelected = true;
else
thumbFrame.IsSelected = false;
}
}
/// <summary>
/// Clear All Highlight
/// </summary>
private void ClearAllSelections()
{
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>())
{
thumbFrame.IsSelected = false;
}
}
/// <summary>
/// Check for any selected items prior
/// </summary>
private int? GetPageIndexOfThumbSelected(int _pageindex)
{
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>())
{
if (thumbFrame.IsSelected &&
_pageindex != thumbFrame.getPageIndex())
{
return thumbFrame.getPageIndex();
}
}
return null;
}
Now to the Processing of the Drag Drop.
private void thumbFlow_DragDrop(object sender, DragEventArgs e)
{
FlowLayoutPanel _destination = (FlowLayoutPanel)sender;
Point p = _destination.PointToClient(new Point(e.X, e.Y));
var item = _destination.GetChildAtPoint(p);
if (item == null)
{
p.Y = p.Y - 10;
item = _destination.GetChildAtPoint(p);
}
int dropIndexValue = _destination.Controls.GetChildIndex(item, false);
if (dropIndexValue < 0)
return;
//**************************************************//
//** Process multiple Select Drag / Drop
//** If Drag From > Drag To, move after.
//** If Drag From < Drag To, move before.
//**************************************************//
//** First .. Find all items that are selected **//
Boolean WasDragUp = false;
int? firstDragIndexValue = null;
int newIndexVal = dropIndexValue;
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>().Where(selVal => selVal.IsSelected))
{
if (firstDragIndexValue == null)
{
firstDragIndexValue = _destination.Controls.GetChildIndex(thumbFrame, false);
if (firstDragIndexValue > dropIndexValue)
WasDragUp = true;
}
thumbFrame.pageIndex = newIndexVal;
newIndexVal++;
}
//** Second .. Find all items that are NOT selected **//
if (WasDragUp)
{
newIndexVal = 0;
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>().Where(selVal => !selVal.IsSelected))
{
if (_destination.Controls.GetChildIndex(thumbFrame, false) == dropIndexValue)
if (newIndexVal <= dropIndexValue)
newIndexVal = dropIndexValue + getThumbSelectedCnt();
thumbFrame.pageIndex = newIndexVal;
newIndexVal++;
}
}
else
{
newIndexVal = 0;
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>().Where(selVal => !selVal.IsSelected))
{
thumbFrame.pageIndex = newIndexVal;
if (_destination.Controls.GetChildIndex(thumbFrame, false) == dropIndexValue)
{
newIndexVal = dropIndexValue + getThumbSelectedCnt();
}
newIndexVal++;
}
}
//** Third .. Set the Child Index value **//
newIndexVal = 0;
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>().OrderBy(si => si.pageIndex))
{
thumbFrame.pageIndex = newIndexVal;
_destination.Controls.SetChildIndex(thumbFrame, thumbFrame.pageIndex);
thumbFrame.IsSelected = false;
newIndexVal++;
}
//** Finally, rebuild the screen **//
_destination.Invalidate();
}
private void thumbFlow_DragEnter(object sender, DragEventArgs e)
{
//apply/clear highlight
foreach (PictureBox thumbFrame in thumbFlow.Controls.OfType<PictureBox>())
{
if (thumbFrame == ActiveControl)
{
thumbFrame.IsSelected = true;
}
}
e.Effect = DragDropEffects.Move;
if (dragDropOccurred == false)
{
dragDropOccurred = true;
}
}
/// <summary>
/// Scroll when user drags above or below the window object.
/// </summary>
private void thumbFlow_DragLeave(object sender, EventArgs e)
{
int BegY_ThumbFlow = this.thumbFlow.FindForm().PointToClient(this.thumbFlow.Parent.PointToScreen(this.thumbFlow.Location)).Y;
int thumbFlowBound_Y = this.thumbFlow.Height + BegY_ThumbFlow;
int mouseY = this.thumbFlow.FindForm().PointToClient(MousePosition).Y;
while (mouseY >= thumbFlowBound_Y)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value + DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
while (mouseY <= BegY_ThumbFlow)
{
thumbFlow.VerticalScroll.Value = thumbFlow.VerticalScroll.Value - DRAG_DROP_SCROLL_AMT;
mouseY = thumbFlow.FindForm().PointToClient(MousePosition).Y;
thumbFlow.Refresh();
}
}
Update the file
I would put my code in here but it really is more specific to each individual on what they plan on doing.. I basically rebuild the file and saved it.