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.
Related
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));
}
}
}
I'm trying to make a ListBox which holds items of variable size. I got that much to work, as can be seen here:
But as you can see, the first item seems to be ignored or something. What's going on here and how I make it also be affected by the ItemHeight property?
My code looks like this:
private void ClassicEvent_Load(object sender, EventArgs e)
{
eventCommands.DrawMode = DrawMode.OwnerDrawVariable;
eventCommands.MeasureItem += EventCommands_MeasureItem;
eventCommands.DrawItem += EventCommands_DrawItem;
eventCommands.Items.Add("Text: I heard you were going to the Pokémon Centre.");
eventCommands.Items.Add("if (Has Pokemon (Bulbasaur) in PC)");
eventCommands.Items.Add(" Text: You have a Bulbasaur.");
eventCommands.Items.Add("else");
eventCommands.Items.Add(" Text: You don't have a Bulbasaur.");
eventCommands.Items.Add("end");
eventCommands.Items.Add("Text: So be it, then.");
}
private void EventCommands_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
e.Graphics.DrawString(eventCommands.Items[e.Index].ToString(), e.Font, new SolidBrush(e.ForeColor), e.Bounds);
e.DrawFocusRectangle();
}
private void EventCommands_MeasureItem(object sender, MeasureItemEventArgs e)
{
eventCommands.ItemHeight = 48;
}
I have tried changing the e.Bounds rectangle and also tried changing ItemHeight if the item iterated over was the first one, but I can't figure it out.
In EventCommands_MeasureItem, apparently MeasureItemEventArgs e also contains an ItemHeight property. Using that does work.
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);
}
}
I'm new in this forum, and my english is not perfect, so i want to excuse if my question isn't writen good.
I'm making a painting program with C# and all is perfect expect one problem.
When i'm drawing a line, or rectangle or elipse, when the mouseMove event is called the old shapes is drawed too.
How to draw a shape with mouseMove event and the old shapes to not been drawed.
Here is part of my code for more clarification.
//---Variables declared by the Prgrammer---//
//'parent' is variable that take the MdiParent
//'mouseIsDown' is boolean variable
//'startPoint' and 'endPoint' are Point Varables
//'pen' is Pen variable that is configured eralier
//'graphic' is pictureBox in the same form.
private void pbx_MouseDown(object sender, MouseEventArgs e)
{
if (parent.btnLine.Checked)
{
mouseIsDown = true;
startPoint = new Point(e.X, e.Y);
}
}
private void pbx_MouseMove(object sender, MouseEventArgs e)
{
if (mouseIsDown == true && parent.btnLine.Checked)
{
pen = new Pen(parent.btnPreview.BackColor, 12);
endPoint = new Point(e.X, e.Y);
graphic.DrawLine(pen, startPoint, endPoint);
}
}
private void pbx_MouseUp(object sender, MouseEventArgs e)
{
mouseIsDown = false;
}
Please help me. This is a big problem for me.
Thanks.
Please help me, i'm waiting 2 days.
When i'm making a new drawing to the graphic object, the old graphic is deleted.
I try the graphic.Save() method, but has not give me right result.
How to make the drawings to saty when i'm making another graphic?
As you're using picturebox you can use Invalidate() method.
So, I'm building a little app that has 118 buttons for clicking. It is a seat-chooser for an airplane. Instead of adding 118 buttons, i added an image and included 118 rectangles on it, positioning them correctly.
When the user clicks a rectangle, I can't seem to find a way to identify which seat was clicked... Is there a way to add a name field to the Rectangle class or any other way to solve this?
The Rectangle structure is sealed, so you can't inherit it.
But you can try just making your own class:
public class Seat {
private string _SeatKey;
private Rectangle _SeatRectangle;
public Seat(string seatKey, Rectangle seatRectangle) {
_SeatKey = seatKey;
_SeatRectangle = seatRectangle;
}
public string SeatKey {
get { return _SeatKey; }
}
public Rectangle SeatRectangle {
get { return _SeatRectangle; }
set { _SeatRectangle = value; }
}
}
Example:
private List<Seat> _Seats = new List<Seat>();
public Form1() {
InitializeComponent();
_Seats.Add(new Seat("1a", new Rectangle(10, 10, 10, 10)));
_Seats.Add(new Seat("2b", new Rectangle(20, 20, 10, 10)));
}
private void Form1_Paint(object sender, PaintEventArgs e) {
foreach (Seat seat in _Seats)
e.Graphics.FillRectangle(Brushes.Red, seat.SeatRectangle);
}
private void Form1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
foreach (Seat seat in _Seats) {
if (seat.SeatRectangle.Contains(e.Location))
MessageBox.Show("Clicked on seat " + seat.SeatKey);
}
}
}
Since airplanes can change and have a different number of seats, you can make your program more generic using just labels or buttons.
200 labels or button are not so much in reality, and it work with performances similar to draw text over an image or a panel in the Paint event.
However for your specific problem, you should get the MouseDown event and use X and Y coordinates to understand where the user clicked.
Bit dirty but you can use Cursor.X and Cursor.Y to get the mouse click location. If its going to run in fullscreen you could check the location of the rectangles.