I have a checkedlistbox and a listbox both the same height with the same number of items in them. I use the listbox to hold status of checkedlistbox a item.
If there's a scrollbar, then scrolling the checkedlistbox should also scroll the listbox with the same index. I kind of got that working except they scroll at different amounts.
private void checkedListBox1_MouseWheel(object sender, MouseEventArgs e)
{
int scroll = e.Delta / 120;
if (scroll == 1)
{
checkedListBox1.TopIndex -= 5;
listBox1.TopIndex = checkedListBox1.TopIndex;
}
else if (scroll == -1)
{
checkedListBox1.TopIndex += 5;
listBox1.TopIndex = checkedListBox1.TopIndex;
}
}
Additionally, I'm using this code to get the same indexes selected but there's a slight lag until the listbox index gets selected after selecting from the checkedlistbox. Anyway to make it select at the same time?
private void checkedListBox1_MouseClick(object sender, MouseEventArgs e)
{
if (checkedListBox1.Items.Count > 0)
{
int selected = checkedListBox1.SelectedIndex;
if (checkedListBox1.GetItemChecked(selected) == false)
{
checkedListBox1.SetItemChecked(selected, true);
listBox1.SetSelected(selected, true);
}
else if (checkedListBox1.GetItemChecked(selected) == true)
{
checkedListBox1.SetItemChecked(selected, false);
listBox1.SetSelected(selected, true);
}
}
}
You can put listboxes inside Two scrollviewer and in the viewChanged event write the code on xaml.cs part
private void ScrollViewer1_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
ScrollViewer2.ScrollToHorizontalOffset(double.Parse(ScrollViewer1.HorizontalOffset.ToString()));
}
http://www.codeguru.com/cpp/controls/listview/article.php/c4163
http://www.codeproject.com/Tips/197335/Synchronized-ListBox-objects-or-how-to-scroll-2-Li
Related
I have a WPF c# application that has a window with two Reordering Listboxes right next to each other. I used the examples in this link to make the listbox user control. Unfortunately this allows the user to drag from one box and drop in the other. How can I make sure that this doesn't happen?
Here is my code:
public void setItems(List<string> values){
_items = new ObservableCollection<Item>();
foreach (string s in values)
{
_items.Add(new Item(s));
}
listBox.DisplayMemberPath = "Name";
listBox.ItemsSource = _items;
listBox.PreviewMouseMove += ListBox_PreviewMouseMove;
var style = new Style(typeof(ListBoxItem));
style.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
style.Setters.Add(
new EventSetter(
ListBoxItem.PreviewMouseLeftButtonDownEvent,
new MouseButtonEventHandler(ListBoxItem_PreviewMouseLeftButtonDown)));
style.Setters.Add(
new EventSetter(
ListBoxItem.DropEvent,
new DragEventHandler(ListBoxItem_Drop)));
listBox.ItemContainerStyle = style;
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point point = e.GetPosition(null);
Vector diff = _dragStartPoint - point;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
var lb = sender as ListBox;
var lbi = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource));
if (lbi != null)
{
DragDrop.DoDragDrop(lbi, lbi.DataContext, DragDropEffects.Move);
}
}
}
private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_dragStartPoint = e.GetPosition(null);
}
private void ListBoxItem_Drop(object sender, DragEventArgs e)
{
if (sender is ListBoxItem)
{
var source = e.Data.GetData(typeof(Item)) as Item;
var target = ((ListBoxItem)(sender)).DataContext as Item;
int sourceIndex = listBox.Items.IndexOf(source);
int targetIndex = listBox.Items.IndexOf(target);
Debug.WriteLine("Target: " + targetIndex.ToString());
Move(source, sourceIndex, targetIndex);
}
}
private void Move(Item source, int sourceIndex, int targetIndex)
{
if (sourceIndex < targetIndex)
{
_items.Insert(targetIndex + 1, source);
_items.RemoveAt(sourceIndex);
}
else
{
int removeIndex = sourceIndex + 1;
if (_items.Count + 1 > removeIndex)
{
_items.Insert(targetIndex, source);
_items.RemoveAt(removeIndex);
}
}
}
}
I figured it out in case anyone else has a similar issue...
I changed the Move function to the following:
private void Move(Item source, int sourceIndex, int targetIndex)
{
IList<Item> prevItems = _items;
try
{
_items.RemoveAt(sourceIndex);
_items.Insert(targetIndex, source);
}
catch(ArgumentOutOfRangeException)
{
//User doesn't need to be notified about this. It just means that they dragged out of the box they were ordering.
//The application does not need to be stopped when this happens.
_items = prevItems;
Debug.WriteLine("User tried to drag between boxes.. Order of boxes were not changed. ");
}
}
I realized that if I remove the item first then I won't have to worry about changing the target or remove index based on the position of the sourceIndex in relation to the targetIndex. Instead I can wrap it in a try catch and look for an ArgumentOutOfRangeException, which will happen if the user tries to drag and drop between. If that happens I reset the box to the previous items.
As far as I can tell this solution works fine... There may have been a simpler way to go about this.
I need to add mouse multiselection support to a winform listview. (User will click one item and drag it to fifth and she'll be able to select items 1 to 5) I think I should inherit the listview to add new features to it but i don't know which events or methods should i add. How can i do it? Thanks, in advance.
Make custom ListView of and override MouseMove (bonus - Ctr+A support):
[System.ComponentModel.DesignerCategory("Code")]
public class MyListView : ListView
{
protected override void OnMouseMove(MouseEventArgs e)
{
// easy mouse selection
if (MouseButtons == MouseButtons.Left)
{
var item = this.HitTest(e.Location).Item;
if (item != null)
item.Selected = true;
}
base.OnMouseMove(e);
}
protected override void OnKeyDown(KeyEventArgs e)
{
// ctrl-a - select all
if (e.KeyCode == Keys.A && e.Control)
SelectAll();
base.OnKeyDown(e);
}
}
It's not the best implementation, if you move too fast, then some items will be skipped.
Some explanations
ListView is a bit special control, because it has different views to present data. In icon view you select multiple items by drawing a rectangle with mouse (press button, draw rectangle, release). Same works in details view. All intersected by rectangle items will be selected. Important: this way of selection required to draw rectangle starting from unoccupied by items area.
Problem: in details view unoccupied area located to the right of last column and to the bottom of last item. Often columns take all horizontal space, items take all vertical, so there is no such area exists then. So this way of selection will not work!
Other well known possibility to to select multiple items in ListView is to select first item, press Shift and then click on last item. All items in the range (or rectangle in icon view) will be selected. Or keep Ctr pressed and click items to toggle their selection one by one.
Sinatrs approach is a good idea.
With 'Details' view you can try something like this, to get a Windows Explorer like behaviour:
Int32 firstClickItemIndex = 0;
public Boolean numInRange(Int32 num, Int32 first, Int32 last)
{
return first <= num && num <= last;
}
private void listView1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A && e.Control)
{
foreach (ListViewItem lvItem in listView1.Items)
lvItem.Selected = true;
}
}
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
if (MouseButtons == MouseButtons.Left && listView1.HitTest(e.Location).Item != null)
firstClickItemIndex = listView1.HitTest(e.Location).Item.Index;
}
private void listView1_MouseMove(object sender, MouseEventArgs e)
{
if (MouseButtons == MouseButtons.Left)
{
ListViewItem lvItem = listView1.HitTest(e.Location).Item;
if (lvItem != null)
{
lvItem.Selected = true;
if (listView1.SelectedItems.Count > 1)
{
Int32 firstSelected = listView1.SelectedItems[0].Index;
Int32 lastSelected = listView1.SelectedItems[listView1.SelectedItems.Count - 1].Index;
foreach (ListViewItem tempLvItem in listView1.Items)
{
if (numInRange(tempLvItem.Index, firstSelected, lastSelected) && (numInRange(tempLvItem.Index, lvItem.Index, firstClickItemIndex) || numInRange(tempLvItem.Index, firstClickItemIndex, lvItem.Index)))
{
tempLvItem.Selected = true;
}
else
{
tempLvItem.Selected = false;
}
}
}
}
}
}
I have got a DataGridView with a MultiSelect=True in C#.
I want to limit the maximum number of simultaneously selected rows to 2, so that user can select only one or two rows at the same time. How can I achive this?
There are no events like BeforeSelectedRowsChanged or ValidatingSelectedRows.
My DataGridView is Readonly also.
** EDIT **
my SelectionMode is FullRowSelect
On the SelectionChanged event you can do this:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedCells.Count > 2)
{
dataGridView1.SelectedCells[0].Selected = false;
}
}
This will prevent/undo selecting any more cells after selecting two.
For whole rows:
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 2)
{
dataGridView1.SelectedRows[0].Selected = false;
}
}
This always leave selected 2 last selected rows
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count > 2)
{
for (int i = 2; i < dataGridView1.SelectedRows.Count; i++)
{
dataGridView1.SelectedRows[i].Selected = false;
}
}
}
You could try overriding SetSelectedRowCore, calling the base with adding your new limitation to the selected condition.
protected virtual void SetSelectedRowCore(int rowIndex,bool selected )
{
base(rowIndex, selected && currentSelection < allowedSelectionCount);
}
SetSelectedRowCore
I have copied some code and modified it to suit my application. And I will continue to tweak and clean up the code until I am statisfied with it. But I have encountered a little error. I have two datagridviews and wish to move datagridrows from one to another. However, while the drag&drop events all fire, the dataGridView_Routes_DragDrop() will execute the log command because there is no data in e.Data.GetData. What have I done wrong? Am I missing something? I've tried to look through several guides but nothing specifically covers this issue.
How can I get the datagrid pass the dragged datagridrow over to the other datagrid?
/* Drag & Drop */
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private void dataGridView_Trips_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView_Trips.DoDragDrop(dataGridView_Trips.Rows[rowIndexFromMouseDown], DragDropEffects.Copy);
}
}
}
private void dataGridView_Trips_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
rowIndexFromMouseDown = dataGridView_Trips.HitTest(e.X, e.Y).RowIndex;
if (rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2), e.Y - (dragSize.Height / 2)), dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void dataGridView_Routes_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void dataGridView_Routes_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(DataRowView)))
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView_Routes.PointToClient(new Point(e.X, e.Y));
// If the drag operation was a copy then add the row to the other control.
if (e.Effect == DragDropEffects.Copy)
{
DataGridViewRow rowToMove = e.Data(typeof(DataGridViewRow)) as DataGridViewRow;
dataGridView_Routes.Rows.Add(rowToMove);
}
}
else
{
log("Geen data! #01", "Fout");
}
}
/* End Drag & Drop */
I don't know. But the following function has been adjusted and it works as intended. Not quite sure how the previous code broke.
EDIT: The typeof was written with DataViewRow instead of DataGridViewRow. Fail.
private void dataGridView_Routes_DragDrop(object sender, DragEventArgs e)
{
try
{
if (e.Data.GetDataPresent(typeof(DataGridViewRow)))
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView_Routes.PointToClient(new Point(e.X, e.Y));
// If the drag operation was a copy then add the row to the other control.
if (e.Effect == DragDropEffects.Copy)
{
DataGridViewRow Row = (DataGridViewRow)e.Data.GetData(typeof(DataGridViewRow));
dataGridView_Routes.Rows.Add(Row.Cells[0].Value, Row.Cells[1].Value, Row.Cells[2].Value);
}
}
else
{
log("Geen data! #01", "Fout");
}
}
catch (Exception msg)
{
log(msg.Message,"Fout");
}
}
This solution is for the people who have the datagridViews bound to customObjects. It works like a charm with multiple selection. Suggestions are accepted.
Assuming you want to drag from datagridview1 to datagridview2
//datagridview1 is bound to this BindingList
BindingList<myObject> object_bound_list1;
//datagridview2 is bound to this BindingList
BindingList<myObject> object_bound_list2;
List<myObject> selected_Object_list = new List<myObject>();
List<int> selected_pos_list = new List<int>();
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView1.DoDragDrop(
selected_Object_list,
DragDropEffects.Move);
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
int rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
//if shift key is not pressed
if (Control.ModifierKeys != Keys.Shift && Control.ModifierKeys != Keys.Control)
{
//if row under the mouse is not selected
if (!selected_pos_list.Contains(rowIndexFromMouseDown) && rowIndexFromMouseDown > 0)
{
//if there only one row selected
if (dataGridView1.SelectedRows.Count == 1)
{
//select the row below the mouse
dataGridView.ClearSelection();
dataGridView1.Rows[rowIndexFromMouseDown].Selected = true;
}
}
}
//clear the selection lists
selected_Object_list.Clear();
selected_pos_list.Clear();
//add the selected objects
foreach (DataGridViewRow row in dataGridView1.SelectedRows)
{
selected_Object_list.Add(object_bound_list1[row.Index]);
selected_pos_list.Add(row.Index);
}
}
private void dataGridView2_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dataGridView2_DragDrop(object sender, DragEventArgs e)
{
if (e.Effect == DragDropEffects.Move)
{
foreach (var item in selected_Object_list)
{
object_bound_list2.Add(item);
}
}
}
This is the code:
private void Lightnings_Mode_Load(object sender, EventArgs e)
{
this.Size = new Size(416, 506);
this.Location = new Point(23, 258);
listBoxIndexs();
this.listBox1.SelectedIndex = 0; // This will make the listBox when showing it first time first item to be already selected !!!!!!
}
private void listBoxIndexs()
{
for (int i = 0; i < Form1.lightningsRegions.Count; i++)
{
listBox1.Items.Add(Form1.lightningsRegions[i]);
}
}
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
item = listBox1.SelectedItem.ToString();
this.f1.PlayLightnings();
f1.pdftoolsmenu();
if (item != null && !pdf1.Lightnings.Contains(item.ToString()))
{
pdf1.Lightnings.Add(item.ToString());
}
}
Im using the variable item in two places in Form1.
Once to extract the string and play the numbers inside and once to add item to the List Lightnings.
In the first time for playing the numbers i want it to be:
this.listBox1.SelectedIndex = 0;
Since i want to be able to play already the first item once i clicked a button and showed/opened the listBox.
In the second place where im adding the item to the Lightnings List i want that it will add the item only if i clicked first on any item.
Since i did :
this.listBox1.SelectedIndex = 0;
It will add item automatic to Lightnings once i show/open the listBox
I need it to be added to the List only if i click first an item in the other hand i also want to be selectedindex = 0 since i want it to be selected so i can play it.
So how can i separate between the SelectedIndex = 0 for playing and for adding the item to the List ?
If I understand correctly, you can simply add a flag.
bool allowItemAdding;
private void Lightnings_Mode_Load(object sender, EventArgs e)
{
allowItemAdding = false; //setting false here because *sometimes* Load event is called multiple times.
this.Size = new Size(416, 506);
this.Location = new Point(23, 258);
listBoxIndexs();
this.listBox1.SelectedIndex = 0;
allowItemAdding = true; //set flag to true after selecting the index initially
}
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
item = listBox1.SelectedItem.ToString();
this.f1.PlayLightnings();
f1.pdftoolsmenu();
if (allowItemAdding)
{
if (item != null && !pdf1.Lightnings.Contains(item.ToString()))
{
pdf1.Lightnings.Add(item.ToString());
}
}
}
The flag will then stay true until you explicitly change it to false, so you can control when items should be added or not.
use _SelectionChangeCommitted instead of listBox1_SelectedIndexChanged