Redrawing and custom shaped windows question - c#

I'm using C#2.0 and I want to create a facebook style tooltip window. I currently made it with 2 windows and transparent key. One for the triangle arrow pointer and one for the square. The whole picture looks like that:
I have problem with the redrawing (as shown in the picture).
Is there a way to use whole shaped window on that? (While I need to make it sizeable)
If no, is this the proper way to make that? Or I need to 'glue' the triangle to the rectangle

Two ways to solve --
Using transparency: Irregular shaped Windows Form (C#)
Or using Control.Region which is the actual shaping of the window. Plenty of samples or:
How do I make a genuinely transparent Control?
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
const int ArrowSize = 25;
Point[] points = new[] {
new Point(ArrowSize, 0),
new Point(this.Width, 0),
new Point(this.Width, this.Height),
new Point(ArrowSize, this.Height),
new Point(ArrowSize, ArrowSize),
new Point(0, ArrowSize/2)
// don't need - autocloses
// ,new Point(ArrowSize, 0)
};
GraphicsPath path = new GraphicsPath();
path.AddLines(points);
this.Region = new Region(path);
}

Related

Can't get Control.OnPaint method to work

I couldnt find anything like that at all (basicly every problem from so else is always a syntax problem) and well..., the situation is a bit more complicated. to avoid using 500 lines of code im going to describe it for the most part:
Ive got a Form wich is acting as a parent Form (MdiParent) and another Form wich is a Child but basicly a fully functional Form at its own. Im using several OnPaint methods in the childform, witch work perfectly fine, and 3 custom buttons on the parent Form witch also have their own OnPaint methods. These 3 buttons (actualy panels) and every other control on the parent Form are contained in a PictureBox witch fills the parent Form completely and is used to make the background of the parent transparent / clickthrough via TransparencyKey (havnt found any other ways of doing that).
the Problem is that every OnPaint Method on the parent wont work at all (they're beeing executed but dont paint anything).
here is some code but that isnt the problem i'd say:
this.myButtonObject1.BackColor = System.Drawing.Color.Red;
this.myButtonObject1.Location = new System.Drawing.Point(840, 0);
this.myButtonObject1.Name = "myButtonObject1";
this.myButtonObject1.Size = new System.Drawing.Size(50, 50);
this.myButtonObject1.TabIndex = 0;
this.myButtonObject1.Click += new System.EventHandler(this.myButton1_Click);
this.myButtonObject1.Paint += new System.Windows.Forms.PaintEventHandler(this.myButtonObject1_Paint);
private void myButtonObject1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
LinearGradientBrush lgb = new LinearGradientBrush(new PointF(0, 0), new PointF(myButtonObject1.Width, myButtonObject1.Height), Color.Green, Color.Lime);
Pen p = new Pen(lgb, 5);
g.DrawRectangle(p, myButtonObject1.Bounds);
lgb.Dispose();
p.Dispose();
}
if anyone can tell me; what am i doing wrong?
PS: i m using .net 4.5, VS 2015, and havnt changed any of the default settings besides TopMost FormBorderStyle ShowInTaskbar StartPosition and ofc the color and trancparencyKey, but i dont think it has anything todo with that.
Update
The small error in your code is to use the Panel's Bounds property, which at runtime will refer to the Panel's Location within its Parent! But the drawing code must be relative to the object, not its parent!
So do not use Bounds but ClientRectangle and make sure to set the right PenAlignment:
using (LinearGradientBrush lgb =
new LinearGradientBrush(ClientRectangle, Color.Green, Color.Lime, 0f) ) //or some angle!
using (Pen p = new Pen(lgb, 5))
{
p.Alignment = PenAlignment.Inset;
g.DrawRectangle(p, ClientRectangle);
}
Set myButtonObject1.FlatStyle to FlatStyle.Standard.

Transparency key messing up on transparent images

I have a C# WinForm that has a transparent key of Lime. I also set the background color of the form to Lime. This works great for things like buttons and almost anything you can think of. The form looks like it is not there and allows me to create a custom looking form.
I have a picture box with background of Transparent and the picture box image is has a drop shadow. When I run the application, the drop shadow is not transparent. The drow shadow has a background color of the forms background color (lime). It looks horrible.
How do I have a transparent form that also allows transparent images to be placed on it properly.
Programs such as SWTOR game launcher have this nice background drop shadow so I know it is possible.
Thank You!
You're looking for one of two things, either Windows Regions
OR
Layered Windows
Here is an excellent example for C#
Or a little example i put together:
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
Region r;
PointF[] p = new PointF[9];
p[0] = new PointF(70, 0);
p[1] = new PointF(170, 0);
p[2] = new PointF(240, 70);
p[3] = new PointF(240, 170);
p[4] = new PointF(170, 240);
p[5] = new PointF(70, 240);
p[6] = new PointF(0, 170);
p[7] = new PointF(0, 70);
p[8] = new PointF(70, 0);
gp.AddPolygon(p);
r = new Region(gp);
this.Region = r;
gp.Dispose();
r.Dispose();
}
you will need to put this code into the form you want it to effect.

Highlight the Rectangular Area while Dragging it

I am creating a image viewer sort of application. I am on Windows and using .Net
In my app, I am trying to highlight a Particular area while dragging.
I have created a Rectangle.
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
Now I am dragging this rectangular area along with my mouse movements.
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
MoveRect(ptNew);
ptOld = ptNew;
}
Here I am trying to move this rect along with my mouse
void MoveRect(Point point)
{
Graphics grfxClient = CreateGraphics();
Rectangle tempRectangle = new Rectangle(areaRect.Left, areaRect.Top, areaRect.Width, areaRect.Height);
grfxClient.DrawRectangle(rectPen, tempRectangle);
this.Invalidate();
grfxClient.Dispose();
}
My Code till this point is working fine.
Now I would like to darken the INVERSE drag Area (The area which is outside the drag region), I mean the area which is within this Rectangle should gets highlighted while dragging.
Any idea how to proceed.
Thanks.
-Pankaj
I suppose you can do it by creating a Region object that covers the outside of the rectangle and fill it with a semi-transparent SolidBrush to make it look darkened.
You also don't have to create a graphics and draw in OnMouseMove event, but just shift the rectangle and invalidate the surface of the control you are drawing on.
The code I used looks more or less like this:
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Point ptOld = new Point(0, 0);
Pen rectPen = new Pen(Brushes.White, 3);
//A new field with a semi-transparent brush to paint the outside of the rectangle
Brush dimmingBrush = new SolidBrush(Color.FromArgb(128, 0, 0, 0));
protected override void OnPaint(PaintEventArgs e)
{
Region outsideRegion = new System.Drawing.Region(e.ClipRectangle);
outsideRegion.Exclude(areaRect);
Graphics dcPaint = e.Graphics;
dcPaint.FillRegion(dimmingBrush, outsideRegion);
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseMove(MouseEventArgs e)
{
Point ptNew = new Point(e.X, e.Y);
int dx = ptNew.X - ptOld.X;
int dy = ptNew.Y - ptOld.Y;
areaRect.Offset(dx, dy);
ptOld = ptNew;
this.Invalidate();
}
The method named MoveRect is not needed.
It now seems to work as you wanted it to.
Suggestions
I also have some suggestions. You don't have to use them, maybe they will be helpful for you.
You haven't written what kind of control you are using to draw on (or overriding Form methods and painting directly on it), but I suggest you to use a PictureBox control, create a custom control derived from it and override its events. This should make the painting process smooth and prevent flickering. To do it this way:
Create a new user control by selecting Add and User Control... and name a new control i.e. MyPictureBox
change the parent class of the control, so it should now contain the line:
public partial class MyPictureBox : PictureBox
open file MyPictureBox.Designer.cs and comment out these lines:
//this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
//this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
copy the code I posted in this answer and add line base.OnPaint(e); and the beginning of OnPaint method
compile the project
now you should be able to open designer of your main form, drag MyPictureBox control from the toolbox and use it without additional code needed
You also might consider changing the behaviour of the highlighted area, so mouse cursor was in the center of it. I suppose it would be more intuitive to the user.
If you have any issues with the code, just write it in the comments and I'll try to help :).

C# WPF - Adorner ZIndex

I have a Grid with a Adorner to provide some drawn pattern. See img: http://imgur.com/D649W
My problem is that this Adorner(dots on the Grid) is layered on top of everything. The white square are draggable but now when the Adorner are on top, I can't drag. I would like the layer to be behind every component added to the Grid. Any suggestions on how I can set the ZIndex?
Thanks.
Code below:
MyAdorner ad = new MyAdorner(grid);
AdornerLayer adLayer = AdornerLayer.GetAdornerLayer(grid);
adLayer.Add(ad);
I push my Button and this is adding the MyAdorner to the grid. MyAdorner looks like this:
public MyAdorner(Grid adornedGrid)
: base(adornedGrid) {
Height = adornedGrid.Height;
Width = adornedGrid.Width;
brush = new VisualBrush();
brush.Stretch = Stretch.Fill;
brush.TileMode = TileMode.Tile;
brush.Viewport = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewportUnits = BrushMappingMode.Absolute;
brush.Viewbox = new Rect(0, 0, SnapDistance, SnapDistance);
brush.ViewboxUnits = BrushMappingMode.Absolute;
ellipse = new Ellipse() { Fill = new SolidColorBrush(Colors.Blue), Width = 2, Height = 2 };
brush.Visual = ellipse;
}
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) {
Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 0);
drawingContext.DrawRectangle(brush, renderPen, new Rect(new Point(0, 0), AdornedElement.DesiredSize));
}
If your problem is that the adorner is covering the elements you want to manipulate so that they become un-draggable etc, set .IsHitTestVisible = False on the adorner.
You can also set the adorner's opacity to some semi-transparent value to see the background through it if that is desirable.
Is this what you're looking for?
Panel.SetZIndex(ad, 20)
Attached properties of the framework are usually asignable from static methods of the UIElement that holds it.
EDIT:
Possible alternative: - make your own Panel
Easy and dirty way to make sure that your wanted elements are ALWAYS on top:
Declare a static in a Util library:
public static int ZIndexCount;
Then when you want an element on top you simply do:
SetZIndex(_viewbox, Util.ZIndexCount++);
Of course, if your application runs 5 years without being interrupted the ZIndexCount will go back to 0...
It works like a charm in my applications.
I know this is quite old but I thought I try anyway:
You can add a new AdornerDecorator to you visual tree hierarchy to render the controls at the right level. By default the root of the tree provides the AdornerDecorator but you can add as many as you want and your the components you add will be rendered in them. For more information - see here
<Grid>
<AdornerDecorator>
...your Adorners render here
</AdornerDecorator>
</Grid>
https://wangmo.wordpress.com/2008/10/19/relations-between-adorner-adornerlayer-and-adornerdecorator/

Delete rectangle using .NET?

Can I delete the old rectangle which I have drawn and draw a new rectangle?
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = this.panel1.CreateGraphics();
Pen pen = new Pen(Color.Black, 2);
g.DrawRectangle(pen, 100,100, 100, 200);
g.dispose();
}
No, you cannot "delete" something that's already been drawn. You can overwrite it with something else, but drawing with Graphics objects is like painting in real-life: once the paint is dry, you can only paint over it with another colour, you can't "erase" it.
You probably shouldn't be drawing things in response to a MouseClick, either. It's best to only draw things in response to a Paint event. What I would do in this situation is add a Rectangle structure to a list on the MouseClick and then call panel1.Invalidate() to ask it to redraw itself. Then in the Paint event for the panel, do the drawing there.
This will kill two birds with one stone, because you will be able to "erase" thing by simply removing them from the list of stuff to draw.
This is usually done by maintaining a collection of objects you want drawn. The mouse click should update this collection and then tell the window (or the affect region) to refresh. This has the enormous advantage of preserving whatever you've drawn if the window is moved off-screen, hidden behind other windows, minimized, etc.
For a rudimentary solution, create a hierarchy of drawable shape types derived from a common abstract Shape class, and use, e.g., a List for the collection. The base Shape class will have an abstract Draw method that the derived classes override.
For a more industrial-strength solution, look around for 2-D scene graphs.
One can use Graphics.Save() and Graphics.Restore(state) methods for that. For example:
private void SaveRestore2(PaintEventArgs e)
{
// Translate transformation matrix.
e.Graphics.TranslateTransform(100, 0);
// Save translated graphics state.
GraphicsState transState = e.Graphics.Save();
// Reset transformation matrix to identity and fill rectangle.
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(new SolidBrush(Color.Red), 0, 0, 100, 100);
// Restore graphics state to translated state and fill second
// rectangle.
e.Graphics.Restore(transState);
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), 0, 0, 100, 100);
}
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.restore.aspx
Also, depending on the application, you might look at using DrawReversibleFrame. You can change the rectangle location by calling the Offset method.
Instead of calling g.DrawRectangle(pen, 100,100, 100, 200); , maintain the rectangle as a object which will be drawn by the graphics object. Each time you will update this rectangle object with new one and graphics object will draw the new one.
The refresh should clear the old rectangle and graphics will draw the new one.
You can just use VisualBasic PowerPacks, it is included with my version of Visual Studio 2008
Here's a sample code that will draw a rectangle over a TextBox, i.e. I am giving it a custom border
Dim x = TextBox1.Location.X
Dim y = TextBox1.Location.Y
Dim width = TextBox1.Width
Dim height = TextBox1.Height
Dim ShapeContainer1 As New Microsoft.VisualBasic.PowerPacks.ShapeContainer
Me.Controls.Add(ShapeContainer1)
Dim RectangleShape1 As New Microsoft.VisualBasic.PowerPacks.RectangleShape
ShapeContainer1.Shapes.AddRange(New Microsoft.VisualBasic.PowerPacks.Shape() {RectangleShape1})
RectangleShape1.Location = New System.Drawing.Point(x - 1, y - 1)
RectangleShape1.Size = New System.Drawing.Size(width + 1, height + 1)
RectangleShape1.BorderColor = Color.MistyRose
ShapeContainer1.Refresh()
Code is self describing but if you'd have any problem, just leave a message...
I think using DrawReversibleFrame is the right solution.
The first call draw the rectangle, the second call undraw it and so on.
Here is a sample code, a clic on the button will make the rectangle appear/disapper.
Rectangle pRect = new Rectangle(10, 10, 20, 20);
private void rect_Click(object sender, EventArgs e)
{
ControlPaint.DrawReversibleFrame(pRect, this.BackColor, FrameStyle.Thick);
}

Categories

Resources