I'm creating a WinForm program in C# and I want to customize its skin or interface using PNG or other image files. I'm not only concerned at the buttons but also its background, progress bar, and other few controls. I realize from most of my research that TransparentKey is used for an irregular shape form then panel supports PNG transparency.
My question: I was wondering if there's a better way, the actual and recommended way, to implement the custom interface for my program.
My current code uses the BackgroundImage property to replace the image. Yet I do believe this is not the way I should be implementing this.
Here's a piece my current code:
public List<Image> BaseImage = new List<Image>();
public List<Image> ClickedImage = new List<Image>();
public List<object> ControlAction = new List<object>();
public List<Image> HoverImage = new List<Image>();
private int counter = 0;
public Control CreatePanel(Image BaseImage, Image HoverImage, Image ClickedImage,
string Name, int x, int y, ButtonAction action)
{
this.BaseImage.Add(BaseImage);
this.HoverImage.Add(HoverImage);
this.ClickedImage.Add(ClickedImage);
int width = BaseImage.Width;
int height = BaseImage.Height;
Actions Action = new Actions();
EventHandler SelectedAction = null;
switch (action)
{
case ButtonAction.Cancel:
SelectedAction = Action.Cancel;
break;
case ButtonAction.Exit:
SelectedAction = Action.Exit;
break;
case ButtonAction.Minimize:
SelectedAction = Action.Minimize;
break;
case ButtonAction.Open:
SelectedAction = Action.Open;
break;
case ButtonAction.Pause:
SelectedAction = Action.Pause;
break;
case ButtonAction.Start:
SelectedAction = Action.Start;
break;
}
Simplify.Invoke(() =>
{
this.newPanel.SuspendLayout();
this.newPanel.Location = new System.Drawing.Point(x, y);
this.newPanel.Name = Name;
this.newPanel.TabIndex = counter;
this.newPanel.Size = new System.Drawing.Size(width, height);
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = this.BaseImage[counter];
this.newPanel.Click += new EventHandler(SelectedAction);
this.newPanel.MouseEnter += new EventHandler(PanelHover);
this.newPanel.MouseDown += new MouseEventHandler(PanelClicked);
this.newPanel.MouseLeave += new EventHandler(PanelLeave);
this.newPanel.ResumeLayout(false);
});
ControlAction.Add(action);
counter += 1;
return newPanel;
}
private void PanelClicked(object sender, MouseEventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = ClickedImage[newPanel.TabIndex];
this.newPanel.Size = new Size(ClickedImage[newPanel.TabIndex].Width, ClickedImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
private void PanelHover(object sender, EventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = HoverImage[newPanel.TabIndex];
this.newPanel.Size = new Size(HoverImage[newPanel.TabIndex].Width, HoverImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
private void PanelLeave(object sender, EventArgs e)
{
this.newPanel.SuspendLayout();
this.newPanel.BackColor = Color.FromArgb(0, 255, 255, 255);
this.newPanel.BackgroundImage = BaseImage[newPanel.TabIndex];
this.newPanel.Size = new Size(BaseImage[newPanel.TabIndex].Width, BaseImage[newPanel.TabIndex].Height);
this.newPanel.ResumeLayout(false);
}
My code above creates a Panel which functions as a button. Its action is taken from a class called Actions which basically just assign the method to be assigned when the user clicks it. I change the control state by replacing its BackgroundImage, at the same time changing its size to avoid getting cropped area.
Thanks in advance.
Additional Information
- I'm using .NET Framework 2.0
My question: I was wondering if there's a better way, the actual and
recommended way, to implement the custom interface for my program.
I would recommend using WPF (Windows presentation foundation). It offers highly customizable controls and also supports hardware accelerated graphics.
With winforms, you will get flickering screen, issues with resize and redraw which you will not face with wpf.
Related
I need to graph a polygonal on a panel (size 400, 400)
I try this, but it doesn't work.
PointF[] points = new PointF[totalpaso + 1];
for (int d = 0; d <= totalpaso; d++)
{
s = (float)(hola1[d] + 200);
w = (float)(hola2[d] + 200);
j = new PointF(s, w);
points[d] = j;
}
grafico.DrawPolygon(lapiz, points);
I think you should take a look at this post:
https://msdn.microsoft.com/en-us/library/07e699tw(v=vs.110).aspx
But with only that method you won't get far. It'll be drawn in a OnPaint Method:
C# Forms - Using Paint methods?
You will probably want to make your own control based on Panel.
A really basic example is this:
public sealed class MyPanel: Panel
{
public MyPanel()
{
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var brush = new SolidBrush(this.ForeColor))
{
e.Graphics.FillEllipse(brush, 0, 0, this.Width, this.Height);
}
}
}
To test it, add the class to a Windows Forms project, compile, and then drop it onto a form and set it to "Dock" in the designer.
Then set the BackColor to, say, red and the ForeColor to blue, then run the program.
You can then add your required drawing code to the OnPaint() override.
I'm in need of a way to make TextBox appear like a parallelogram but i can't figure out how to do so. I currently have this code:
private void IOBox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Point cursor = PointToClient(Cursor.Position);
Point[] points = { cursor, new Point(cursor.X + 50, cursor.Y), new Point(cursor.X + 30, cursor.Y - 20),
new Point(cursor.X - 20, cursor.Y - 20) };
Pen pen = new Pen(SystemColors.MenuHighlight, 2);
g.DrawLines(pen, points);
}
But apparently it's not working. Either i misplaced/misused it or i'm not doing something right.
This is the method that i use to add it.
int IOCounter = 0;
private void inputOutput_Click(object sender, EventArgs e)
{
IOBox box = new IOBox();
box.Name = "IOBox" + IOCounter;
IOCounter++;
box.Location = PointToClient(Cursor.Position);
this.Controls.Add(box);
}
Any idea how i can fix it? IOBox is a UserControl made by me which contains a TextBox. Is that rightful to do?
If its possible, you should make your application using WPF. WPF is designed to do exactly what you are trying to do.
However, it can be done in WinForms, though not easily. You will need to make a new class that inherits the TextBox WinForm control. Here is an example that makes a TextBox look like a circle:
public class MyTextBox : TextBox
{
public MyTextBox() : base()
{
SetStyle(ControlStyles.UserPaint, true);
Multiline = true;
Width = 130;
Height = 119;
}
public override sealed bool Multiline
{
get { return base.Multiline; }
set { base.Multiline = value; }
}
protected override void OnPaintBackground(PaintEventArgs e)
{
var buttonPath = new System.Drawing.Drawing2D.GraphicsPath();
var newRectangle = ClientRectangle;
newRectangle.Inflate(-10, -10);
e.Graphics.DrawEllipse(System.Drawing.Pens.Black, newRectangle);
newRectangle.Inflate(1, 1);
buttonPath.AddEllipse(newRectangle);
Region = new System.Drawing.Region(buttonPath);
base.OnPaintBackground(e);
}
}
Keep in mind that you will still have to do other things, such as clipping the text, etc. But this should get you started.
This is a different approach to what I was doing earlier.
In my combo owner drawn combo box draw 3 lines(solid,dash,dashdot) to be drawn with the color selected from an earlier colpr picker drop down
this.DrawMode = DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
int startX = e.Bounds.Left + 5;
int startY = (e.Bounds.Y);
Point p1=new Point(startX,startY);
int endX = e.Bounds.Right - 5;
int endY = (e.Bounds.Y);
ComboBoxItem item = (ComboBoxItem)this.Items[e.Index];
Point p2=new Point(endX,endY);
base.OnDrawItem(e);
Pen SolidmyPen = new Pen(item.foreColor, 1);
Pen DashedPen = new Pen(item.foreColor, 1);
DashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
Pen DashDot = new Pen(item.foreColor, 1);
DashDot.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
// Pen DashedPen = new Pen(item.foreColor, (Int32)this.Items[e.Index]);
Bitmap myBitmap = new Bitmap(item.Picture);
Graphics graphicsObj;
graphicsObj = Graphics.FromImage(myBitmap);
switch (e.Index)
{
case 0:
graphicsObj.DrawLine(SolidmyPen, p1, p2);
break;
case 1:
graphicsObj.DrawLine(DashedPen, p1, p2);
break;
case 2:
graphicsObj.DrawLine(DashDot, p1, p2);
break;
}
Here's what I'm trying to do. Draw 3lines(solid,dash,dashdot) in the combo box.
I don't see any lines in the combo box except some blue which is the selected color
Thank u
Try this.
I started a new winforms application. Created a class based off of ComboBox added your code and modified it a bit. I think you major problem was in your bitmap part. You create a new bitmap, then draw on that, but you never use the bitmap you created. If you wish to keep the code you created you would have to add to the end of the method item.Picture=myBitmap. But I think that would call the ondrawitem again and you would be in an infinite loop. Instead of creating a graphics object based off the item.Picture, just use the graphics object created for you in the DrawItemEventArgs.
e.Graphics
here is what I did, I think I cleaned it up a bit. And you may already know but you should always wrap pens, brushes and graphics in using {....} like I demonstrated below.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
public class MyComboBox : ComboBox
{
public MyComboBox()
{
this.DrawMode = DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
//I removed you int startX... endy... stuff, unless you want to keep it for readability there is no need
Point p1 = new Point(e.Bounds.Left + 5, e.Bounds.Y + 5);
Point p2 = new Point(e.Bounds.Right - 5, e.Bounds.Y + 5);
//I am not sure why you would want to call the base.OnDrawItem, feel free to uncomment it if you wish though
//base.OnDrawItem(e);
switch (e.Index)
{
case 0:
using ( Pen SolidmyPen = new Pen(e.ForeColor, 1))
e.Graphics.DrawLine(SolidmyPen, p1, p2);
break;
case 1:
using (Pen DashedPen = new Pen(e.ForeColor, 1))
{
DashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
e.Graphics.DrawLine(DashedPen, p1, p2);
}
break;
case 2:
using (Pen DashDot = new Pen(e.ForeColor, 1))
{
DashDot.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
e.Graphics.DrawLine(DashDot, p1, p2);
}
break;
}
}
}
I use a similar method to draw a combobox. But GDI+ keeps throwing an exception. This works fine on Windows XP, but not on Windows 7.
So I had to fix it with a hack.
I added a timer to fire off an event, 100 ms after the form was initially shown. This event shows the first item in the combo box list.
private void timer1_Tick(object sender, EventArgs e)
{
// Use a short 100 ms delay before showing the default items
// in the dropdown lists
predefinedComboBox.SelectedIndex = 0;
// Disable the timer
timer1.Enabled = false;
}
The Layout event was too early. The control was not ready yet to be drawn. So an exception was thrown. I am not sure what other events can be used to achieve the desired effect.
I'm doing a small project where my vc# application needs to include a text scroller / news ticker. Application will be running on 30+ screens showing off internal ad production at my workplace.
I've been googling and testing for a couple of months now but has yet to find / create a good solution where the movement is smooth and not choppy.
So my question is: is it possible to create perfect smooth scroll motion in c# or do I need to go about it some other way?
The code I'm using at the moment, part of a sample I edited, is running almost smooth except it seems to lag every 100 ms or so.
Here is the code I'm using:
namespace ScrollDemo1
{
public partial class NewsTicker : Panel
{
private Timer mScroller;
private int mOffset;
private string mText;
private Size mPixels;
private Bitmap mBuffer;
public NewsTicker()
{
mScroller = new Timer();
mScroller.Interval = 1;
mScroller.Enabled = false;
mScroller.Tick += DoScroll;
}
[Browsable(true)]
public override string Text
{
get { return mText; }
set
{
mText = value;
mScroller.Enabled = mText.Length > 0;
mPixels = TextRenderer.MeasureText(mText, this.Font);
mOffset = this.Width;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
private void DoScroll(object sender, EventArgs e)
{
mOffset -= 1;
if (mOffset < -mPixels.Width) mOffset = this.Width;
Invalidate();
Update();
}
protected override void OnPaint(PaintEventArgs e)
{
if (mBuffer == null || mBuffer.Width != this.Width || mBuffer.Height != this.Height)
mBuffer = new Bitmap(this.Width, this.Height);
Graphics gr = Graphics.FromImage(mBuffer);
Brush bbr = new SolidBrush(this.BackColor);
Brush fbr = new SolidBrush(this.ForeColor);
Bitmap bmp = global::ScrollDemo1.Properties.Resources.text_bg1;
TextureBrush tb = new TextureBrush(bmp);
int iLoc = (this.Height / 2) - (mPixels.Height / 2);
//Console.WriteLine(iLoc.ToString());
//gr.FillRectangle(bbr, new Rectangle(0, 0, this.Width, this.Height));
gr.FillRectangle(tb, new Rectangle(0, 0, this.Width, this.Height));
gr.DrawString(mText, this.Font, fbr, mOffset, iLoc);
e.Graphics.DrawImage(mBuffer, 0, 0);
bbr.Dispose();
fbr.Dispose();
gr.Dispose();
}
}
}
I'd suggest you go with WPF. It has rich and easy to use support for animations and tends to be be very smooth since it is Direct3D based.
I have 3 pictures, each one has a colored circle in it. The 3 pictures are red, green and yellow.
I'm putting it in a PictureBox in a windows form. I want to switch these images from green to yelow to red or otherwise.
Is there anything I can make them fade to each other instead of switching them the normal way?
I know this could be done using flash/j-query easily, but I was wondering how far I can achieve .
Something similar in windows forms using normal windows forms functionality.
Note: I'm using .net framework 4 and windows forms.
See Transition of images in Windows Forms Picture box. There is a solution that transitions the images using a timer on this page.
Code from site:
public class BlendPanel : Panel
{
private Image mImg1;
private Image mImg2;
private float mBlend;
public BlendPanel()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
}
public Image Image1
{
get { return mImg1; }
set { mImg1 = value; Invalidate(); }
}
public Image Image2
{
get { return mImg2; }
set { mImg2 = value; Invalidate(); }
}
public float Blend
{
get { return mBlend; }
set { mBlend = value; Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
if (mImg1 == null || mImg2 == null)
{
e.Graphics.FillRectangle(new SolidBrush(this.BackColor),
new Rectangle(0, 0, this.Width, this.Height));
}
else
{
Rectangle rc = new Rectangle(0, 0, this.Width, this.Height);
ColorMatrix cm = new ColorMatrix();
ImageAttributes ia = new ImageAttributes();
cm.Matrix33 = mBlend;
ia.SetColorMatrix(cm);
e.Graphics.DrawImage(mImg2, rc, 0, 0, mImg2.Width,
mImg2.Height, GraphicsUnit.Pixel, ia);
cm.Matrix33 = 1F - mBlend;
ia.SetColorMatrix(cm);
e.Graphics.DrawImage(mImg1, rc, 0, 0, mImg1.Width,
mImg1.Height, GraphicsUnit.Pixel, ia);
}
base.OnPaint(e);
}
}
Build your project. You can now drop a BlendPanel from the top of the toolbox onto your form. Here's a sample program that uses it:
namespace WindowsApplication1
{
public partial class Form1 : Form
{
private float mBlend;
private int mDir = 1;
public Form1()
{
InitializeComponent();
timer1.Interval = 30;
timer1.Tick += BlendTick;
blendPanel1.Image1 = Bitmap.FromFile(#"c:\temp\test1.bmp");
blendPanel1.Image2 = Bitmap.FromFile(#"c:\temp\test2.bmp");
timer1.Enabled = true;
}
private void BlendTick(object sender, EventArgs e)
{
mBlend += mDir * 0.02F;
if (mBlend < 0) { mBlend = 0; mDir = 1; }
if (mBlend > 1) { mBlend = 1; mDir = -1; }
blendPanel1.Blend = mBlend;
}
}
}
You'll need to modify the Bitmap.FromFile() calls. Build and run. You should see the displayed image smoothly morph from your first image to your second image without any flickering. Lots of ways to tweak the code, have fun.
I don't know if it is a good idea, but i would go for 2 image boxes, one to fade in, other to fade out, and change alpha when time passes.
Like what #Igoris suggested, You need to use two controls overlap each other, and you should define a timer so when you want to fade in or out, start the timer and in its tick decrease the Transparent of the first control and increase it on the second one... , the problem is that the ordinary controls does not support transparent by default. so you have to inherit it and apply transparent here is a custom TransparentPictureBox that inherited from PictureBox:
public class TransparentPictureBox : System.Windows.Forms.PictureBox
{
/// <summary>
/// Initialize new instance of this class.
/// </summary>
public TransparentPictureBox()
: base()
{
DoubleBuffered = true;
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.BackColor = Color.Transparent;
}
}