In traditional desktop app, I perform my custom canvas drawing as follow
class ChartingView : System.Windows.Controls.Canvas
{
protected override void OnRender(DrawingContext drawingContext)
{
// ... All the juicy drawing code right here.
}
}
However, how about in Metro? As in Windows.UI.Xaml.Controls.Canvas, I cannot find OnRender method for me to override.
You can use Children property to populate Canvas. You can put there primitives (shapes, lines, etc.) as well as "complex" controls. And as noted in the #Aaron Murgatroyd comment:
there is no way to just simply draw on a canvas frame by frame
To adjust them on the Canvas you should use Canvas.SetXYZ methods (see for example Canvas.SetLeft and Canvas.SetTop methods).
Related
In my WPF application, I have added WindowsFormsHost in one grid, I want to draw a rectangle on the control inside WinFormsHost.
Application layout:
Code I'm trying:
Adorner Class
public class SimpleRectAdorner : Adorner
{
// Be sure to call the base class constructor.
public SimpleRectAdorner(UIElement adornedElement)
: base(adornedElement)
{
}
// A common way to implement an adorner's rendering behavior is to override the OnRender
// method, which is called by the layout system as part of a rendering pass.
protected override void OnRender(DrawingContext drawingContext)
{
Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);
// Some arbitrary drawing implements.
SolidColorBrush renderBrush = new SolidColorBrush(Colors.Green);
renderBrush.Opacity = 0.2;
Pen renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);
// Draw a circle at each corner.
Rect rect = new Rect(new Point(adornedElementRect.TopLeft.X, adornedElementRect.TopLeft.Y + 50), new Size(150, 50));
drawingContext.DrawRectangle(renderBrush, renderPen, rect);
}
}
Code to add adorner
private void btnDraw_Click(object sender, RoutedEventArgs e)
{
AdornerLayer.GetAdornerLayer(viewerGrid.Children[0]).Add(new SimpleRectAdorner(viewerGrid.Children[0]));
}
Is there any possible way to draw a rectangle on Control which is inside WindowsFormsHost?
Thanks in advance.
As noted in the comment by Clemens, WindowsFormsHost is rendered separately from the rest of your Window, and by necessity it is rendered on top of the Window. At first glance, this seems to be a design limitation about which you can do nothing; however, that is not strictly true.
If a second layer is added over your first layer, simply add a third layer on top of the second. Another Window or a Popup can render over the top of your WindowsFormsHost, and while you will have to jump through some hoops to make it all seem like part of the same Window--ensuring everything moves, minimizes, and restores at the same time, etc--it is certainly possible to do so.
You can use transparency in your third layer to allow the content in the WindowsFormsHost to show and be accessed. For example, you can set AllowsTransparency to true on your WPF Popup. It will be a bit of extra work, but if you absolutely need this feature, you can do it.
I'm building an app that has to be capable of plot a data series in C#. For this task, I started to build every part of the chart application as it follows:
An outer container represented by a class derived from System.Windows.Forms.UserControl, which is a container for the chart axes, plot area and chart title. This class has also the responsability to redraw all its child controls(by invalidate them).
A class derived from System.Windows.Forms.Control to represent the chart axes. The chart is supposed to have multiple Y-Axes and an unique X-Axis.
A class derived from System.Windows.Forms.Control where the data series will be shown.
The last two classes doesn't have a design view as I wrote them without using the templates provided for VS2010. Regardless, I set multiple properties of them to calculate their place and size within the container class, obviously this process is performed before the controls are drawn.
Well, the issue raises when I tried to Invalidate them from the container class. The classes have override the OnPaint method to handle the Paint event, which I trigger from the container class. The code for the OnPaint method in the container class looks like this
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Axis1.Invalidate();
Axis2.Invalidate();
PlotArea.Invalidate();
}
And as I stated above, every class has its respective OnPaint method. So guys, what I'm doing wrong?
I have made a control which inherits TextBox, and I'm trying to give a notebook grid:
I already have the code that will specify where to draw the lines including all of the grids features, but I'm not sure what to draw it to.
I've Googled a lot and searched for a brush that will let me have the same interface as a DrawingContext (so I could call drawingContext.DrawLine() etc.), or something familiar, but I couldn't find any!
So how can I achieve to get my grid background?
P.S I can't create a static bmp file and load it, because the grid color and spacing would most certainly change
You could try using DrawingVisual to get your DrawingContext then create a VisualBrush to assign to your Background. Something like this.
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
dc.DrawLine( new Pen(Brushes.Red,5),new Point(0,0),new Point(50,50));
dc.Close();
VisualBrush vb = new VisualBrush(dv);
textBox1.Background = vb;
You can intercept the Paint event for your control. You get a PaintEventArgs argument, which includes a ClipRectangle and a Graphics object.
Check out Control.Paint Event.
Once you have your Graphics object, you can call DrawLine and FillRectangle on it directly.
You're looking to make a custom-drawn control. You'll want to override the OnPaint method of your control class and draw the background in that method. Here's an example on how to do it: http://msdn.microsoft.com/en-us/library/b818z6z6(v=vs.90).aspx
To draw your background, grab the drawing context and draw your background after first calling the base OnPaint method:
protected override void OnPaint(PaintEventArgs pe)
{
// Call the OnPaint method of the base class.
base.OnPaint(pe);
// Declare and instantiate a new pen.
System.Drawing.Pen myPen = new System.Drawing.Pen(Color.Aqua);
// Draw an aqua rectangle in the rectangle represented by the control.
pe.Graphics.DrawRectangle(myPen, new Rectangle(this.Location, this.Size));
}
EDIT:
Since you're using WPF, you can take a look here to see a full example on custom designs: WPF .NET 3.5 Drawing Customized Controls and Custom UI Elements
I've got a PictureBox, which can be moved vertically. The image displayed in the PictureBox is a transparent GIF, so when viewed in an image viewer, it has no background.
The problem is that when I move the PictureBox in the application, the PictureBox's background moves around too strangely - almost as if the PictureBox has a background itself.
Before:
After (while moving):
Some code:
path = "C:\\note.gif";
note.Image = Image.FromFile(path);
note.Visible = true;
note.BackColor = Color.Transparent;
panel.Controls.Add(note);
I've also tried making the picturebox double buffered, but that doesn't work either.
While WinForms is poorly suited to transparency in usercontrols, it is possible. See this article here. In it the author suggests deriving from Panel rather then UserControl and overridding the OnPaintBackground method to do nothing. this will stop your background from being drawn
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
// Override the CreateParams property:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
Finally, overriding the OnPaint function you can draw your picturebox.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
//Do your drawing here
}
Using this you could create a custom picturebox with transparency, although note you will get flicker and blurring if you move it around the screen in real-time.
Using this and similar techniques we managed to get a WinForms app, Premex XPort to render with a similar branding to their website. This involved multiple transparent controls, painting hacks and all sorts to get it to display correctly.
In conclusion, the reason why Winforms does this poorly is in Win32 based technologies one control owns one pixel on the screen. There is no way to truly composite pixels with transparency as you would expect in HTML or WPF. Later Windows technologies (WPF) do this particularly well so if you really wish to make heavy use of transparency in your app I would suggest moving to this platform, at least in part (WPF can be hosted within WinForms and vice versa).
Best regards,
Win32 controls are not truly transparent.
Your choices are:
Change to WPF
Use PictureBox.Region to clip the control's unwanted ("transparent") areas
See if there are 3rd party controls which'll do what you want
I'm using an Adorner in .NET 3.5, and I'm able to draw by overriding OnRender, but I need the ability to redraw the adorner to change its appearance.
Essentially I'm looking for a way to clear the drawing context and call OnRender again. What's the best way to do this, or is there a better approach?
public class MyAdorner : Adorner
{
private Brush brush = Brushes.Red;
public DragArrowAdorner(UIElement adornedElement) : base(adornedElement)
{}
public void RedrawWithBrush(Brush newBrush)
{
brush = newBrush;
// redraw..?
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
// some drawing code...
drawingContext.DrawRectangle(
brush,
null,
new Rect(AdornedElement.DesiredSize));
}
}
The answer to your question is use InvalidateVisual to cause the OnRender to be called again
However, I would suggest instead of doing custom drawing on OnRender yourself to use the standard styling and visual tree templating to build the actual visual of the adorner. This also means you can run standard XAML animations inside it with storyboards.
If you want to go with this approach, in your adorner class you need to:
in the constructor either call base.AddVisualChild() or create your own visuals collection with the visuals you want to show in the adorner
override ArrangeOverride(Size size) in order to arrange the children properly;
override VisualChildrenCount to return the number of children in the adorner visual tree;
override GetCisualChild(int index) to return a particular child.
You can take a look at the ResizingAdorner MSDN sample for more info.
It's very important to understand that WPF is not like Windows.Forms. OnRender() should really be called AccumulateDrawingObjects(), because that's what it's doing. WPF accumulates a bunch of drawing objects, which it retains to be able to draw the UI whenever it needs to. The magic of efficiently updating the UI is that you can actually change objects in that visual tree after OnRender().
For example, you can make a DrawingGroup "backingStore", and put that into the DrawingContext during OnRender. Then anytime you want to change the visual, you can DrawingGroup.Open(), put new drawing commands into it, and WPF will efficiently re-render that portion of the UI.
It looks like this:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}