WPF Drag and Drop object from listbox - c#

beginner here.
I'm trying to create a usercontrol with one listbox among other control and I want this listbox to allow drag and drop to other similar instance of the usercontrol.
This is the object I want to drag and drop from one listbox to another :
[Serializable]
public class ListBoxFileName : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string FileNameValue;
public string FileName
{
get { return this.FileNameValue; }
set
{
if (value != this.FileNameValue)
{
this.FileNameValue = value;
NotifyPropertyChanged("FileName");
}
}
}
private bool FileIsSelectedValue;
public bool FileIsSelected
{
get { return this.FileIsSelectedValue; }
set
{
if (value != this.FileIsSelectedValue)
{
this.FileIsSelectedValue = value;
NotifyPropertyChanged("FileIsSelected");
}
}
}
}
Here is how I deal with the drag and drop :
private ListBoxItem _dragged;
private void FileNameList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (_dragged != null)
return;
UIElement element = FileNameList.InputHitTest(e.GetPosition(FileNameList)) as UIElement;
while (element != null)
{
if (element is ListBoxItem)
{
_dragged = (ListBoxItem)element;
break;
}
element = VisualTreeHelper.GetParent(element) as UIElement;
}
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (_dragged == null)
return;
if (e.LeftButton == MouseButtonState.Released)
{
_dragged = null;
return;
}
DataObject obj = new DataObject(DataFormats.Serializable, _dragged);
DragDrop.DoDragDrop(_dragged, obj, DragDropEffects.All);
}
private void FileNameList_DragEnter(object sender, DragEventArgs e)
{
if (_dragged == null || e.Data.GetDataPresent(DataFormats.Serializable, true) == false)
e.Effects = DragDropEffects.None;
else
e.Effects = DragDropEffects.All;
}
private void FileListBox_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, true))
{
string[] droppedFilePaths = e.Data.GetData(DataFormats.FileDrop, true) as string[];
for (var i = 0; i < droppedFilePaths.Length; i++)
{
ListBoxFileName filename = new ListBoxFileName();
filename.FileName = droppedFilePaths[i];
filename.FileIsSelected = false;
FileNamesItems.Add(filename);
}
}
if (e.Data.GetDataPresent(DataFormats.Serializable, true))
{
ListBoxFileName BoxItem = new ListBoxFileName();
BoxItem = e.Data.GetData(DataFormats.Serializable) as ListBoxFileName;
}
}
Everything is fine except when the drop event occurs, BoxItem is always null for some reason, so nothing is added to the listbox.
Any hint ?
Thank you

The data of the DataObject should be a ListBoxFileName instead of a ListBoxItem:
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (_dragged == null)
return;
if (e.LeftButton == MouseButtonState.Released)
{
_dragged = null;
return;
}
DataObject obj = new DataObject(DataFormats.Serializable, _dragged.DataContext as ListBoxFileName);
DragDrop.DoDragDrop(_dragged, obj, DragDropEffects.All);
}
private void FileListBox_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.Serializable, true))
{
ListBoxFileName BoxItem = e.Data.GetData(DataFormats.Serializable) as ListBoxFileName;
//...
}
}
This should work assuming that the ItemsSource of the "FileNameList" control is set to an IEnumerable.
Please provide all relevant code snippets required to be able to reproduce your issue from scratch if you need any further help on this.

Related

How to paint owner drawn ListViewItem differently when hovered?

Below is the code. I saw that the MouseMove event is not always triggered, especially when the mouse is moved very fast, and because of this two items can be marked hovered at the same time, so I now use a variable to hold the last hovered item, but the problem is that there are too many redraws. I also saw that DrawListViewItemEventArgs.State property is just ShowKeyboardCues when it should also contain Hot.
private void Form1_Load(object sender, EventArgs e)
{
listView1.OwnerDraw = true;
listView1.View = View.LargeIcon;
listView1.DrawItem += ListView1_DrawItem;
listView1.MouseMove += ListView1_MouseMove;
for (int i = 1; i <= 6; ++i)
{
listView1.Items.Add($"item {i}", 0);
}
}
private void ListView1_MouseMove(object sender, MouseEventArgs e)
{
ListViewItem item = listView1.GetItemAt(e.X, e.Y);
if (item != null)
{
if (LastHoveredItem != null && LastHoveredItem.Index == item.Index)
{
return;
}
listView1.RedrawItems(item.Index, item.Index, false);
}
}
internal ListViewItem LastHoveredItem = null;
private void ListView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
bool hot = e.Item.Bounds.Contains(listView1.PointToClient(Cursor.Position));
if (LastHoveredItem != null)
{
listView1.RedrawItems(LastHoveredItem.Index, LastHoveredItem.Index, false);
}
if (hot)
{
LastHoveredItem = e.Item;
e.Graphics.FillRectangle(Brushes.Green, e.Bounds);
}
else
{
LastHoveredItem = null;
}
e.DrawText();
}
Here are a few new methods and the existing methods are updated:
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
listView1.OwnerDraw = true;
listView1.View = View.LargeIcon;
listView1.DrawItem += ListView1_DrawItem;
listView1.MouseEnter += ListView1_MouseEnter;
listView1.MouseMove += ListView1_MouseMove;
listView1.MouseLeave += ListView1_MouseLeave;
for (int i = 1; i <= 10; ++i)
{
listView1.Items.Add($"item {i}", 0);
}
}
private void ListView1_MouseEnter(object sender, EventArgs e)
{
CheckHoveredAndInvalidate();
}
private void ListView1_MouseLeave(object sender, EventArgs e)
{
RemoveHoveredAndInvalidate();
}
internal static Rectangle GetEntireItemBounds(ListViewItem it)
{
return it.GetBounds(ItemBoundsPortion.Entire);
}
internal ListViewItem GetEntireItemAtCursorPosition()
{
Point p = listView1.PointToClient(Cursor.Position);
foreach (ListViewItem it in listView1.Items)
{
if (GetEntireItemBounds(it).Contains(p))
{
return it;
}
}
return null;
}
private void ListView1_MouseMove(object sender, MouseEventArgs e)
{
CheckHoveredAndInvalidate();
}
private void CheckHoveredAndInvalidate()
{
ListViewItem item = GetEntireItemAtCursorPosition();
if (item == null)
{
RemoveHoveredAndInvalidate();
}
else if (item != null)
{
if (LastHoveredItem != null)
{
if (LastHoveredItem != item)
{
ListViewItem item2 = LastHoveredItem;
LastHoveredItem = item;
listView1.Invalidate(GetEntireItemBounds(item2));
listView1.Invalidate(GetEntireItemBounds(item));
}
else if (LastHoveredItem == item)
{
}
}
else if (LastHoveredItem == null)
{
LastHoveredItem = item;
listView1.Invalidate(GetEntireItemBounds(item));
}
}
}
private void RemoveHoveredAndInvalidate()
{
if (LastHoveredItem != null)
{
ListViewItem item2 = LastHoveredItem;
LastHoveredItem = null;
listView1.Invalidate(GetEntireItemBounds(item2));
}
else if (LastHoveredItem == null)
{
}
}
private void ListView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (LastHoveredItem == e.Item)
{
e.Graphics.FillRectangle(Brushes.Yellow, e.Item.Bounds);
}
else
{
e.Graphics.FillRectangle(Brushes.Green, e.Item.Bounds);
}
}
The text drawing part has been omitted.

ArgumentOutOfRangeException on an object listview when hitting tab

I have an object list view that has two text columns. When I edit the left column and hit tab to go to the right I receive a "ArgumentOutOfRangeException" with an index of -1. Looks like the index is something internal to the list view because I debugged my application and found no errors. Here is the code :
public partial class SummaryOverviewSettingsDlg : Form
{
private List<SummaryDataset> _localSummaryDatasets = new List<SummaryDataset>();
private bool _includeLimits;
private SummaryOverviewSettings _summaryOverviewSettings;
public bool IncludeLimits { get { return _includeLimits; } }
public SummaryOverviewSettingsDlg(SummaryOverviewSettings summaryOverviewSettings)
{
InitializeComponent();
if (summaryOverviewSettings.Datasets != null)
{
_localSummaryDatasets.AddRange(summaryOverviewSettings.Datasets);
}
_summaryOverviewSettings = summaryOverviewSettings;
}
private void DataFilesListDlg_Load(object sender, EventArgs e)
{
foreach(var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
LimitsCheckbox.Checked = _summaryOverviewSettings.ShowLimits;
}
private void OlvFilePaths_CellRightClick(object sender, CellRightClickEventArgs e)
{
var contextMenuSymbol = new ContextMenuStrip();
ToolStripItem item;
item = contextMenuSymbol.Items.Add("Add sample");
item.Click += ContextMenuAddFilePath;
if (e.Model != null)
{
contextMenuSymbol.Items.Add("-");
item = contextMenuSymbol.Items.Add("Delete sample");
item.Click += ContextMenuDeleteFilePath;
}
olvFilePaths.ContextMenuStrip = contextMenuSymbol;
}
private void ContextMenuAddFilePath(object sender, EventArgs e)
{
var item = new SummaryDataset()
{
SampleName = "Sample",
Path = "Path"
};
_localSummaryDatasets.Add(item);
// Rebuild the list in the GUI
olvFilePaths.ClearObjects();
foreach (var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
olvFilePaths.AutoResizeColumns();
}
private void ContextMenuDeleteFilePath(object sender, EventArgs e)
{
if (olvFilePaths.SelectedObject != null)
{
var item = (SummaryDataset)olvFilePaths.SelectedObject;
olvFilePaths.RemoveObject(item);
_localSummaryDatasets.Remove(item);
}
}
private void OlvFilePaths_CellEditFinished(object sender, CellEditEventArgs e)
{
if (e.Control is TextBox textBox)
{
var oldValue = (string)e.Value;
var newValue = (string)e.NewValue;
var col = e.Column.AspectName;
var index = e.ListViewItem.Index;
if (newValue != oldValue)
{
if (col == "SampleName")
{
_localSummaryDatasets[index].SampleName = newValue;
}
else
{
_localSummaryDatasets[index].Path = newValue;
}
}
}
// Rebuild the list in the GUI
olvFilePaths.ClearObjects();
foreach (var dataFile in _localSummaryDatasets)
{
olvFilePaths.AddObject(dataFile);
}
olvFilePaths.AutoResizeColumns();
}
private void OkButton_Click(object sender, EventArgs e)
{
_summaryOverviewSettings.Datasets.Clear();
_summaryOverviewSettings.Datasets.AddRange(_localSummaryDatasets);
_summaryOverviewSettings.ShowLimits = _includeLimits;
DialogResult = DialogResult.OK;
Close();
}
private void ButtonCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void LimitsCheckbox_CheckedChanged(object sender, EventArgs e)
{
_includeLimits = LimitsCheckbox.Checked;
}
}

Custom ToggleButton doesn't respond to ManipulationCompleted event

I have a custom UI element that inherits from System.Windows.Controls.Primitives.ToggleButton. I'm also routing my mouse events through a custom TouchDevice that raises touch events instead.
For some reason, the ManipulationCompleted event never fires. First, the custom TouchDevice can be found here: http://blakenui.codeplex.com/SourceControl/changeset/view/67526#Blake.NUI.WPF/Touch/MouseTouchDevice.cs
Here are the relevant parts of my class:
public class ToggleSwitch: ToggleButton {
private Grid _root;
private readonly IList<int> _activeTouchDevices;
private const double UNCHECKED_TRANSLATION = 0;
private TranslateTransform _backgroundTranslation;
private TranslateTransform _thumbTranslation;
private Grid _root;
private Grid _track;
private FrameworkElement _thumb;
private double _checkedTranslation;
private double _dragTranslation;
private bool _wasDragged;
private bool _isDragging;
public override void OnApplyTemplate()
{
...
MouseTouchDevice.RegisterEvents(_root);
_root.IsManipulationEnabled = true;
_root.TouchDown += OnTouchDown;
_root.TouchUp += OnTouchUp;
_root.GotTouchCapture += OnGotTouchCapture;
_root.LostTouchCapture += OnLostTouchCapture;
_root.ManipulationStarted += OnManipulationStarted;
_root.ManipulationDelta += OnManipulationDelta;
_root.ManipulationCompleted += OnManipulationCompleted;
}
private void OnTouchDown(object sender, TouchEventArgs e)
{
e.TouchDevice.Capture(_root);
}
private void OnGotTouchCapture(object sender, TouchEventArgs e)
{
if (e.TouchDevice.Captured == _root)
{
Manipulation.AddManipulator(_root,e.TouchDevice);
_activeTouchDevices.Add(e.TouchDevice.Id);
}
}
private void OnManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
e.Handled = true;
_isDragging = true;
_dragTranslation = Translation;
ChangeVisualState(true);
Translation = _dragTranslation;
}
private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
e.Handled = true;
var horizontalChange = e.DeltaManipulation.Translation.X;
var direction = Math.Abs(horizontalChange) >= Math.Abs(e.DeltaManipulation.Translation.Y) ? Orientation.Horizontal : Orientation.Vertical;
if (direction == Orientation.Horizontal && horizontalChange != 0.0)
{
_wasDragged = true;
_dragTranslation += horizontalChange;
Translation = Math.Max(UNCHECKED_TRANSLATION, Math.Min(_checkedTranslation, _dragTranslation));
}
}
private void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
e.Handled = true;
_isDragging = false;
var click = false;
if (_wasDragged)
{
var edge = (IsChecked ?? false) ? _checkedTranslation : UNCHECKED_TRANSLATION;
if (Translation != edge)
{
click = true;
}
}
else
{
click = true;
}
if (click)
{
OnClick();
}
_wasDragged = false;
}
}
The OnManipulationCompleted method is never entered.

c# visual studio custom control binding issue

I would like to create a simple custom control (actually a type of button). I have created the control but the step I am missing is how to add the binding. The control code I have looks like this:
public partial class PopUpButton : UserControl
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
public delegate void ChangedEventHandler(object sender, EventArgs e);
public event ChangedEventHandler OnValueChanged;
private bool _checked;
private String _text;
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void DrawButton()
{
// do some stuff
}
private void PopUpButton_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
if (OnValueChanged != null)
{
OnValueChanged.Invoke(this, new EventArgs());
}
}
}
The call to bind to the control looks like this:
regControl1.DataBindings.Clear();
regControl1.DataBindings.Add("Checked", CustomButton1, "Checked");
I know that I need to define a data source and member but cannot see how to implement this. When the above binding is called then regControl1 updates with the value of "Checked" however the function "OnValueChanged" is always null so the binding has failed, thus when "Checked" changes "regControl1" is not updated.
Ideas anyone?
Finally got something working. This solution handles a click both on the body and on the label and re-sizes the component during design. I was almost there but hope this helps:
public partial class PopUpButton : UserControl, INotifyPropertyChanged
{
public PopUpButton()
{
InitializeComponent();
_checked = false;
DrawButton();
}
private bool _checked;
private String _text;
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler ButtonClick;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
protected void Button_Click(object sender, EventArgs e)
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
if (this.ButtonClick != null)
this.ButtonClick(this, e);
}
private void UserControl1_Resize(object sender, EventArgs e)
{
DrawButton();
}
[Bindable(true)]
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
DrawButton();
NotifyPropertyChanged("Checked");
}
}
[Bindable(true)]
public String DisplayText
{
get
{
return _text;
}
set
{
_text = value;
DrawButton();
}
}
private void HandleClick( )
{
_checked = !_checked;
DrawButton();
NotifyPropertyChanged("Checked");
}
private void DrawButton()
{
//do some drawing stuff here
}
private void PopUpButton_Resize(object sender, EventArgs e)
{
DrawButton();
}
}

Editing text of multiple textboxes with function

I`m not able to find answer.
I have several text boxes and if user enters/leaves them I check if he has changed something and the code below works.
private void txtRegNazov_Enter(object sender, EventArgs e)
{
if (txtRegNazov.Text == "n/a")
{txtRegNazov.Text = "";}
}
private void txtRegNazov_Leave(object sender, EventArgs e)
{
if (txtRegNazov.Text == "")
{txtRegNazov.Text = "n/a";}
}
I would like to create a function like
public void ClearFieldDataByEnter()
{
thisHlep.text = "";
}
public void FieldDataByleave()
{
thisHelp.text = "n/a";
}
And then in every field event would be something like:
private void txtRegNazov_Enter(object sender, EventArgs e)
{
thisHelp.Name = name of this txtBox;
ClearFieldDataByEnter();
}
This is only an easy example of what I want
... I am looking for principe ... and I`m still new to C#.
Thank you
Rember that the "sender", in this case, is the actual TextBox.
TextBox txtSender = (TextBox)sender;
You can use the sender parameter, like this:
private void txtRegNazov_Enter(object sender, EventArgs e)
{
ClearFieldDataByEnter(sender);
}
private void txtRegNazov_Leave(object sender, EventArgs e)
{
FieldDataByleave(sender);
}
public void ClearFieldDataByEnter(object text)
{
textBox = text as TextBox;
if (textbox == null)
return;
if (textbox.Text == "n/a")
{
textbox.Text = String.Empty;
}
}
public void FieldDataByleave(object text)
{
textBox = text as TextBox;
if (textbox == null)
return;
if (String.IsNullOrEmpty(textbox.Text))
{
textbox.Text = "n/a";
}
}

Categories

Resources