WPF Drawing context - c#

In my wpf application i am drawing a lot of geometries as below. My requirement is to change the color of drawingvisual with out redrawing it? any possibilities in wpf?
using (DrawingContext vDrawingContext = vDrawingVisual.RenderOpen())
{
StreamGeometry vGeom = GetCutGeometry(mLength, mWidth);
vDrawingContext.DrawGeometry(mBackGroundBrush, ForeGroundPen, vGeom);
vDrawingContext.Close();
VisualChildren.Add(vDrawingVisual);
}
How could be mBackGroundBrush dyamic colors?

Provided that mBackGroundBrush is a modifiable SolidColorBrush (i.e. it is created in your application and none of the predefined brushes), you could simply change its Color property. That will change the fill color of each drawn geometry with redrawing.
private SolidColorBrush mBackGroundBrush = new SolidColorBrush(Colors.Black);
...
mBackGroundBrush.Color = Colors.Red;
or
mBackGroundBrush.Color = Color.FromArgb(255, 255, 0, 0);

I have done one work around as below. seems working.
///Kept as arefrence while initial drawing phase.
private DrawingVisual mDrawingVisual = null;
if (null != mDrawingVisual)
{
using (DrawingContext vDrawingContext = mDrawingVisual.RenderOpen())
{
DrawingGroup vDrawingGroup = VisualTreeHelper.GetDrawing(mDrawingVisual);
if (null != vDrawingGroup)
{
foreach (Drawing vDrawing in vDrawingGroup.Children)
{
GeometryDrawing vGeometryDrawing = vDrawing as GeometryDrawing;
if (null != vGeometryDrawing)
{
vGeometryDrawing.Brush = mBackGroundBrush;
}
}
}
vDrawingContext.DrawDrawing(vDrawingGroup);
vDrawingContext.Close();
}
}

Related

How to make Win2D BlendEffect apply to current drawing surface (background)?

I want to draw some images on to existing canvas using multiply blend mode. However, I don't know how to do it as the BlendEffect class require me to assign the Background variable but that is suppose to be the canvas which I could not put there.
private void OnDrawCanvas(CanvasControl sender, CanvasDrawEventArgs args)
{
var list = new LinkedList<ImageNode>();
mRootNode.GetTraverseList(list, false);
foreach (var item in list)
{
if (!item.treeVisible)
continue;
if (item.mLayerPixels != null)
{
if (item.mLayer.BlendModeKey == BlendModeType.MULTIPLY)
{
var blendEffect = new BlendEffect()
{
//Background = ???, // what to put????
Foreground = item.mLayerPixels,
Mode = BlendEffectMode.Multiply
};
args.DrawingSession.DrawImage(blendEffect, item.mLayer.Left, item.mLayer.Top);
}
else
{
args.DrawingSession.DrawImage(item.mLayerPixels, item.mLayer.Left, item.mLayer.Top);
}
}
}
}
I ended up creating an offscreen CanvasRenderTarget to do the blending. When all the drawing is done, I create a CanvasBitmap from CanvasRenderTarget which allow me to draw the final result to the UI with args.DrawingSession.DrawImage();

Draw Border around ListBox

How would I go about drawing a border with a specified width and color around a listbox?
Can this be done without overriding the OnPaint method?
Following Neutone's suggestion, here is a handy function to add and remove a Panel-based border around any control, even if it is nested..
Simply pass in the Color and size you want, and if you want a BorderStyle. To remove it again pass in Color.Transparent!
void setBorder(Control ctl, Color col, int width, BorderStyle style)
{
if (col == Color.Transparent)
{
Panel pan = ctl.Parent as Panel;
if (pan == null) { throw new Exception("control not in border panel!");}
ctl.Location = new Point(pan.Left + width, pan.Top + width);
ctl.Parent = pan.Parent;
pan.Dispose();
}
else
{
Panel pan = new Panel();
pan.BorderStyle = style;
pan.Size = new Size(ctl.Width + width * 2, ctl.Height + width * 2);
pan.Location = new Point(ctl.Left - width, ctl.Top - width);
pan.BackColor = col;
pan.Parent = ctl.Parent;
ctl.Parent = pan;
ctl.Location = new Point(width, width);
}
}
You can place a list box within a panel and have the panel serve as a border. The panel backcolor can be used to create a colored border. This doesn't require much code. Having a colored border around a form component can be an effective way of conveying status.
The problem with the ListBox control is that it does not raise the OnPaint method so you can not use it to draw a border around the control.
There are two methods to paint a custom border around the ListBox:
Use SetStyle(ControlStyles.UserPaint, True) in the constructor, then you can use the OnPaint method to draw the border.
Override WndProc method that handles operating system messages identified in the Message structure.
I used the last method to paint a custom border around the control, this will eliminate the need to use a Panel to provide a custom border for the ListBox.
public partial class MyListBox : ListBox
{
public MyListBox()
{
// SetStyle(ControlStyles.UserPaint, True)
BorderStyle = BorderStyle.None;
}
protected override void WndProc(ref Message m)
{
base.WndProc(m);
var switchExpr = m.Msg;
switch (switchExpr)
{
case 0xF:
{
Graphics g = this.CreateGraphics;
g.SmoothingMode = Drawing2D.SmoothingMode.Default;
int borderWidth = 4;
Rectangle rect = ClientRectangle;
using (var p = new Pen(Color.Red, borderWidth) { Alignment = Drawing2D.PenAlignment.Center })
{
g.DrawRectangle(p, rect);
}
break;
}
default:
{
break;
}
}
}
}

Datagridview to draw wafer map

Currently I'm using C# datagridview to draw a wafer map which contains >500 rows and >700 columns.
However, there are a few issues:
Slow performance. As i need to adjust the column width, I have to loop through and assign individually.
for (int i = 0; i < this.dgvCompleteMapGrid.Columns.Count; i++)
{
this.dgvCompleteMapGrid.Columns[i].Width = 8;
}
I only need to draw the cell border for cells with value as a wafer map has an almost round shape. I'm using cellpainting event:
if (e.Value != null) {
if (e.Value.ToString() == "")
{
e.AdvancedBorderStyle.All = DataGridViewAdvancedCellBorderStyle.None;
}
else
{
if (e.Value.ToString() == "1")
{
e.CellStyle.BackColor = Color.Lime;
}
else
{
e.CellStyle.BackColor = Color.Red;
}
//check if the top border set None for neighbor empty value cell
if (e.AdvancedBorderStyle.Top.ToString() == "None")
{
e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.Single;
}
//repeat the same for left right and bottom
}
}
However, it seems like it will draw multiple border duplicate for most of the cells.
Is Datagridview recommended? I tried to draw rectangle on a panel and the performance is even worse.
Here's my Picturebox that allows for pixellated resizing (versus interpolated). E.g. similar to zooming in on a Microsoft Paint picture.
using System.Windows.Forms;
namespace WireWorld
{
public class ZoomablePicturebox : PictureBox
{
public ZoomablePicturebox()
{
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
pe.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
pe.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
base.OnPaint(pe);
}
}
}
To generate the bitmap, I use something like this:
// Pick width and height for your case
Bitmap MyBitmap = new Bitmap(width, height);
using (var g = Graphics.FromImage(retVal))
g.Clear(BackColor); // Pick a background fill color
// Draw your points, whatever your loop needs to be and your point definition is
foreach (MyPointDef point in _Points)
{
MyBitmap.SetPixel(point.X, point.Y, point.Color);
}
Then I put a picture box in a panel on my form. The panel then provides the scrolling. I can set the picture and zoom as follows:
canvasPB.SizeMode = PictureBoxSizeMode.Zoom; // Ensures picture is resized as we resize picturebox
canvasPB.Image = MyBitmap;
canvasPB.Height = canvasPB.Image.Height * Zoom; // Zoom is 1, 2, 3, etc, for 1X, 2X, ...
canvasPB.Width = canvasPB.Image.Width * Zoom;
canvasPB being the name of the Picturebox on my form I'm trying to use as my canvas.

Custom control in a UserControl does not render correctly

In this picture...
... you can see next to each "Line Color" label there is a colored circle.
The colored circle is, in my project, a Swatch. Here is the entire code file for Swatch:
public class Swatch : System.Windows.Forms.Panel
{
/*private int _Radius = 20;
[System.ComponentModel.Category("Layout")]
public int Radius
{
get { return _Radius; }
set { _Radius = value; }
} */
private System.Drawing.Color _BorderColor = System.Drawing.Color.Transparent;
[System.ComponentModel.Category("Appearance")]
public System.Drawing.Color BorderColor
{
get { return _BorderColor; }
set { _BorderColor = value; }
}
private System.Drawing.Color _FillColor = System.Drawing.Color.Blue;
[System.ComponentModel.Category("Appearance")]
public System.Drawing.Color FillColor
{
get { return _FillColor; }
set { _FillColor = value; }
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
System.Drawing.Rectangle RealRect = new System.Drawing.Rectangle(e.ClipRectangle.Location, e.ClipRectangle.Size);
RealRect.Inflate(-1, -1);
int Radius = Math.Min(RealRect.Size.Height, RealRect.Size.Width);
System.Drawing.Rectangle SqRect = new System.Drawing.Rectangle();
SqRect.Location = RealRect.Location;
SqRect.Size = new System.Drawing.Size(Radius, Radius);
System.Drawing.Drawing2D.CompositingQuality PrevQual = e.Graphics.CompositingQuality;
using (System.Drawing.SolidBrush Back = new System.Drawing.SolidBrush(this.FillColor))
{
using (System.Drawing.Pen Pen = new System.Drawing.Pen(new System.Drawing.SolidBrush(this.BorderColor)))
{
//e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.FillEllipse(Back, SqRect);
e.Graphics.DrawEllipse(Pen, SqRect);
}
}
e.Graphics.CompositingQuality = PrevQual;
}
public Swatch()
{
this.SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true);
this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(System.Windows.Forms.ControlStyles.ResizeRedraw, true);
this.SetStyle(System.Windows.Forms.ControlStyles.SupportsTransparentBackColor, true);
this.DoubleBuffered = true;
}
}
Each row is a UserControl which consists of a TableLayoutPanel, labels, a Swatch control, and a NumericUpDown box.
There about 10 rows and they are placed in TableLayoutPanel, which sits inside a TabPage on a tab control. The tab page has AutoScroll set to true so that overflow causes the tab page to scroll.
The problem is that whenever I run the application and scroll up and down, the Swatches (the colored circles) tear and show all sorts of artifacts, as seen in the picture above. I'd like to have clean scrolling with no rendering artifacts.
I've tried using SetStyle (as suggested here Painting problem in windows form) but it has had no effect.
The UserControl (each row) has DoubleBuffered set to true, and that too has had no effect either.
I fear I am missing something rather obvious.
The problem is that you calculate the radius of the circle based on the clipping rectangle. So when the line is only partially visible a bad value is resulted.
You should calculate it based on the real rectangle, the one provided by the base class, and let it being clipped normally.

Drawing Transparent Images

I'm writing a CSS sprite engine in C#, however I'm having a few issues. I create the master image, set all the properties to that then iterate the sprites and draw those to the master image. However when I come to save the master image it only appears to be just the empty master image with transparent background and none of the sprites. I'm very confused at where I'm going wrong.
The code I'm using is:
// Work out the width/height required
int max_width = 0;
int max_height = 0;
foreach(SpriteInformation sprite in sprites) {
if (max_width < (sprite.Left + greatest_width)) max_width = sprite.Left + greatest_width;
if (max_height < (sprite.Top + greatest_height)) max_height = sprite.Top + greatest_height;
}
// Create new master bitmap
Bitmap bitmap = new Bitmap(max_width,max_height,PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
// Set background color
SolidBrush brush;
if (cbxBackground.Checked) {
if (txtColor.Text == "") {
brush = new SolidBrush(Color.Black);
} else {
brush = new SolidBrush(pnlColor.BackColor);
}
} else {
if (txtColor.Text == "") {
brush = new SolidBrush(Color.White);
} else {
brush = new SolidBrush(pnlColor.BackColor);
}
}
//graphics.FillRectangle(brush,0,0,bitmap.Width,bitmap.Height);
bitmap.MakeTransparent(brush.Color);
graphics.Clear(brush.Color);
// Copy images into place
ImageAttributes attr = new ImageAttributes();
//attr.SetColorKey(brush.Color,brush.Color);
foreach(SpriteInformation sprite in sprites) {
Rectangle source = new Rectangle(0,0,sprite.Width,sprite.Height);
Rectangle dest = new Rectangle(sprite.Left,sprite.Top,sprite.Width,sprite.Height);
graphics.DrawImage(sprite.Sprite,dest,0,0,sprite.Width,sprite.Height,GraphicsUnit.Pixel,attr);
}
// Save image
string format = ddlFormat.Items[ddlFormat.SelectedIndex].ToString();
if (format == "PNG") {
dlgSave.Filter = "PNG Images|*.png|All Files|*.*";
dlgSave.DefaultExt = ",png";
if (dlgSave.ShowDialog() == DialogResult.OK) {
bitmap.Save(dlgSave.FileName,ImageFormat.Png);
}
} else if (format == "JPEG") {
} else {
}
What are sprite.Left and Top? If they aren't 0 that may be a problem. I think you have dest and source the wrong way round?
http://msdn.microsoft.com/en-us/library/ms142045.aspx
Try a simpler DrawImage variant if you haven't already.
e.g. DrawImage(sprite.Sprite,0,0)
You draw to "graphics", but then then you save "bitmap"? I'm not sure if that graphics internally works with your original "bitmap" object...

Categories