While writing a PowerPoint add-in, I need to draw something on the screen on top of the slideshow.
I am able to draw lines and images, but they disappear almost immediately.
Example code:
private void Application_SlideShowBegin(PowerPoint.SlideShowWindow Wn)
{
using (var g = Graphics.FromHwnd((IntPtr)Wn.HWND))
{
g.DrawLine(new Pen(Color.Red, 10), new System.Drawing.Point(100, 100), new System.Drawing.Point(200, 300));
Image img = Properties.Resources.img;
g.DrawImageUnscaled(img, new Rectangle(250, 250, img.Width, img.Height));
}
}
Any idea how I can keep the drawn lines / images on the screen?
I should have seen it earlier - this is about refreshes. The code needs to be run a bit later, so if the paint is delayed, the drawing stays on the window.
The following example now works as expected:
private Timer _Ticker = new Timer();
private PowerPoint.SlideShowWindow _SlideshowWindow = null;
private void Application_SlideShowBegin(PowerPoint.SlideShowWindow Wn)
{
_SlideshowWindow = Wn;
_Ticker.Interval = 30;
_Ticker.AutoReset = true;
_Ticker.Elapsed += Ticker_Elapsed;
_Ticker.Enabled = true;
}
private void Ticker_Elapsed(object sender, ElapsedEventArgs e)
{
if (_Ticker.Enabled)
{
using (var g = Graphics.FromHwnd((IntPtr)_SlideshowWindow.HWND))
{
g.DrawLine(new Pen(Color.Red, 10), new System.Drawing.Point(100, 100), new System.Drawing.Point(200, 300));
Image img = Properties.Resources.img;
g.DrawImageUnscaled(img, new Rectangle(250, 250, img.Width, img.Height));
}
_Ticker.Enabled = false;
_Ticker.Elapsed -= Ticker_Elapsed;
}
}
Related
I need to add WedgeRectCallout callout on picturebox using C#.
Is there a way to do it?
please refer image using below link to know about the callout.
In word document, I can do the same using Spire.Doc library and writing below code:
ShapeObject Shape1 = para1.AppendShape(30, 50, ShapeType.WedgeRectCallout);
Here is a minimal example. It creates a Panel subclass with a nested TextBox.
Note that you can't add controls to a PictureBox in the designer. Instead you can put it on top and use the Nest function to embed it in the PBox..
(I use a trick to simplify the drawing of the border: since shrinking a GraphicsPath is hard and I am too lazy to write out two of them, I draw the border twice and add a little offset. The effect is a shadow effect, which looks rather nice for a cheat..)
Here is the class in action:
And here is the class code:
class WedgeCallout : Panel
{
public WedgeCallout()
{
tb = new TextBox();
Controls.Add(tb);
}
TextBox tb = null;
GraphicsPath gp = new GraphicsPath();
protected override void OnLayout(LayoutEventArgs levent)
{
tb.Size = new Size(Width - 10, (int)(Height * 0.66));
tb.Location = new Point(5, 5);
tb.BackColor = BackColor;
tb.ForeColor = ForeColor ;
tb.BorderStyle = BorderStyle.None;
tb.Text = "Hiho";
tb.Multiline = true;
tb.TextAlign = HorizontalAlignment.Center;
tb.Font = Font;
SetRegion();
base.OnLayout(levent);
}
protected override void OnBackColorChanged(EventArgs e)
{
base.OnBackColorChanged(e);
if (BackColor != Color.Transparent)
tb.BackColor = BackColor;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (Tag == null) return;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
using(SolidBrush brush = new SolidBrush(tb.BackColor))
e.Graphics.FillPath(brush, (GraphicsPath)Tag);
using (Pen pen = new Pen(Color.DarkGray, 2f))
e.Graphics.DrawPath(pen, (GraphicsPath)Tag);
e.Graphics.TranslateTransform(-1, -1);
using (Pen pen = new Pen(ForeColor, 2f))
e.Graphics.DrawPath(pen, (GraphicsPath)Tag);
}
void SetRegion()
{
Rectangle r = ClientRectangle;
int h = (int)(r.Height * 0.75f);
if (gp != null) gp.Dispose();
gp = new GraphicsPath();
gp.AddPolygon(new PointF[]{ new Point(0,0),
new Point(r.Width-1, 0), new Point(r.Width-1, h),
new PointF(50, h) , new Point(0, r.Height-1),
new PointF(20, h), new PointF(0, h)});
Region = new Region(gp);
Tag = gp;
}
}
You can set the colors and the Font in the designer..
And the Nest function:
void Nest(Control child, Control parent)
{
Point p0 = parent.PointToScreen(Point.Empty);
Point p1 = child.PointToScreen(Point.Empty);
child.Location = new Point(p1.X - p0.X, p1.Y - p0.Y);
child.Parent = parent;
}
It is called from the Form where the controls sit: Nest(wedgeCallout1, pictureBox1);
Note that to save to callout with the pbox in one image you need to
Nest the callout in the PBox
Use pbox.DrawToBitmap
Temporarily set the backcolor to transparent
Example:
private void saveBtn_Click(object sender, EventArgs e)
{
Size sz = pictureBox1.ClientSize;
using (Bitmap bmp = new Bitmap(sz.Width, sz.Height))
{
Color old = wedgeCallout1.BackColor;
wedgeCallout1.BackColor = Color.Transparent;
pictureBox1.DrawToBitmap(bmp, pictureBox1.ClientRectangle);
bmp.Save(filename, ImageFormat.Png);
wedgeCallout1.BackColor = old;
}
}
I'm trying to achieve an effect similar to this site. I am basically trying to implement a way to "compare" two similar images (with different colors, etc). I managed to do this in a not so brilliant way using two PictureBox controls (Winforms) one next to the other, and changing their Size and Location attributes on a MouseMove event.
The result works, but it flickers a lot and it's not really the best way to do it.
Is there a better way to do this, maybe with a WPF or by changing the code in any way? Here it is:
private void pbImg1_MouseMove(object sender, MouseEventArgs e)
{
pbImg2.Image = CropImage(array[1], new Rectangle(pbImg1.Size.Width, 0, totalSize.Width - pbImg1.Size.Width, 240));
pbImg1.Size = new Size(e.X, pbImg1.Height);
pbImg2.Location = new Point(pbImg1.Size.Width + pbImg1.Location.X, pbImg2.Location.Y);
pbImg2.Size = new Size(totalSize.Width - pbImg1.Size.Width, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
private void pbImg2_MouseMove(object sender, MouseEventArgs e)
{
pbImg1.Image = CropImage(array[0], new Rectangle(0, 0, totalSize.Width - pbImg2.Size.Width, 240));
pbImg1.Size = new Size(pbImg1.Width + e.X, 240);
lpbImg1Size.Text = pbImg1.Size.ToString();
lpbImg2Size.Text = pbImg2.Size.ToString();
lpbImg1Location.Text = pbImg1.Location.ToString();
lpbImg2Location.Text = pbImg2.Location.ToString();
}
public Bitmap CropImage(Bitmap source, Rectangle section)
{
// An empty bitmap which will hold the cropped image
//TRY CATCH
Bitmap bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
// Draw the given area (section) of the source image
// at location 0,0 on the empty bitmap (bmp)
g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
return bmp;
}
And here you can see the behavior of the program:
https://gfycat.com/VillainousReadyAmazonparrot
You need two images imageLeft, imageRight with the same size as the picturebox:
private int pos = 0; //x coordinate of mouse in picturebox
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if(pos == 0)
{
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height));
}
else
{
e.Graphics.DrawImage(Properties.Resources.imageLeft, new Rectangle(0, 0, pos, pictureBox1.Height),
new Rectangle(0, 0, pos, pictureBox1.Height), GraphicsUnit.Pixel);
e.Graphics.DrawImage(Properties.Resources.imageRight, new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height),
new Rectangle(pos, 0, pictureBox1.Width - pos, pictureBox1.Height), GraphicsUnit.Pixel);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pos = e.X;
pictureBox1.Invalidate();
}
I have this method that should generate what I need to print:
private void myPrintDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
e.Graphics.PageUnit = GraphicsUnit.Inch;
e.Graphics.DrawImage(makeBarcode(), 500, 1000);
e.Graphics.DrawString("Do Not Seperate", makeFont(), Brushes.Black, 100, 2);
e.Graphics.DrawString("Choking Hazard", makeFont(), Brushes.Black, 200, 2);
}
How ever when I print it, it comes up blanks and when I look at it in PrintPreviewDialog it comes up blanket. What I am missing here?
This is my constructor by the way:
public Form1()
{
InitializeComponent();
this.myPrintDocument.PrintPage += new
System.Drawing.Printing.PrintPageEventHandler
(this.myPrintDocument_PrintPage);
}
For Mark
private Image makeBarcode()
{
InventoryBLL ibll = new InventoryBLL();
Barcode b = new Barcode();
b.IncludeLabel = true;
b.Height = 35;
b.Width = 200;
b.BackColor = Color.White;
b.ForeColor = Color.Black;
b.Alignment = BarcodeLib.AlignmentPositions.CENTER;
return b.Encode(TYPE.CODE128, ibll.getFNSKU(txtASIN.Text));
}
private Font makeFont()
{
Font myFont = new Font("arial", 10);
return myFont;
}
e.Graphics.PageUnit = GraphicsUnit.Inch;
...
e.Graphics.DrawString("Choking Hazard", ..., 200, 2);
That string is going to print at 200 inches. Somewhat safe to assume that your paper isn't that big, 8 inch is about typical. You cannot see what's off the paper. Consider changing the PageUnit or using much smaller floating point print positions.
In the top of Form1 I did: Bitmap bmp; In the paint event I'm drawing to the pictureBox and also to the bmp file:
private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
SolidBrush brush;
Pen p = null;
Point connectionPointStart;
Point connectionPointEnd;
Graphics g = e.Graphics;
bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics bitmapGraphics = Graphics.FromImage(bmp);
//g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
moveCounter++;
label6.Text = moveCounter.ToString();
brush = new SolidBrush(Color.Red);
p = new Pen(brush);
for (int idx = 0; idx < wireObject1._point_X.Count; ++idx)
{
Point dPoint = new Point((int)wireObject1._point_X[idx], (int)wireObject1._point_Y[idx]);
dPoint.X = dPoint.X - 5; // was - 2
dPoint.Y = dPoint.Y - 5; // was - 2
Rectangle rect = new Rectangle(dPoint, new Size(10, 10));
g.FillEllipse(brush, rect);
bitmapGraphics.FillEllipse(brush, rect);
// g.FillEllipse(brush, rect);
}
for (int i = 0; i < wireObject1._connectionstart.Count; i++)
{
int startIndex = wireObject1._connectionstart[i];
int endIndex = wireObject1._connectionend[i];
connectionPointStart = new Point((int)wireObject1._point_X[startIndex], (int)wireObject1._point_Y[startIndex]);
connectionPointEnd = new Point((int)wireObject1._point_X[endIndex], (int)wireObject1._point_Y[endIndex]);
p.Width = 2;
g.DrawLine(p, connectionPointStart, connectionPointEnd);
bitmapGraphics.DrawLine(p, connectionPointStart, connectionPointEnd);
}
}
I did a new instance in the paint event for the bmp file. I also make a new graphics variable for the bmp file. And every place i draw or fill ellipse to the pictureBox I also draw it to the bmp file.
Now in the save function I did:
private void SavePictureBoxToBitmap()
{
//PbToBitmap++;
//String tempFile = #"d:\PictureBoxToBitmap\" + PbToBitmap.ToString("D6") + bmp;
bmp.Save(#"d:\PictureBoxToBitmap\bit.bmp");
}
If im doing bmp.Save it will save to the hard disk a white bmp file with the drawings inside only. If im doing pictureBox1.Image.Save it will save to the hard disk a file with the picture in the pictureBox only without the drawings.
How can i combine it so i will make one save line/command and it will create one bmp file on the hard disk with the pictureBox image and the drawings together?
When you want to draw something onto an Image, create a Graphics element using the static function FromBitmap:
Graphics gfx = Graphics.FromImage(pictureBox1.Image)
Then you draw things on the picture box by using the gfx instance, e.g. gfx.DrawLine.
Then to save it, use the pictureBox1.Image.Save function.
UPDATE
Your code is a bit too cryptic for me, but I made a simple example which might answer your question. I created a newq empty form, added a picture box, and then implemented the Paint function.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics gfx = Graphics.FromImage(pictureBox1.Image);
gfx.DrawLine(new Pen(Color.Red, 5), new Point(10, 10), new Point(20, 20));
gfx.DrawLine(new Pen(Color.Red, 5), new Point(20, 10), new Point(10, 20));
pictureBox1.Image.Save("test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
pictureBox1.Refresh(); // The file will be correct without this, but the update will not be shown
}
using (var bmp = new Bitmap(panel1.Width, panel1.Height))
{
pictureBox1.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save("output.png", System.Drawing.Imaging.ImageFormat.Jpeg);
}
Should work
Can someone tell me what i don't make as it should, why i cannot save the drawing to a physical storage?
private void panel1_Paint(object sender, PaintEventArgs e)
{
Pen p = new Pen(Color.Red, 3);
Bitmap bmp = new Bitmap(700, 900);
Graphics gr = this.CreateGraphics();
gr.DrawLine(p, new Point(30, 30), new Point(80, 120));
gr.DrawEllipse(p, 30, 30, 80, 120);
//when i do this way it saves only a black rectangle, without other drawn content
bmp.Save(#"C:\testBMP.jpeg", ImageFormat.Jpeg);
// If i use the following 2 commented lines it saves only a empty rectangle.
//Rectangle rec = new Rectangle(0, 0, 700, 900);
// panel1.DrawToBitmap(bmp, rec);
}
Thank you for advice!
You have two problems here.
Drawing your panel's contents. This should be done inside its Paint event handler, like this:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen p = new Pen(Color.Red, 3))
{
// get the panel's Graphics instance
Graphics gr = e.Graphics;
// draw to panel
gr.DrawLine(p, new Point(30, 30), new Point(80, 120));
gr.DrawEllipse(p, 30, 30, 80, 120);
}
}
Saving your panel's contents as an image. This part should be done somewhere else (for example, when you click on a "Save" button):
private void saveButton_Click(object sender, EventArgs e)
{
int width = panel1.Size.Width;
int height = panel1.Size.Height;
using (Bitmap bmp = new Bitmap(width, height))
{
panel1.DrawToBitmap(bmp, new Rectangle(0, 0, width, height));
bmp.Save(#"C:\testBMP.jpeg", ImageFormat.Jpeg);
}
}
The instance gr has nothing to do with your bitmap (bmp). So you're creating graphics that are associated with the form or control, and have a separate bitmap. When you save the bitmap, you haven't drawn anything in it.
You need to get a Graphics object from the Image, not from your form. I have not tested this, but it should work.
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen p = new Pen(Color.Red, 3))
using (Bitmap bmp = new Bitmap(700, 900))
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.DrawLine(p, new Point(30, 30), new Point(80, 120));
gr.DrawEllipse(p, 30, 30, 80, 120);
bmp.Save(#"C:\testBMP.jpeg", ImageFormat.Jpeg);
}
}