Trying to implement drag and drop between 2 listboxes and all examples I've seen so far don't really smell good.
Can someone point me to or show me a good implementation?
Here's a sample form. Get started with a new WF project and drop two list boxes on the form. Make the code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
listBox1.Items.AddRange(new object[] { "one", "two", "three" });
listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
listBox1.MouseMove += new MouseEventHandler(listBox1_MouseMove);
listBox2.AllowDrop = true;
listBox2.DragEnter += new DragEventHandler(listBox2_DragEnter);
listBox2.DragDrop += new DragEventHandler(listBox2_DragDrop);
}
private Point mDownPos;
void listBox1_MouseDown(object sender, MouseEventArgs e) {
mDownPos = e.Location;
}
void listBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button != MouseButtons.Left) return;
int index = listBox1.IndexFromPoint(e.Location);
if (index < 0) return;
if (Math.Abs(e.X - mDownPos.X) >= SystemInformation.DragSize.Width ||
Math.Abs(e.Y - mDownPos.Y) >= SystemInformation.DragSize.Height)
DoDragDrop(new DragObject(listBox1, listBox1.Items[index]), DragDropEffects.Move);
}
void listBox2_DragEnter(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
if (obj != null && obj.source != listBox2) e.Effect = e.AllowedEffect;
}
void listBox2_DragDrop(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
listBox2.Items.Add(obj.item);
obj.source.Items.Remove(obj.item);
}
private class DragObject {
public ListBox source;
public object item;
public DragObject(ListBox box, object data) { source = box; item = data; }
}
}
the proper way to do a drag-drop control in .net is by running code in the 2nd control's DragDrop event handler.
It may "smell" weird, but this is how it works in .NET.
Google gave this: http://www.codeproject.com/KB/dotnet/csdragndrop01.aspx
It seems a pretty reasonable tutorial. If it smells bad, I think that's more to do with the API for drag and drop being awkward to use rather than the tutorial itself being poor.
Related
As you can see below, I want to start moving when the component visibility changes.
because otherwise I need the user to click again to start the movement, and that is bad in terms of usability for my application.
public MoveILayoutControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(MoveILayoutControl_IsVisibleChanged);
this.moveThumb.DragDelta += new DragDeltaEventHandler(MoveThumb_DragDelta);
}
void MoveILayoutControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.IsVisible)
{
// Raise Drag Event !?
}
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var myData = DataContext as ILayoutVisual;
if (myData != null)
{
Point dragDelta = new Point(e.HorizontalChange, e.VerticalChange);
if (myData.Rotation != 0)
{
Matrix toCalculate = ((this.Parent as FrameworkElement).RenderTransform).Value;
if (toCalculate != null)
{
dragDelta = toCalculate.Transform(dragDelta);
}
}
myData.X += dragDelta.X;
myData.Y += dragDelta.Y;
}
}
I believe the only way is using reflection to change the internal values of the thumb. Changing the property "IsDragging" (not tested).
I looked up the source code of Thumb and I think a better way is to simulate a MouseLeftButtonEvent on the thumb:
var evt = new MouseButtonEventArgs(mouseDevice, timestamp, MouseButton.Left)
{
RoutedEvent = UIElement.MouseLeftButtonDownEvent
};
thumb.RaiseEvent(evt);
We know very well how easy is too create a WPF application where user can paint a rectangle using the mouse. To do this you just create a Rectangle control and set its coordinates, you don't worry about DoubleBuffering, repainting and such stuff. Well, I'd be very happy yo use WPF for the application where user can paint different shapes, but the clients insists to be a WinForms application. So the solution here is to use the XOR or ROP operation like in old good WinAPI years and I don't really like this. This doesn't give me a nice option to move a text while in XOR mode.
So I was thinking how can I achieve same smooth painting experience in a WinForms application like I'd have in WPF. Put together such a code, where I wanted to create a separate layer where I'd paint the current shape, while leaving intact the rest of the objects. I used same technique in an iPad application and worked pretty well.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace TestPainting
{
public partial class Form1 : Form
{
private bool _isMouseDown;
private Graphics _bufferGraphics;
private Point _startPos;
private TransparentPanel _paintBuffer;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
_isMouseDown = true;
_paintBuffer = new TransparentPanel
{
Size = Size,
};
Controls.Add(_paintBuffer);
_paintBuffer.BringToFront();
_bufferGraphics = Graphics.FromHwnd(_paintBuffer.Handle);
_startPos = e.Location;
Capture = true;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!_isMouseDown)
return;
_bufferGraphics.Clear(Color.Transparent);
_bufferGraphics.DrawRectangle(Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y);
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
_isMouseDown = false;
Capture = false;
_bufferGraphics.Dispose();
Controls.Remove(_paintBuffer);
_paintBuffer.Dispose();
}
}
public class TransparentPanel : Panel
{
public TransparentPanel()
{
DoubleBuffered = true;
}
[Browsable(false)]
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do Nothing
}
}
}
which if course doesn't work as needed. I'm getting a black panel when pressing the mouse instead of a transparent one. Plus the rectangle while is painted flickers a lot, even though I did set the DoubleBuffering stuff.
Can someone provide some better ideas of such an implementation or maybe there some other open source project where I can see how other people are doing. I'd need to have same experience as in Paint.NET, just too bad is not open source anymore. (I know I can use Reflector, and I did, but man, there is tons of code over there :) )
Thx for any ideas.
Try this (see FIX #1 and FIX #2):
private void Form1_MouseDown( object sender, MouseEventArgs e )
{
_isMouseDown = true;
_paintBuffer = new TransparentPanel
{
Size = Size,
};
Controls.Add( _paintBuffer );
_paintBuffer.BringToFront();
// FIX #1:
//
this.Refresh();
_bufferGraphics = Graphics.FromHwnd( _paintBuffer.Handle );
_startPos = e.Location;
Capture = true;
}
private void Form1_MouseMove( object sender, MouseEventArgs e )
{
if ( !_isMouseDown )
return;
//FIX #2:
// _bufferGraphics.Clear( Color.Transparent );
_bufferGraphics.Clear( this.BackColor );
_bufferGraphics.DrawRectangle( Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y );
}
How can I create System.Windows.Forms.WebBrowser in background STA thread? I try use some code like this:
var tr = new Thread(wbThread);
tr.SetApartmentState(ApartmentState.STA);
tr.Start();
private void wbThread()
{
CWebBrowser browser = new CWebBrowser();
var text = browser.Navigate("http://site.com", CWebBrowser.EventType.loadCompleted).Body.InnerHtml;
}
CWebBrowser - custom class, wich delegate System.Windows.Forms.WebBrowser object Navigate method and wait until page completed loads. The problem is LoadCompleted event on System.Windows.Forms.WebBrowser object never raises. I found some solution here, but it does not work (can't find method Application.Run() on my WPF app).
public class CWebBrowser : ContentControl
{
public readonly System.Windows.Forms.WebBrowser innerWebBrowser;
private readonly AutoResetEvent loadCompletedEvent;
private readonly AutoResetEvent navigatedEvent;
public enum EventType
{
navigated, loadCompleted
}
public CWebBrowser()
{
innerWebBrowser = new System.Windows.Forms.WebBrowser();
loadCompletedEvent = new AutoResetEvent(false);
navigatedEvent = new AutoResetEvent(false);
System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
host.Child = innerWebBrowser;
Content = host;
innerWebBrowser.DocumentCompleted +=new System.Windows.Forms.WebBrowserDocumentCompletedEventHandler(innerWebBrowser_DocumentCompleted);
innerWebBrowser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(innerWebBrowser_Navigated);
}
void innerWebBrowser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
{
navigatedEvent.Set();
}
void innerWebBrowser_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
if (((sender as System.Windows.Forms.WebBrowser).ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete) || innerWebBrowser.IsBusy)
return;
var doc = innerWebBrowser.Document;
loadCompletedEvent.Set();
}
public System.Windows.Forms.HtmlDocument Navigate(string url, EventType etype)
{
if (etype == EventType.loadCompleted)
loadCompletedEvent.Reset();
else if (etype == EventType.navigated)
navigatedEvent.Reset();
innerWebBrowser.Navigate(url);
if (etype == EventType.loadCompleted)
loadCompletedEvent.WaitOne();
else if (etype == EventType.navigated)
navigatedEvent.WaitOne();
System.Windows.Forms.HtmlDocument doc = null;
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(
delegate
{
doc = innerWebBrowser.Document;
}));
return doc;
}
}
Thansk for all advices and sorry for my bad english :o(
Why don't you use the default WebBrowser control like this?
public MainPage()
{
InitializeComponent();
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(startNavigate);
}
void startNavigate()
{
WebBrowser wb = new WebBrowser();
wb.LoadCompleted += new LoadCompletedEventHandler(wb_LoadCompleted);
wb.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(wb_Navigated);
wb.Navigate(new Uri("http://www.google.com"));
}
void wb_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
// e.Content
}
void wb_LoadCompleted(object sender, NavigationEventArgs e)
{
// e.Content when the document finished loading.
}
Edit: You are using old System.Windows.Forms.WebBrowser control, instead System.Windows.Controls.WebBrowser which is part of WPF.
Hi
I'm trying to get rid of the annoying "About Silverlight" context menu that pops up whenever you right click in a Silverlight application. I've added the usual ways:
In App.xaml
rootVisual.MouseRightButtonDown += ((s, args) => args.Handled = true);
and the same for all ChildWindows.
The problem that persist is in all "pop up"-controls like comboboxes and datepicker calender popup. There I can't get rid of it. I would like to handle the right click in a style that I can make implicit for the entire application. Is this possible? Can I solve it some other smart way?
Best
Daniel
The answer was to inherit the combobox and make a custom control like this:
public class CellaComboBox : ComboBox
{
public CellaComboBox()
{
DropDownOpened += _dropDownOpened;
DropDownClosed += _dropDownClosed;
}
private static void _dropDownClosed(object sender, EventArgs e)
{
HandlePopupRightClick(sender, false);
}
private static void _dropDownOpened(object sender, EventArgs e)
{
HandlePopupRightClick(sender, true);
}
private static void HandlePopupRightClick(object sender, bool hook)
{
ComboBox box = (ComboBox)sender;
var popup = box.GetChildElement<Popup>();
if (popup != null)
{
HookPopupEvent(hook, popup);
}
}
static void HookPopupEvent(bool hook, Popup popup)
{
if (hook)
{
popup.MouseRightButtonDown += popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown += popup_MouseRightButtonDown;
}
else
{
popup.MouseRightButtonDown -= popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown -= popup_MouseRightButtonDown;
}
}
static void popup_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
with the extension method for framworkelement looking like this:
public static class FrameworkElementExtensions
{
public static TType GetChildElement<TType>(this DependencyObject parent) where TType : DependencyObject
{
TType result = default(TType);
if (parent != null)
{
result = parent as TType;
if (result == null)
{
for (int childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); ++childIndex)
{
var child = VisualTreeHelper.GetChild(parent, childIndex) as FrameworkElement;
result = GetChildElement<TType>(child) as TType;
if (result != null) return result;
}
}
}
return result;
}
}
You need to handle the DatePicker in the same way but instead of DropDownOpened and DropDownClosed you use CalenderOpened and CalenderClosed
C# Corner has an article for fixing the about popup on Silverlight 3:
Disable Context Menu in Silverlight 3 Application
I am currently porting a windows forms application to wpf. There is a listbox with filenames in it. It should be possible to drag (multiple) items to the windows explorer.
This was easy in with the old windows form, but I can't find a way how this can be done in wpf.
This was the code I used with windows forms:
void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
string[] files = GetSelection();
if (files != null)
{
DoDragDrop(new DataObject(DataFormats.FileDrop, files), DragDropEffects.Copy );
}
}
Ok... I found a solution for my Problem, based on this tutorial:
private void List_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Store the mouse position
startPoint = e.GetPosition(null);
}
private void List_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance &&
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
if (listView1.SelectedItems.Count == 0)
{
return;
}
string[] files = GetSelection();
string dataFormat = DataFormats.FileDrop;
DataObject dataObject = new DataObject(dataFormat, files);
DragDrop.DoDragDrop(listView1, dataObject, DragDropEffects.Copy);
}
}
Try checking out Bea Stollnitz' blog for a thorough implementation of drag and drop in WPF.
Bea Stollnitz - How can I drag and drop items between data bound ItemsControls?
try this code that help u to solve your problem
public partial class MainWindow : Window
{
List<string> file = new List<string>();
public MainWindow()
{
InitializeComponent();
file.Add(#"D:\file1.txt");
file.Add(#"D:\folder1");
file.Add(#"D:\folder2");
file.Add(#"D:\folder3");
lstTest.DataContext = file;
}
private Point start;
private void lstTest_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.start = e.GetPosition(null);
}
private void lstTest_MouseMove(object sender, MouseEventArgs e)
{
Point mpos = e.GetPosition(null);
Vector diff = this.start - mpos;
if (e.LeftButton == MouseButtonState.Pressed && Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance && Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
if (this.lstTest.SelectedItems.Count == 0)
{
return;
}
string[] Files = new string[file.Count] ;
for (int i = 0; i < file.Count; i++)
{
Files[i] = file[i];
}
string dataFormat = DataFormats.FileDrop;
DataObject dataObject = new DataObject(dataFormat, (lstTest.SelectedItems.Cast<string>()).ToArray<string>());
DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Copy);
}
}
}
make sure you checked ( checkbox ) the multiple selection option