WinForms - WPF like painting - wpf

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 );
}

Related

Disable drag and drop from webbrowser control c#

I am trying to prevent users from dragging images that display in my webbrowser control.
I tried setting webbrowser.Document.MouseOver e.Cursor = Cursors.No Still not working.
I tried a few other ways.
I am unable to prevent images being dragged on the desktop.
Is it possible to prevent dragging of images from webbrowser control to desktop?
There is a way to prevent the body element of the HTML document to handle drag operation.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
this.webBrowser1.Url = new Uri("https://www.google.bg/search?q=stackoverflow&biw=1920&bih=950&source=lnms&tbm=isch&sa=X&sqi=2&ved=0ahUKEwjw2cWH4oTQAhVG8RQKHWCWB4AQ_AUIBigB");
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.webBrowser1.Document.Body.Drag += new HtmlElementEventHandler(Body_Drag);
}
private void Body_Drag(object sender, HtmlElementEventArgs e)
{
e.ReturnValue = false;
}
}

How to submit events to InkCanvas in WPF manually?

How would I be able to submit events manually to be received by InkCanvas ?
What I need to do, is to set the mode of InkCanvas to ink mode, and then, send virtual events to InkCanvas so that I get a drawing behavior as if user used the real mouse.
Thanks
The following code snippet shows an example of drawing a shape in InkCanvas:
StylusPointCollection stroke1Points = new StylusPointCollection();
stroke1Points.Add(new StylusPoint(50,10));
stroke1Points.Add(new StylusPoint(90,50));
stroke1Points.Add(new StylusPoint(10,50));
stroke1Points.Add(new StylusPoint(50,10));
Stroke stroke1 = new Stroke(stroke1Points);
canvas.Strokes.Add(stroke1);
Where canvas is of type InkCanvas. The above generates a triangle in the canvas.
"And yes, you may accept the answer if it helps you."
Something like this?
private void inkSurface_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
inkSurface.CaptureMouse();
_inkStroke = new Stroke(
e.StylusDevice.GetStylusPoints(inkSurface));
_inkStroke.DrawingAttributes.Width = 5;
_inkStroke.DrawingAttributes.Height = 5;
_inkStroke.DrawingAttributes.Color = Colors.Black;
inkSurface.Strokes.Add(_inkStroke);
e.Handled = true;
}
private void inkSurface_MouseMove(object sender, MouseEventArgs e)
{
if (_inkStroke != null)
{
_inkStroke.StylusPoints.Add(
e.StylusDevice.GetStylusPoints(inkSurface));
}
e.Handled = true;
}
private void inkSurface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
inkSurface.ReleaseMouseCapture();
e.Handled = true;
}

Recommendation for Windows.Forms Control that Supports Mouse Panning and Zooming

I found Customize a panel with Autoscroll property at http://www.codeproject.com/KB/miscctrl/CustomAutoScrollPanel.aspx that is wrapper around a Panel with AutoScroll = True.
I like this control because it provides the "performScrollHorizontal" and "performScrollVertical" methods. Yet, it uses the Windows API functions instead of ScrollableControl and its VerticalScroll and HorizontalScroll properties.
Is this a good control to use? I think it should be using ScrollableControl instead of the Windows API. What do you think? Is there a better control available? Do I even need a control? I would think that ScrollableControl provides everything I would need.
EDIT: I found the HScrollBar and VScrollBar controls. Should I be using them?
These two other controls are nice but do not give me a way to control the scroll bars like the above control does.
A scrollable, zoomable, and scalable picture box at
http://www.codeproject.com/KB/miscctrl/ScalablePictureBox.aspx
Pan and Zoom Very Large Images at
http://www.codeproject.com/KB/GDI-plus/PanZoomExample.aspx
What I really want is a control:
that scrolls when the user moves the mouse toward the edge of the control,
allows the user to pan
allows the user to zoom
supports using the mouse with Shift or Ctrl or Alt keys pressed
Any recommendations, help, or areas to look at is greatly appreciated. A control would be nice as I am not that good yet.
Some code to play with. It supports focus, panning and scrolling. Zooming is work-to-do, my laptop's mousepad got in the way of testing it. Use a timer to implement auto-scrolling at the edges.
using System;
using System.Drawing;
using System.Windows.Forms;
class ZoomPanel : Panel {
public ZoomPanel() {
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.AutoScroll = this.TabStop = true;
}
public Image Image {
get { return mImage; }
set {
mImage = value;
Invalidate();
mZoom = 1.0;
this.AutoScrollMinSize = (mImage != null) ? mImage.Size : Size.Empty;
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.Cursor = Cursors.SizeAll;
mLastPos = e.Location;
this.Focus();
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) this.Cursor = Cursors.Default;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
this.AutoScrollPosition = new Point(
-this.AutoScrollPosition.X - e.X + mLastPos.X,
-this.AutoScrollPosition.Y - e.Y + mLastPos.Y);
mLastPos = e.Location;
Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
if (mImage != null) {
mZoom *= 1.0 + 0.3 * e.Delta / 120;
this.AutoScrollMinSize = new Size((int)(mZoom * mImage.Width),
(int)(mZoom * mImage.Height)); \
// TODO: calculate new AutoScrollPosition...
Invalidate();
}
base.OnMouseWheel(e);
}
protected override void OnPaint(PaintEventArgs e) {
if (mImage != null) {
var state = e.Graphics.Save();
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
e.Graphics.DrawImage(mImage,
new Rectangle(0, 0, this.AutoScrollMinSize.Width, this.AutoScrollMinSize.Height));
e.Graphics.Restore(state);
}
//if (this.Focused) ControlPaint.DrawFocusRectangle(e.Graphics,
// new Rectangle(0, 0, this.ClientSize.Width, this.ClientSize.Height));
base.OnPaint(e);
}
protected override void OnEnter(EventArgs e) { Invalidate(); base.OnEnter(e); }
protected override void OnLeave(EventArgs e) { Invalidate(); base.OnLeave(e); }
private double mZoom = 1.0;
private Point mLastPos;
private Image mImage;
}

Drag and Drop between 2 list boxes

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.

Change the background color of Winform ListView headers

How can you change the background color of the Headers of a ListView?
You can do this by setting the OwnerDraw property for the list view to true.
This then allows you to provide event handlers for the listview's draw events.
There is a detailed example on MSDN
Below is some example code to set the header colour to red:
private void listView1_DrawColumnHeader(object sender,
DrawListViewColumnHeaderEventArgs e)
{
e.Graphics.FillRectangle(Brushes.Red, e.Bounds);
e.DrawText();
}
I think (but am happy to be proved wrong) that with OwnerDraw set to true, you will need to also provide handlers for the other draw events that have default implementations as shown below:
private void listView1_DrawItem(object sender,
DrawListViewItemEventArgs e)
{
e.DrawDefault = true;
}
I certainly haven't managed to make the listview draw the items without that.
I know this is a little late to the party but I still saw this post and this would have helped me. Here is a little abstracted application of the code david supplied
using System.Windows.Forms;
using System.Drawing;
//List view header formatters
public static void colorListViewHeader(ref ListView list, Color backColor, Color foreColor)
{
list.OwnerDraw = true;
list.DrawColumnHeader +=
new DrawListViewColumnHeaderEventHandler
(
(sender, e) => headerDraw(sender, e, backColor, foreColor)
);
list.DrawItem += new DrawListViewItemEventHandler(bodyDraw);
}
private static void headerDraw(object sender, DrawListViewColumnHeaderEventArgs e, Color backColor, Color foreColor)
{
using (SolidBrush backBrush = new SolidBrush(backColor))
{
e.Graphics.FillRectangle(backBrush, e.Bounds);
}
using (SolidBrush foreBrush = new SolidBrush(foreColor))
{
e.Graphics.DrawString(e.Header.Text, e.Font, foreBrush, e.Bounds);
}
}
private static void bodyDraw(object sender, DrawListViewItemEventArgs e)
{
e.DrawDefault = true;
}
Then call this in your form constructor
public Form()
{
InitializeComponent();
*CLASS NAME*.colorListViewHeader(ref myListView, *SOME COLOR*, *SOME COLOR*);
}
Just replace the *CLASS NAME* with whatever class you put the first bit of code in and the *SOME COLOR*'s with some sort of color.
//Some examples:
Color.white
SystemColors.ActiveCaption
Color.FromArgb(0, 102, 255, 102);

Resources