WPF Datagrid drag and drop questions - wpf

I have a WPF Datagrid and I'm implementing drag and drop functionality.
The datagrid has a list of "files" and the user can drag them and copy the file to the desktop.
This is done like this:
string[] files = new String[myDataGrid.SelectedItems.Count];
int ix = 0;
foreach (object nextSel in myDataGrid.SelectedItems)
{
files[ix] = ((Song)nextSel).FileLocation;
++ix;
}
string dataFormat = DataFormats.FileDrop;
DataObject dataObject = new DataObject(dataFormat, files);
DragDrop.DoDragDrop(this.myDataGrid, dataObject, DragDropEffects.Copy);
I have two questions:
1. When I want to drag multiple items- this is a problem because after I select a couple and start clicking on one to start dragging- only that gets selected and the other items get deselected. I tried the solution that is given here but for some reason it doesn't work.
2. I want to remove the dragged item from the datagrid after it is copied. The problem is that I don't know how to check if the file was copied or whether the user just dragged it on the screen without copying it.
I hope you can help me solve these problems.
Thanks!

I think this i what you are looking for:
add this code to the DataGrid__PreviewMouseLeftButtonDown event handler:
private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.startingPosition = e.GetPosition(null);
DependencyObject dep = (DependencyObject)e.OriginalSource;
// iteratively traverse the visual tree until get a row or null
while ((dep != null) && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
//if this is a row (item)
if (dep is DataGridRow)
{
//if the pointed item is already selected do not reselect it, so the previous multi-selection will remain
if (songListDB.SelectedItems.Contains((dep as DataGridRow).Item))
{
// now the drag will drag all selected files
e.Handled = true;
}
}
}
and now the draging won't change you selection.
Have a good luck!
I used that article to write my answer

Improved on finding the row.
Also selecting the clicked row when not dragging is added.
This now behaves exactly as other Microsoft selectors (eg Outlook)
public TreeDataGrid()
{
Loaded += TreeDataGrid_Loaded;
LoadingRow += new EventHandler<DataGridRowEventArgs>(TreeDataGrid_LoadingRow);
}
#region MultiSelect Drag
object toSelectItemOnMouseLeftButtonUp;
void TreeDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(Row_PreviewMouseLeftButtonDown);
e.Row.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(Row_PreviewMouseLeftButtonUp);
}
void Row_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridRow row = (DataGridRow)sender;
toSelectItemOnMouseLeftButtonUp = null; // always clear selecteditem
if (SelectedItems.Contains(row.Item)) //if the pointed item is already selected do not reselect it, so the previous multi-selection will remain
{
e.Handled = true; // this prevents the multiselection from disappearing, BUT datagridcell still gets the event and sets DataGrid's private member _selectionAnchor
toSelectItemOnMouseLeftButtonUp = row.Item; // store our item to select on MouseLeftButtonUp
}
}
void Row_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DataGridRow row = (DataGridRow)sender;
if (row.Item == toSelectItemOnMouseLeftButtonUp) // check if it's set and concerning the same row
{
if (SelectedItem == toSelectItemOnMouseLeftButtonUp) SelectedItem = null; // if the item is already selected whe need to trigger a change
SelectedItem = toSelectItemOnMouseLeftButtonUp; // this will clear the multi selection, and only select the item we pressed down on
typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, new DataGridCellInfo(row.Item, ColumnFromDisplayIndex(0))); // we need to set this anchor for when we select by pressing shift key
toSelectItemOnMouseLeftButtonUp = null; // handled
}
}
#endregion

Related

Windows Forms: Manually paint SelectedItem of ComboBox

I have a ComboBox (ToolStripCombobox, to be more precise) filled with items of type KeyValuePair<Int32, FontFamily>. I managed to have the Items beeing painted manually by using the DrawItem event. So every Item is painted with the FontFamily of the corresponding KeyValuePair. This works fine for the DropDownList, but when I select an Item out of the List and the list closes, the text in the ComboBox says something like "[21, [FontFamily: Name=Arial]]" which is most likely the result of SelectedItem.ToString().
Any ideas how to solve this problem?
here is the code of my custom DrawItem method:
private void fontComboBoxDrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if ((e.State & DrawItemState.Focus) != 0)
{
e.DrawFocusRectangle();
}
Brush objBrush = null;
var itemToDraw = this.fontComboBox.Items[e.Index];
KeyValuePair<Int32, FontFamily> windowsFontItem = (KeyValuePair<Int32, FontFamily>)itemToDraw;
objBrush = new SolidBrush(e.ForeColor);
e.Graphics.DrawString(windowsFontItem.Value.Name, new Font(windowsFontItem.Value, e.Font.Size), objBrush, e.Bounds);
if (objBrush != null)
{
objBrush.Dispose();
}
objBrush = null;
}
Update:
It works as expected, when I set the DropDownStyle of the ComboBox to ComboBoxStyle.DropDownList
But I´d rather use ComboBoxStyle.DropDown, so you can edit the Text to search for Fonts.

wpf datagrid filter column header value access

I am using control datagrid filter control from http://www.codeproject.com/Articles/42227/Automatic-WPF-Toolkit-DataGrid-Filtering and it is working fine.
I want to save filter conditions ( header textboxes value ).
How can I get header/textboxes value on any button click and again set some header textboxes in some other event.
[More details]
I am consuming Filter control in one of my wpf application.Downloaded project is also containing a consumer test project (DataGridFilterTest). add a simple button with click event outside the grid ( no relation with grid).NOw I filter the data with some text in header column textbox. The added button click event i want the value or object of this textbox.Idea is I will save these text somewhere in xml and later on next time ( new request),i ll open grid with pre fixed data filter with same text.
Thanks
What a about a method like this one, where you go through the VisualTree to get the desired values. Once you have the DataGridColumnHeader and DataGridColumnCell is easy to get the value:
private void DataGrid_MouseRightButtonUp(object sender,
MouseButtonEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
// iteratively traverse the visual tree
while ((dep != null) &&
!(dep is DataGridCell) &&
!(dep is DataGridColumnHeader))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
return;
if (dep is DataGridColumnHeader)
{
DataGridColumnHeader columnHeader = dep as DataGridColumnHeader;
// do something
}
if (dep is DataGridCell)
{
DataGridCell cell = dep as DataGridCell;
// do something
}
}
I took it from here http://www.scottlogic.com/blog/2008/12/02/wpf-datagrid-detecting-clicked-cell-and-row.html, it can be very useful for you
I faced the exact same problem. Here is a solution. It might not be the most elegant one, but it works.
In the Datagrid's Loaded event, find the TextBox elements. You can use the algorithm written here. You can narrow down the elements by checking their names, which will be "PART_TextBoxFilter", and their type will be DataGridFilterLibrary.Support.DelayTextBox (which descends from TextBox). It will still find more than you need, you can check if their binding paths are not empty (that is not part of the below sample). The point is to hook a KeyDown event handler on them:
void dgFilter_Loaded(object sender, RoutedEventArgs e)
{
foreach (TextBox tb in FindVisualChildren<TextBox>(sender as DataGrid))
{
if (tb != null && tb.Name == "PART_TextBoxFilter")
{
tb.KeyUp += new KeyEventHandler(tbDelayTextBox_KeyUp);
}
}
}
In the event handler method, the filter values can be reached, but that is not enough. The name of the binding path must be saved as well, otherwise you will not know which column it should filter. Furthermore, you can have multiple datagrids in your window, so the name of the datagrid is also important.
I used a Dictionary for saving the filters.
Dictionary<string,string> filterValues;
void tbDelayTextBox_KeyUp(object sender, KeyEventArgs e)
{
string dgName;
var bindingPath = DelayTextBoxBindingPath(sender as DataGridFilterLibrary.Support.DelayTextBox, out dgName);
if (!String.IsNullOrEmpty(bindingPath))
{
var key = dgName + "_" + bindingPath;
if (filterValues.ContainsKey(key))
{
filterValues[key] = ((TextBox)sender).Text;
}
else
{
filterValues.Add(key, ((TextBox)sender).Text);
}
}
}
Here is the method to get the binding path and the datagrid's name:
string DelayTextBoxBindingPath(DataGridFilterLibrary.Support.DelayTextBox __dtb, out string datagridName)
{
datagridName = String.Empty;
var result = String.Empty;
if (__dtb != null)
{
BindingExpression be = __dtb.GetBindingExpression(TextBox.TextProperty) as BindingExpression;
if (be != null)
{
var dgcf = be.DataItem as DataGridFilterLibrary.DataGridColumnFilter;
if (dgcf != null && dgcf.FilterCurrentData != null)
{
result = dgcf.FilterCurrentData.ValuePropertyBindingPath;
datagridName = dgcf.DataGrid.Name;
}
}
}
return result;
}
To save/load the filter, you can use the window's OnClosing, and the Datagrid's Loaded event (above), respectively.
Hope this helps.

Getting data from detail row

How can i get data from devexress gridcontrol's detail row via double-click.
If i focused on child row gridview's double click event doesn't catch.
i tried this method, but my request is catching data by double click
private void gcOperasyonlar_FocusedViewChanged(object sender, DevExpress.XtraGrid.ViewFocusEventArgs e)
{
if (e.View != null && e.View.IsDetailView)
(e.View.ParentView as GridView).FocusedRowHandle = e.View.SourceRowHandle;
GridView detailView = gcOperasyonlar.FocusedView as GridView;
MessageBox.Show(detailView.GetFocusedRowCellValue("Kalip").ToString());
}
thanks for your help
There is also an easier way:
ColumnView cv = _gridControlxyz.FocusedView as ColumnView;
selectedRow row = cv.GetRow(cv.FocusedRowHandle)
I found this code on the forum, It might be useful as long as your grid is not editable (so that mouse click doesn't activate the editable field).
private void gridView1_DoubleClick(object sender, EventArgs e) {
GridView view = (GridView)sender;
Point pt = view.GridControl.PointToClient(Control.MousePosition);
DoRowDoubleClick(view, pt);
}
private static void DoRowDoubleClick(GridView view, Point pt) {
GridHitInfo info = view.CalcHitInfo(pt);
if(info.InRow || info.InRowCell) {
string colCaption = info.Column == null ? "N/A" : info.Column.GetCaption();
MessageBox.Show(string.Format("DoubleClick on row: {0}, column: {1}.", info.RowHandle, colCaption));
}
}
http://www.devexpress.com/Support/Center/Question/Details/A2934
Let's say you have two gridviews (I'm guessing you're using gridviews in your grid control): gvMaster and gvDetail.
You should implement event DoubleClick for your gvDetail in order to achieve desired functionality:
private void gvDetail_DoubleClick(object sender, EventArgs e) {
var gv = sender as GridView; // sender is not gvDetail! It's an instance of it. You have as many as there are rows in gvMaster
var row = gv.GetDataRow(e.FocusedRowHandle); // or use gv.GetRow(e.FocusedRowHandle) if your datasource isn't DataSet/DataTable (anything with DataRows in it)
MessageBox.Show(row["Kalip"].ToString());
}

Change datagrid column values in DatagridCombobox selectedindexchanged event

I have a datagrid with DatagridComboboxcolumn as one of the column in winforms.
Combobox is contain two items Y,N.
If user select Y,I need to change the value for the two columns of same row.
Same thing will happen when User select "N".
I have tried to register ComboBox_SelectedIndexChanged as follows.
But not able to get the row index or coulmn index for the selected row and change the values of the same row columns.
Please help me asap.
private void gridTesr_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox combo = e.Control as ComboBox;
if(combo != null)
{
// Remove an existing event-handler, if present, to avoid
// adding multiple handlers when the editing control is reused.
combo.SelectedIndexChanged -=new EventHandler(ComboBox_SelectedIndexChanged);
// Add the event handler.
combo.SelectedIndexChanged +=new EventHandler(ComboBox_SelectedIndexChanged);
}
}
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
}
Try declaring your own method to handle the ComboBox.SelectedIndexChanged event.
private void gridTesr_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox combo = e.Control as ComboBox;
if(combo != null)
{
// Remove an existing event-handler, if present
combo.SelectedIndexChanged -= this.MyComboEventHandler;
// Add the event handler
combo.SelectedIndexChanged += this.MyComboEventHandler;
}
}
private void MyComboEventHandler(object sender, EventArgs e)
{
string myValue = ((ComboBox)sender).SelectedItem.ToString();
DataGridViewCell cell = gridTesr.CurrentCell;
MessageBox.Show(string.Format("The current ComboBox resides in the Row {0} and Column {1} and it currently has the {3} value.",
cell.RowIndex, cell.ColumnIndex, myValue));
}

Is ScrollIntoView synchronized in virtualstackpannel (Especially in WPF DataGrid)?

We have a problem for focus a cell of DataGrid after Its data of bounded collection has Refreshed.
for example we set a filter for its collection and then we want to refocus a stored cell of stored column.
Is it true that we think a call to ScrollIntoView is synchronized it means after call it our desired row and cell are created and we can set focus? (again it means after we call to ScrollIntoView , Is it true we think that Itemsgenerator finished its work and we can surly find our desired cell)
$
//set filter of DataGrid Collection
DataGrid_Standard.ScrollIntoView(rowNumber,cellNumber);
//we sure our desired cell are created now
DataGridRow row = (DataGridRow)DataGrid_Standard.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
DataGrid_Standard.ScrollIntoView(DataGrid_Standard.Items[index]);
row = (DataGridRow)DataGrid_Standard.ItemContainerGenerator.ContainerFromIndex(index);
}
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
// try to get the cell but it may possibly be virtualized
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
// now try to bring into view and retreive the cell
DataGrid_Standard.ScrollIntoView(rowContainer, DataGrid_Standard.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column); cell.focus();
Related
Here's a datagrid Selection changed event handler that moves a virtualized row into view and then sets the focus to that row. This works for me:
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
if (dg.SelectedItem == null) return;
dg.ScrollIntoView(dg.SelectedItem);
DataGridRow dg_row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.SelectedItem);
if (dg_row == null) return;
dg_row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
EDIT: Using the dg_row.MoveFocus method had an undesriable effect (a checkbox column required two clicks to set instead of one) and it worked better for me to just use
dg_row.Focus();
Action action = () =>
{
dg .ScrollIntoView(dg .SelectedItem);
var item = dg.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
if (item == null) return;
item.Focus();
};
Dispatcher.BeginInvoke(DispatcherPriority.Background, action);
This should work fine for your case.

Resources