How do we make a Panel control scroll whatever's inside of it? I'm not talking about controls or user controls or custom controls. I'm talking only about pixels; drawn with GDI+:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GDITEST
{
public partial class Form1 : Form
{
public class Item
{
public int Height { get; set; }
public int Width { get; set; }
public int Top { get; set; }
}
internal List<Item> items = new List<Item>();
public Form1()
{
InitializeComponent();
}
private void panel_Paint(object sender, PaintEventArgs e)
{
if (items != null)
{
if (items.Count >= 1)
{
foreach (Item item in items)
{
using (Pen pen = new Pen(Color.Blue, 1))
{
int count;
count = items.Count;
count = count >= 1 ? count : 1;
e.Graphics.DrawRectangle(pen, 0, item.Top, (item.Width - SystemInformation.VerticalScrollBarWidth), item.Height);
}
}
}
}
}
private void button1_Click(object sender, EventArgs e)
{
items.Add(new Item() { Width = this.Width, Height = 25, Top = (items.Count * 25) });
panel.Refresh();
}
}
}
The above code draws a blue rectangle (kinda like a vertical list). When the number of rectangles extends the height of the panel, I want the panel to scroll.
I've not been able to find out how to do this, since most of the results only return stuff related to scrolling custom controls.
I did read somewhere (which I can no longer find) that you can use some translateX or translateY methods... Yet I am having a hard time trying to find out anything more about those methods.
There's a simple bug in your code, you forgot to offset what you draw by the scroll amount. The Panel class has two other quirks, it was optimized to be a container control and cuts corners on the way it paints. You get rid of all three issues by creating your derived class from Panel. Project > Add Class > paste the code shown below. Build > Build and drop the new control from the top of the toolbox onto your form.
using System;
using System.Windows.Forms;
class MyPanel : Panel {
public MyPanel() {
this.DoubleBuffered = this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
base.OnPaint(e);
}
}
You can further optimize your Paint event handler by paying attention to the e.ClipRectangle property and skip drawing an item when it is clipped. Just in case: assign the AutoScrollMinSize property to fit the items.
Here's a rough example on how to manually show the scrollbars. The panel contains a red rectangle, which can be clicked and dragged. If the rectangle is moved outside the viewable area, the scrollbars appear.
public class DrawPanel : Panel {
public Rectangle rect = new Rectangle(0, 0, 200, 100);
int offsetX = 0;
int offsetY = 0;
bool grabbing = false;
public DrawPanel() {
Dock = DockStyle.Fill;
AutoScroll = true;
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
var p = e.Location;
if (rect.Contains(p)) {
grabbing = true;
offsetX = rect.X - p.X;
offsetY = rect.Y - p.Y;
}
}
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
grabbing = false;
}
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (grabbing) {
var p = e.Location;
rect.Location = new Point(p.X + offsetX, p.Y + offsetY);
Invalidate();
int right = rect.X + rect.Width;
int bottom = rect.Y + rect.Height;
if (right > Width || bottom > Height) {
this.AutoScrollMinSize = new Size(right + 1, bottom + 1);
}
}
}
protected override void OnScroll(ScrollEventArgs se) {
base.OnScroll(se);
Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var g = e.Graphics;
var p = AutoScrollPosition;
Rectangle r = rect;
r.X += p.X;
r.Y += p.Y;
g.DrawRectangle(Pens.Red, r);
}
}
Related
I have an abstract class "Element" which its instances have to use the function DrawRectangle() from System.Drawing.
The problem is that because the class is abstract it can't inherit from FORM so the program doesn't recognize the classes..
How can I solve this?
I've tried puting a variable of type Form inside Element and didn't work.
I also tried the opposite, puting an Element variable inside Form.
It is my first time with Winform and C#...
It is supposed to be a mouse race game, the objects drawn in page are supposed to chase the mouse or go in other directions but I need it to be object oriented.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyMouseGame
{
public abstract class Element
{
//Form1 myForm = new Form1();// not sure I need it
public enum EnumShape { Square, Circle, Triangle }
public enum EnumType { Chase, Escape, Random }
//variables
private double X { get; set; }
private double Y { get; set; }
private int Size { get; }
private int Speed { get; }
private EnumShape Shape { get; set; }
protected EnumType Type { get; set; }
private static Random R = new Random();
//methods
public int generateRandomNum()
{
int randomNum = R.Next(20, 100);
return randomNum;
}
public Element()
{
X= 0;
Y = 0;
Shape = 0;
Size = generateRandomNum();
Speed = generateRandomNum();
Type = 0;
}
public void ElementsFactory()
{
}
public void hitTarget(e) { }
public abstract void Draw(Form1 g); //here I tried to pass a form inside
public abstract void Move();
}
}
//example of one of the instances classes of element
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyMouseGame
{
class E_Escape : Element
{
int move_circleX = 0;
int move_circleY = 0;
bool flag = false;
Rectangle circle = new Rectangle(10, 70, 35, 35);
Pen p = new Pen(Color.Black);
//circle
public E_Escape()
{
}
public override void Draw(Form1 g) {
g.DrawEllipse(p, circle);
g.FillEllipse(new SolidBrush(Color.Red), circle);
}
public override void Move() {
if (!flag)
{
if (circle.X >= panel1.Width - 2)
flag = true;
circle.X += 10;
}
else
{
if (circle.X <= 30)
flag = false;
circle.X -= 10;
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace MyMouseGame
{
public partial class Form1 : Form
{
Graphics g;
Element [] elements= new Element[3];
public Form1()
{
this.DoubleBuffered = true;
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
g = panel1.CreateGraphics();
//timer set
t.Interval = 100;
t.Tick += new EventHandler(timer1_Tick);
t.Start();
}
private void recThreadTracker()
{
//move rectangle
while (true)//here I tried to call element finction draw
{
elements[0].Draw(g);
elements[1].Draw(g);
elements[2].Draw(g);
}
}
public void threadCounter()
{
long counter = 0;
while (true)
{
counter++;
this.Invoke(new Action(() =>
{
lcountRes.Text = counter.ToString();
}));
Thread.Sleep(1000);
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Thread counterThread = new Thread(threadCounter);
counterThread.Start();
Thread recTrackThread = new Thread(recThreadTracker);
recTrackThread.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
foreach (Element elements in elements)
{
elements.Move();
}
//Refresh();
Invalidate();
}
}
}
The drawing routine needs two things. A Graphics object, and the Size of the drawing area.
For example, to draw a circle on the center of the graphics area
public void Draw(Graphics g, Size target)
{
int diameter = 16;
g.DrawEllipse(Pens.Black, target.Width/2 - diameter/2, target.Height/2 - diameter/2, diameter, diameter);
}
And in your form's paint event you call all the draw functions
void panel1_Paint(object sender, PaintEventArgs e)
{
foreach(var item in Elements)
{
item.Draw(e.Graphics, pictureBox1.ClientSize);
}
}
I'm working on a base for a small simple game-style program and I'm using Form1_Resize to control the scale and location of the premade controls i've placed. Currently they're just a picturebox and two blank buttons for testing however they scale and work perfect when the screen is being resized in this method.
I added a custom control to place images with transparency, and it functions fine as well however when I resize the screen the image from the custom control will disappear. I know it's to do with it being painted over and not being repainted when the screen is being resized. By telling it to refresh in the form1_Resize method it does reappear after resizing is done but still is gone while being resized. I've tried everything I could find really but this is still mostly new to me and i'm having no luck.
Here are my classes below. TestControl is basically a combination of the ScreenDrawing and DrawControl classes with a few tweaks as a test however it didn't seem to make any difference at all.
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
public partial class Form1 : Form
{
int baseFormWidth = 1280;
int baseFormHeight = 720;
int baseStartButtonWidth = 250;
int baseStartButtonHeight = 100;
int baseSettingsButtonWidth = 250;
int baseSettingsButtonHeight = 60;
// When resized, difference of form size and window size
int windowWidthOffset;
int windowHeightOffset;
//Position of button box to center it when scaling screen
int startButtonXOffset;
int startButtonYOffset;
int settingsButtonXOffset;
int settingsButtonYOffset;
public Form1()
{
InitializeComponent();
windowWidthOffset = this.Width - GameWindow.Width;
windowHeightOffset = this.Height - GameWindow.Height;
}
float WidthScale()
{
return (float)this.Width / (float)baseFormWidth;
}
float HeightScale()
{
return (float)this.Height / (float)baseFormHeight;
}
private void Form1_Resize(object sender, EventArgs e)
{
GameWindow.Width = this.Width - windowWidthOffset;
GameWindow.Height = this.Height - windowHeightOffset;
float scaledStartButtonWidth = baseStartButtonWidth * WidthScale();
float scaledStartButtonHeight = baseStartButtonHeight * HeightScale();
StartButton.Width = (int)scaledStartButtonWidth;
StartButton.Height = (int)scaledStartButtonHeight;
float scaledSettingsButtonWidth = baseSettingsButtonWidth * WidthScale();
float scaledSettingsButtonHeight = baseSettingsButtonHeight * HeightScale();
SettingsButton.Width = (int)scaledSettingsButtonWidth;
SettingsButton.Height = (int)scaledSettingsButtonHeight;
startButtonXOffset = GameWindow.Width / 2 - StartButton.Width / 2;
startButtonYOffset = (int)(GameWindow.Height / 1.44) - StartButton.Height / 2;
settingsButtonXOffset = GameWindow.Width / 2 - (SettingsButton.Width / 2);
settingsButtonYOffset = GameWindow.Height - (int)(SettingsButton.Height * 1.25);
Point startButtonLocation = new Point(startButtonXOffset, startButtonYOffset);
Point settingsButtonLocation = new Point(settingsButtonXOffset, settingsButtonYOffset);
StartButton.Location = startButtonLocation;
SettingsButton.Location = settingsButtonLocation;
}
private void Settings_Click(object sender, EventArgs e)
{
}
private void Start_Click(object sender, EventArgs e)
{
}
}
}
ScreenDrawing.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace List_Game
{
abstract public class ScreenDrawing : Panel
{
protected Graphics graphics;
abstract protected void OnDraw();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Update the private member so we can use it in the OnDraw method
this.graphics = e.Graphics;
// Set the best settings possible (quality-wise)
this.graphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAlias;
this.graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
this.graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
this.graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
// Calls the OnDraw subclass method
OnDraw();
}
}
}
DrawControl.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
class DrawControl : ScreenDrawing
{
Image displayedImage = null;
int width;
int height;
Rectangle big;
public void SetImage()
{
if (this.BackgroundImage == null)
{
displayedImage = global::List_Game.Properties.Resources.StartButton2;
}
else displayedImage = this.BackgroundImage;
}
protected override void OnDraw()
{
SetImage();
// Sets the images' sizes and positions
width = displayedImage.Size.Width;
height = displayedImage.Size.Height;
this.Width = width;
this.Height = height;
big = new Rectangle(0, 0, width, height);
// Draws the two images
this.graphics.DrawImage(displayedImage, big);
}
}
}
TestControl.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace List_Game
{
public class TransparentControl : Control
{
private readonly Timer refresher;
private Image _image = global::List_Game.Properties.Resources.StartButton2;
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
refresher = new Timer();
refresher.Tick += TimerOnTick;
refresher.Interval = 50;
refresher.Enabled = true;
refresher.Start();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do not paint background
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
//Hack
public void Redraw()
{
RecreateHandle();
}
private void TimerOnTick(object source, EventArgs e)
{
RecreateHandle();
refresher.Stop();
}
public Image Image
{
get
{
return _image;
}
set
{
_image = global::List_Game.Properties.Resources.StartButton2;
RecreateHandle();
}
}
}
}
I'm plowing through the Dietle C# book one page at a time and I'm stuck.
On page 555, there's the most basic drawing program you can imagine. As you move your mouse around, it's supposed to draw an ellipse on the screen.
Well, mine doesn't.
I've checked everything possible. I've gone onto the Dietel website and downloaded the code and tried that. I think I'm doing something wrong outside of the text-based programming. I mean, there are settings and stuff in the properties windows.
I think I got it all right, but nothing seems to work. But obviously I don't have it all right or it would work.
The full code is a bit longer than what I have below, but even this simplified code doesn't work. It's supposed to draw ellipses any time you have the mouse on the screen. Studio Express does a nice job of catching a lot of syntax errors, but of course it can't catch logic errors. Any ideas as to what's wrong?
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
using (Graphics graphics = CreateGraphics())
{
graphics.FillEllipse(
new SolidBrush(Color.Blue), e.X, e.Y, 20, 20);
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
MouseMove Event??
public Form1()
{
InitializeComponent();
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);
}
I would do it like this:
public partial class Form1 : Form
{
private List<IDrawAble> shapes = new List<IDrawAble>();
private MyEllipse currentlyDrawing;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
currentlyDrawing = new MyEllipse() { X1 = e.X, Y1 = e.Y, X2 = e.X, Y2 = e.Y };
Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
shapes.Add(currentlyDrawing);
currentlyDrawing = null;
Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
foreach (var item in shapes)
{
item.Draw(e.Graphics);
}
if (currentlyDrawing != null)
{
currentlyDrawing.Draw(e.Graphics);
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (currentlyDrawing != null)
{
currentlyDrawing.X2 = e.X;
currentlyDrawing.Y2 = e.Y;
Invalidate();
}
}
}
class MyEllipse : IDrawAble
{
public int X1 { get; set; }
public int Y1 { get; set; }
public int X2 { get; set; }
public int Y2 { get; set; }
public void Draw(Graphics g)
{
g.FillEllipse(new SolidBrush(Color.Blue), X1, Y1, X2 - X1, Y2 - Y1);
}
}
interface IDrawAble
{
void Draw(Graphics g);
}
Make sure all eventhandlers are hooked to the events of the form.
To get rid of the flickering set the DoubleBuffered Property of the Form to true.
What is it doing now?
Just to Add.
Make sure your event MouseMove is mapped with the Method Form1_MouseMove
In the Method Hieght and weight are 20 which means it is circle and not eclipse.
for to debug first replace e.X and e.Y with number in below snippet it is 0
SolidBrush redBrush = new SolidBrush(Color.Red);
// Create location and size of ellipse.
float x = 0.0F;
float y = 0.0F;
float width = 200.0F;
float height = 100.0F;
using (Graphics graphics = CreateGraphics())
{
graphics.FillEllipse(redBrush, x, y, width, height);
}
I am building a Windows form application and I have a text box for searching purposes.
I would like to put a search icon inside the text box, at the right or left
like this:
I would prefer at the right
Update 1
I am asking about Windows forms not ASP.net or MVC
You can use a Panel, a TextBox and a PictureBox.
The TextBox must be placed in a Panel so you can't write over your search picture.
You can create a new UserControl which will do the required job. You have to extend the TextBox class for that. Look at the code below:
public class IconTextBox : System.Windows.Forms.TextBox
{
public IconTextBox() : base() { SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true); this.Multiline = true; }
public System.Drawing.Bitmap BitmapImage
{
set;
get;
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
System.Drawing.Image img = BitmapImage as System.Drawing.Image;
e.Graphics.DrawImage(img, new System.Drawing.Point(this.Width - (img.Width), 0));
}
}
And in the OnPaint method you can specify the image. Also you can extend this to have a custom property which can be the image path. Your choice.
Along the lines of Atanas's answer I found the following to work well. The SearchImage and CancelSearchImage properties can be set to control the images used.
public class SearchTextBox : TextBox
{
private const int EM_SETMARGINS = 0xd3;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private PictureBox searchPictureBox;
private Button cancelSearchButton;
public SearchTextBox()
{
cancelSearchButton = new Button();
cancelSearchButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
cancelSearchButton.Size = new Size(16, 16);
cancelSearchButton.TabIndex = 0;
cancelSearchButton.TabStop = false;
cancelSearchButton.FlatStyle = FlatStyle.Flat;
cancelSearchButton.FlatAppearance.BorderSize = 0;
cancelSearchButton.Text = "";
cancelSearchButton.Cursor = Cursors.Arrow;
Controls.Add(cancelSearchButton);
cancelSearchButton.Click += delegate
{
Text = "";
Focus();
};
searchPictureBox = new PictureBox();
searchPictureBox.Anchor = AnchorStyles.Top | AnchorStyles.Right;
searchPictureBox.Size = new Size(16, 16);
searchPictureBox.TabIndex = 0;
searchPictureBox.TabStop = false;
Controls.Add(searchPictureBox);
// Send EM_SETMARGINS to prevent text from disappearing underneath the button
SendMessage(Handle, EM_SETMARGINS, (IntPtr)2, (IntPtr)(16 << 16));
UpdateControlsVisibility();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
UpdateControlsVisibility();
}
private void UpdateControlsVisibility()
{
if (string.IsNullOrEmpty(Text))
{
cancelSearchButton.Visible = false;
searchPictureBox.Visible = true;
}
else
{
cancelSearchButton.Visible = true;
searchPictureBox.Visible = false;
}
}
[Browsable(true)]
public Image SearchImage
{
set
{
searchPictureBox.Image = value;
searchPictureBox.Left = Width - searchPictureBox.Size.Width - 4;
searchPictureBox.Top = Height - searchPictureBox.Size.Height - 4;
}
get { return searchPictureBox.Image; }
}
[Browsable(true)]
public Image CancelSearchImage
{
set
{
cancelSearchButton.Image = value;
cancelSearchButton.Left = Width - searchPictureBox.Size.Width - 4;
cancelSearchButton.Top = Height - searchPictureBox.Size.Height - 4;
}
get { return cancelSearchButton.Image; }
}
}
Using a user control, or adding code every time you want to do this can get very cumbersome. The way I handle this is to add an initializer class which can be called from my form at runtime. The behavior of this code is when the user starts typing, the image disappears. If the textbox has no content, then the image shows up. I handle the click event for the picture box to set focus to the textbox to preserve the illusion that it is part of the control, and offset the left in order to allow the | to be displayed showing that the textbox has focus and is ready to receive input.
By writing a controller instead of a user control, I avoid having to propagate all of the events and properties from the textbox through my user control. This class is dependent on System.Windows.Forms, and can either be included directly in your Windows Forms application, or added to a control library which can be called from multiple applications.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApplication1
{
class TextBoxIcon
{
public static TextBoxIcon AddIcon(TextBox textbox, Image icon)
{
if (icon != null) {
return new TextBoxIcon(textbox, icon);
} else {
return null;
}
}
private TextBox _TextBox;
private PictureBox _PictureBox;
private TextBoxIcon(TextBox textbox, Image icon) {
this._TextBox = textbox;
this._PictureBox = new PictureBox();
this._PictureBox.BackColor = textbox.BackColor;
this._PictureBox.Image = ScaleImage(icon);
this._TextBox.Parent.Controls.Add(_PictureBox);
this._PictureBox.Location = new Point(textbox.Left + 5, textbox.Top + 2);
this._PictureBox.Size = new Size(textbox.Width - 10, textbox.Height - 4);
this._PictureBox.Anchor = textbox.Anchor;
this._PictureBox.Visible = _TextBox.Visible;
this._PictureBox.BringToFront();
textbox.Resize += TextBox_Resize;
textbox.TextChanged += TextBox_TextChanged;
textbox.Leave += TextBox_Leave;
_PictureBox.Click += PictureBox_Click;
textbox.VisibleChanged += TextBox_VisibleChanged;
}
public static Image ScaleImage(Image img) {
if (img.Height == 16) {
return img;
} else {
return new Bitmap(img, new Size((int)((img.Height / 16.0) * img.Width), 16));
}
}
private void TextBox_Resize(Object sender, EventArgs e) {
_PictureBox.Size = new Size(_TextBox.Width - 10, _TextBox.Height - 4);
}
private void TextBox_VisibleChanged(Object sender, EventArgs e) {
_PictureBox.Visible = _TextBox.Visible;
}
private void ShowPictureBox() {
_PictureBox.Visible = String.IsNullOrEmpty(_TextBox.Text);
}
private void TextBox_TextChanged(Object sender, EventArgs e) {
ShowPictureBox();
}
private void TextBox_Leave(Object sender, EventArgs e) {
ShowPictureBox();
}
public void PictureBox_Click(object sender, EventArgs e) {
_TextBox.Focus();
}
}
}
Here is how the class would be used from the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
TextBoxIcon.AddIcon(txtSearch, Properties.Resources.search);
}
}
}
As long as the text box exists, and has been added to the form, the call can happen at any time.
I suggest to use RichTextBox instead of TextBox .And you can set the BackgroundImage property in the Load of the form :
public Form1()
{
InitializeComponent();
richTextBox1.BackgroundImage = Image.FromFile("image path");
}
And you must add the handling of events like TextChanged and leave to show and Hide the background image
Following is my code. i am trying to draw line, filled rectangle, etc.....
Problem is that, lets suppose i draw a line but when i try to draw an other line first drawn line disappears. so i want help that i'll be able to draw multiple shapes on a form and first draw lines don't disappears.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace finalPaint
{
public partial class Form1 : Form
{
List<Point> points = new List<Point>();
Rectangle rect;
Point first;
Point last;
string op;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
points.Add(e.Location);
rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
last = e.Location;
this.Invalidate();
this.Update();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
switch (op)
{
case "toolStripButton1":
{
if (points.Count > 2)
{
e.Graphics.DrawLines(Pens.Black, points.ToArray());
}
}
break;
case "toolStripButton2":
{
using (Pen pen = new Pen(Color.Red, 2))
{
e.Graphics.DrawRectangle(pen, rect);
}
}
break;
case "toolStripButton3":
{
Pen pen = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen, first, last);
this.Update();
}
break;
case "toolStripButton4":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillRectangle(pen, rect);
}
}
break;
case "toolStripButton5":
{
using (SolidBrush pen = new SolidBrush(Color.Red))
{
e.Graphics.FillEllipse(pen, rect);
}
}
break;
case "toolStripButton6":
{
using (Pen pen = new Pen(Color.Red,2))
{
e.Graphics.DrawEllipse(pen, rect);
}
}
break;
default:
break;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
rect = new Rectangle(e.X, e.Y, 0, 0);
first = e.Location;
this.Invalidate();
}
private void toolStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
private void selectedButton(object sender, EventArgs e)
{
foreach (ToolStripButton btn in toolStrip1.Items)
{
btn.Checked = false;
}
ToolStripButton btnClicked = sender as ToolStripButton;
btnClicked.Checked = true;
op = btnClicked.Name;
}
}
}
On each Paint event, you need to paint all of the objects you want on the screen. You are not just painting on top of what is already there. You are repainting the entire scene.
The reason for this is that your control may be obscured from view at some point, and Windows will repaint it when it is revealed again.
If you want to keep a memory of all the the objects, you need to store them in your code. Since each object is different (lines, rectangles, ellipses) you will want to store them in a manner that lets you differentiate. You could create classes like this:
public class DrawingShape
{
public string Name { get; set; }
public DrawingShapeType Type { get; set; }
// other shared properties
public virtual void Draw(Graphics g)
{
}
}
public class DrawingRectangle : DrawingShape
{
public DrawingRectangle()
{
Name = "Rectangle";
Type = DrawingShapeType.Rectangle;
}
public override void Draw(Graphics g)
{
// draw this shape
}
}
public enum DrawingShapeType
{
Rectangle,
Ellipse,
Line,
}
Then you can just store all your objects in a List. The order of the items in the list is your z-order, so you add items to the list and you enumerate through the list in your Paint event and draw each one differently depending on the type.
From here you can store pen and brush information in the class and other info. Your Paint event can tell each class to paint itself and it doesn't need to know which type they are.
You need a possibility to store all your shapes, in order to draw them all in the Paint event, since the background of the form is repainted before each call to Paint. Let us define a base class from which we will derive all the shapes
abstract class Shape
{
public abstract void Paint(PaintEventArgs e);
public abstract void UpdateShape(Point newLocation);
}
Here we declare the abstract methods Paint and UpdateShape that we will have to override in the derived classes. UpdateShape will be called in MouseMove.
Let us start with the freeform line
class FreeformLine : Shape
{
private List<Point> _points = new List<Point>();
public FreeformLine(Point startLocation)
{
_points.Add(startLocation);
}
public override void Paint(PaintEventArgs e)
{
if (_points.Count >= 2) {
e.Graphics.DrawLines(Pens.Black, _points.ToArray());
}
}
public override void UpdateShape(Point newLocation)
{
const int minDist = 3;
// Add new point only if it has a minimal distance from the last one.
// This creates a smoother line.
Point last = _points[_points.Count - 1];
if (Math.Abs(newLocation.X - last.X) >= minDist ||
Math.Abs(newLocation.Y - last.Y) >= minDist)
{
_points.Add(newLocation);
}
}
}
Here we need a list of points. In the constructor, we pass the first point. The Paint method just executes the paint logic that you had already defined and the UpdateShape method adds new points to our points list.
The straight line works in a very similar way, but defines only the first and the last point.
class StraightLine : Shape
{
private Point _first;
private Point _last;
public StraightLine(Point startLocation)
{
_first = startLocation;
}
public override void Paint(PaintEventArgs e)
{
if (!_last.IsEmpty) {
Pen pen2 = new Pen(Color.Red, 2);
e.Graphics.DrawLine(pen2, _first, _last);
}
}
public override void UpdateShape(Point newLocation)
{
_last = newLocation;
}
}
We define only one rectangle class and add a variable in order to remember if the shape is filled or not.
class RectangleShape : Shape
{
protected bool _filled;
protected Rectangle _rect;
protected Point _start;
public RectangleShape(Point startLocation, bool filled)
{
_start = startLocation;
_rect = new Rectangle(startLocation.X, startLocation.Y, 0, 0);
_filled = filled;
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillRectangle(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawRectangle(pen, _rect);
}
}
}
public override void UpdateShape(Point newLocation)
{
int x = Math.Min(_start.X, newLocation.X);
int y = Math.Min(_start.Y, newLocation.Y);
int width = Math.Abs(newLocation.X - _start.X);
int height = Math.Abs(newLocation.Y - _start.Y);
_rect = new Rectangle(x, y, width, height);
}
}
Finally, we declare the ellipse class. Since this one uses a rectangle as well, we just derive it from our rectangle class.
class Ellipse : RectangleShape
{
public Ellipse(Point startLocation, bool filled)
: base(startLocation, filled)
{
}
public override void Paint(PaintEventArgs e)
{
if (_filled) {
using (SolidBrush brush = new SolidBrush(Color.Red)) {
e.Graphics.FillEllipse(brush, _rect);
}
} else {
using (Pen pen = new Pen(Color.Red, 2)) {
e.Graphics.DrawEllipse(pen, _rect);
}
}
}
}
Here we only override the Paint method. All the rectangle update logic remains the same.
Now to the form. Here we declare the global variables
List<Shape> _shapes = new List<Shape>();
Shape _lastShape;
string op;
In the mouse down event we create a new shape and add it to the list like this
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
switch (op) {
case "toolStripButton1":
_lastShape = new FreeformLine(e.Location);
break;
case "toolStripButton2":
_lastShape = new RectangleShape(e.Location, false);
break;
case "toolStripButton3":
_lastShape = new StraightLine(e.Location);
break;
case "toolStripButton4":
_lastShape = new RectangleShape(e.Location, true);
break;
case "toolStripButton5":
_lastShape = new Ellipse(e.Location, true);
break;
case "toolStripButton6":
_lastShape = new Ellipse(e.Location, false);
break;
default:
break;
}
_shapes.Add(_lastShape);
Refresh();
}
In the MouseMove we update the last shape like this
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _lastShape != null) {
_lastShape.UpdateShape(e.Location);
this.Refresh();
}
}
The Paint method is now much simpler
private void Form1_Paint(object sender, PaintEventArgs e)
{
foreach (Shape shape in _shapes) {
shape.Paint(e);
}
}
Note that we do all the shape specific things in the shape classes, instead of doing them in the form. The only place in the form where have to care about the different shapes, is where we create the different shapes. This is a typical object-oriented approach. It is easier to maintain and to extend. You could add new shapes, with only a minimum changes in the form itself.