How to prevent panel content from being erased upon scroll - c#

I have a question regarding how I can prevent some content drawn on a panel control from being erased when a scroll action brings it out of view.
What I am trying to do is create a 2D tile-map editor. Whenever a mouse click event happens on the panel, a tile should get drawn onto the panel. I have this working fine. But if I place the object on the panel and scroll to one side, and scroll back, the object I had placed is gone.
I have done some research and I have seen suggestions on implementing the paint event. The problem is that I do not understand what to implement here. I think most of my struggles is coming from not fully understanding the Graphics object.
Here is some of my code:
private void canvas_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = canvas.CreateGraphics();
float x1 = CommonUtils.GetClosestXTile(e.X);
float y1 = CommonUtils.GetClosestYTile(e.Y);
if (currentTile != null)
{
g.DrawImage(currentTile, x1, y1);
me.AddTile((int)currX, (int)currY, (int)x1, (int)y1, "C:\\DemoAssets\\tileb.png");
}
else
{
// dont do anything
}
g.Dispose();
}
private void canvas_Paint(object sender, PaintEventArgs e)
{
// update here?
}

To hold multiple Tiles, you'd need a List to hold each clicked location along with its associated tile:
List<Tuple<Image, PointF>> Tiles = new List<Tuple<Image, PointF>>();
private void canvas_MouseClick(object sender, MouseEventArgs e)
{
if (currentTile != null)
{
float x1 = CommonUtils.GetClosestXTile(e.X);
float y1 = CommonUtils.GetClosestYTile(e.Y);
Tiles.Add(new Tuple<Image, PointF>(currentTile, new PointF(x1, y1)));
canvas.Refresh();
me.AddTile((int)currX, (int)currY, (int)x1, (int)y1, "C:\\DemoAssets\\tileb.png");
}
}
private void canvas_Paint(object sender, PaintEventArgs e)
{
foreach (Tuple<Image, PointF> tile in Tiles)
{
e.Graphics.DrawImage(tile.Item1, tile.Item2);
}
}

Related

How can I remove one drawn object from winform?

I have created a winform and a picturebox, you can draw / place icons on the on the picturebox.
g2.DrawIcon(SystemIcons.Warning, new Rectangle(screenPositionX, screenPositionY, _levelWidth, _levelHeight));
like this
But my problem is I want to be able to remove the warning icons with the press of a button, but I dont know how.
I already tried g2.Clear, but this removes all the icons.
I also tried just drawing over them, but this draws over everything and I can't find the correct background color.
My question is, how do I remove a single drawn object?
You need a field that keeps track if the icons are to be drawn or not. Flip the value as a result of button presses, and when it comes time to draw (paint event) use the value to determine what to draw.
public partial class Form1 : Form
{
bool showIcons = true;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var wt = pictureBox1.ClientSize.Width;
var ht = pictureBox1.ClientSize.Height;
// draw grid
for (int i = 0; i < wt; i+=32)
{
e.Graphics.DrawLine(Pens.Black, i, 0, i, ht);
}
for (int j = 0; j < ht; j+=32)
{
e.Graphics.DrawLine(Pens.Black, 0, j, wt, j);
}
if (showIcons)
{
// draw icons
e.Graphics.DrawIcon(SystemIcons.Warning, 5*32-1, 2*32-1);
}
}
private void drawButton_Click(object sender, EventArgs e)
{
showIcons = true;
pictureBox1.Refresh();
}
private void clearButton_Click(object sender, EventArgs e)
{
showIcons = false;
pictureBox1.Refresh();
}
}
After you have painted something on a paper, you can't really take it back without either painting it over completely with the correct color, or discarding (clearing) everything and repainting only those objects that should remain. The same goes for a PictureBox.
If this icon is the only one that can be visible sometimes, and not visible other times, you could introduce a bool field in your form class. If that bool is true, you draw the icon, otherwise you don't. Then, when the user clicks the button, you can change the value of that field, and refresh the form.
Since you are showing very little code, I don't know any names of classes or methods in your solution, so consider this pseudo code!
// Pseudo code
class MyForm : Form
{
// The field that decides whether to draw the icon
private bool showWarningIcon = true;
// The button click handler
public void OnButtonClick()
{
showWarningIcon = false;
Invalidate();
}
// The paint handler
public override void OnPaint(PaintEventArgs e)
{
// Draw other things, then:
if (showWarningIcon)
{
g2.DrawIcon(SystemIcons.Warning, new Rectangle(screenPositionX, screenPositionY, _levelWidth, _levelHeight));
}
}
}

Is it possible to implement a function that in C# Winforms that reorganize the elements as Android

I have this menu (Android style), which is in a FlowLayoutPanel that organizes the elements (Bunifu Tile Buttons):
Well, I thought about implementing a drag function where you could reposition the elements in execution time by dragging them with the mouse as it is done in Android.
To do this, I used a FlowLayoutPanel to organize the elements and this code::
...
Interval = 100, Enabled = true;
private void Timer_0_Tick(object sender, EventArgs e)
{
var cursor = Cursor.Position;
if(bunifuTileButton1.DisplayRectangle.Contains(cursor))
{
if(Hector.Framework.Utils.Peripherals.Mouse.MouseButtonIsDown(Hector.MouseButton.Left))
{
bunifuTileButton1.Location = cursor;
}
}
}
But when I drag the elements, they simply return to their original position by releasing the mouse button.
So, my question is:
Is it possible to implement a function that in C # Winforms that reorganize the elements as Android does using a FlowLayoutPanel that automatically organizes the elements in execution time?
Firstly , you need to implement the functionality which will help you drag-and-drop your controls.But before that, you need to store the control's current position in some class-level variables.Consider the following code snippet :
int xloc;
int yloc;
///other codes in-between
private void btn1_Click()
{
xloc = this.btn1.Location.X;
yloc = this.btn1.Location.Y;
}
Now the drag-and-drop feature :
private Point setNewLocation;
private void btn1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
setNewLocation= e.Location;
}
}
private void btn1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
btn1.Left = e.X + btn1.Left - setNewLocation.X;
btn1.Top = e.Y + btn1.Top - setNewLocation.Y;
}
}
This will help you move the control...But what happens when you place a control over another one ? I mean you definitely don't want one control to overlap the other.Rather you may want to,when one control is placed over the other, the other control will change it's position to the previous position of the control that is overlapping it.So, on the MouseUP event of the currently dragged control , use this :
private void btn1_MouseUP()
{
foreach (Control other in FlowlayoutPanel1.Controls)
{
///change control type as required
if (!other.Equals(btn1) && other is Button && btn1.Bounds.IntersectsWith(other.Bounds))
{
other.Location = new Point(xloc, yloc)
}
}
I haven't debugged the code so if you encounter any bug, make sure to leave a comment :)

WinForm not dragging smoothly when moving by client area

Whenever I move a Windows Form by some component (i.e. a Label) in the client area, I end up with a strange mouse offset in which the form does not stay visually underneath the cursor. It will still move according to my mouse location on the screen, but it dramatically shifts southeast of the cursor's position.
I've had to specify a negative offset of my own to counteract this offset; my code is as follows:
private void component_MouseDown(object sender, MouseEventArgs e)
{
if (sender is Label)
{
if (e.Button == MouseButtons.Left)
{
mouseLoc = new Point(-(e.X + OFFSET_X), -(e.Y + OFFSET_Y));
isMouseDown = true;
}
}
}
private void component_MouseMove(object sender, MouseEventArgs e)
{
if (isTitleLabelMouseDown)
{
Point p = Control.MousePosition;
p.Offset(mouseLoc);
Location = p;
}
}
private void component_MouseUp(object sender, MouseEventArgs e)
{
isMouseDown = false;
}
This offset does fix the problem, but what throws me for a loop is why the form's location offsets when I move it by its client area in the first place?
Thanks!
You seem to be translating client coordinates to screen coordinates. There is a better way...
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.pointtoscreen%28v=vs.110%29.aspx
Edit: And of course there's a better way to do this whole thing. Basically, you want to intercept the click higher up the chain and tell Windows that the click is actually in the window title, which will cause Windows to do the dragging for you...
Winforms - WM_NCHITEST message for click on control

Rectangle around selected ListView item

I would like to draw a rectangle around the selected item in a ListView, due to reading somewhere that Microsoft recommends against changing the 'highlighted colour' of said item. However, I'm using the selectedIndexChanged event and when the actual listviewitem is drawn my rectangle disappears. An educated guess would suggest my rectangle is either behind it or has being cleared when it has being redrawn? So my question is, when would be the best time to actually draw the rectangle as so it is visible? my code so far can be seen below:
void lvMain_SelectedIndexChanged(object sender, EventArgs e)
{
if (lvMain.SelectedItems.Count > 0)
{
if (lastSelectedItem == null) // First time called
{
lastSelectedItem = (sender as System.Windows.Forms.ListView).SelectedItems[0];
DrawHighlightRectanlge(lastSelectedItem);
}
else
{
// TODO: Remove previous highlight
lastSelectedItem = (sender as System.Windows.Forms.ListView).SelectedItems[0];
DrawHighlightRectanlge(lastSelectedItem);
}
}
}
internal void DrawHighlightRectanlge(System.Windows.Forms.ListViewItem item)
{
using (Graphics g = item.ListView.CreateGraphics())
{
g.DrawRectangle(new Pen(Color.Red), new Rectangle(item.Position.X, item.Position.Y, item.Bounds.Width, item.Bounds.Height));
}
}
TIA
Here is a very basic version for an owner-drawn ListView. Set the OwnerDraw property to true and code the DrawItem event, maybe like this:
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawText();
if (e.Item.Selected)
{
Rectangle R = e.Bounds;
R.Inflate(-1, -1);
using (Pen pen = new Pen(Color.Red, 1.5f))
e.Graphics.DrawRectangle(pen, R);
}
}
I make the rectangle a little smaller for it to work in Details View, but you should play around to make it suit your needs and fancy..!
Note: If you have ColumnHeaders you also need to code the DrawColumnHeader event, in its simplest form like this:
private void listView1_DrawColumnHeader(object sender,
DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
And if you have SubItems you need to have a DrawSubItem event, again at least like this:
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
e.DrawDefault = true;
}
Obviously you need to write more code to this event, if you want your rectangle to be drawn here as well. But the default function of DrawBackground and DrawText are available here as well.

How do i get the mouse coordinates X and Y in form1 mouse enter event?

private void Form1_MouseEnter(object sender, EventArgs e)
{
}
e doesn't have the properties X and Y.
I want when the mouse cursor move over a control it will do something.
You can get the current cursor position using the Cursor.Current static property:
var x = Cursor.Current.Position.X;
var y = Cursor.Current.Position.Y;
Note that the MouseEnter event only fires when the cursor enters the control boundary. That may be what you want, but your last sentence seems to indicate you want to know when the mouse moves within a control. In that case, MouseMove may be a more appropriate event to handle.
Use the Control.MousePosition static property as follows:
void Form1_MouseEnter(object sender, EventArgs e) {
Point screenPosition = MousePosition;
Point clientPosition = PointToClient(screenPosition);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Point p = e.Location;
}

Categories

Resources