Create own shape for panel with GraphicsPath - c#

How to set the region like this following picture?
The square I draw was the panel that I want to insert.
Let say the panel size was new Size(200, 500).
Then I want to remove the region of the panel in the middle (with label b and the region was color with blue).
Let say the region was 50,50 in size and the location was 50, 250
how to remove that blue region?
This is what I have done
GraphicsPath a = new GraphicsPath();
Rectangle RectangleRegion = new Rectangle(new point(50, 250), new Size(50,50));
graphicsPath.addRectangle (RectangleRegion)
Panel myPanel = new panel(){new Size(200, 500), new Pont(0,0), region = new region(a);
With this method, it doesn't remove the area I want to have
it just remove the other part I want to keep and it keep the part i want to remove.

Use two Regions and Region.Exclude:
Region r1 = new Region(new Rectangle(11,11,111,111));
Region r2 = new Region(new Rectangle(33,33,66,66));
r1.Exclude(r2);
panel1.Region = r1;
For more complex shapes you will indeed want to create the Regions from GraphicsPaths.

Related

Programmatically created UIElement 'unlinks' when local variable value is set to a new UIElement

Hopefully the title isn't to vague, I'm not sure how else to word it. What I'm trying to do (As a basic example) is add a rectangle to a canvas programmatically, then at some later point change the local variable to a new rectangle with different properties and have that update on the canvas.
// First rectangle
Rectangle rect = new Rectangle()
{
Width = 50,
Height = 50,
Fill = Brushes.Red,
Margin = new Thickness(20, 20, 0, 0)
};
// Add it to the canvas
mainCanvas.Children.Add(rect);
// Change something about the rectangle, which works
rect.Fill = Brushes.Black;
// Create new rectangle
Rectangle newRect = new Rectangle()
{
Width = 15,
Height = 20,
Fill = Brushes.Blue,
Margin = new Thickness(20, 20, 0, 0)
};
// Set the original rectangle to the new rectangle
rect = newRect;
// Canvas rectangle is no longer 'linked' to the rect variable :(
You are reassigning a the rect variable, but that doesn't affect your Canvas. The Canvas only knows about the old rectangle rect used to point to. rect is just a reference to a rectangle. When you add it to the canvas, the canvas copies the reference. It doesn't continue to use the rect variable anymore. So changing rect to reference a new rectangle changes nothing for the canvas, as the canvas still references the original.
You likely will want to do something like the following. I'm just taking a stab here, so you might need to look up appropriate methods, but hopefully this gives you guidance.
mainCanvas.Children.Remove(rect); //take the old rectangle off the canvas
rect = newRect;
mainCanvas.Children.Add(rect); //replace the new rectangle on the canvas

Converting the path of a Rectangle into a GraphicPath/Region

I am learning about GraphicsPath and Region. And using it with Invalidate.
So, I have a Rectangle object and I want to erase this rectangle. But, I only want to erase the edge of the rectangle (that is, the lines).
At the moment I have this:
if(bErase)
{
Rectangle rcRubberBand = GetSelectionRectangle();
GraphicsPath path = new GraphicsPath();
path.AddRectangle(rcLastRubberBand);
Region reg = new Region(path);
myControl3.Invalidate(reg);
myControl3.Update();
}
It works, but it is invalidating the complete rectangle shape. I only need to invalidate the rectangle lines that I had drawn. Can I make such a path with GraphicsPath?
You can't get the system to invalidate anything but a full rectangle.
So you can't use an outline path to save time.
However it can be useful for other things. Let's look at two options :
You can create an outline path
You can exclude parts of a region
The simplest way to create an outline GraphicsPath is to widen a given path with a Pen:
GraphicsPath gp = new GraphicsPath();
gp.AddRectangle(r0);
using (Pen pen = new Pen(Color.Green, 3f)) gp.Widen(pen);
This let's you make use of all the many options of a Pen, including DashStyles, Alignment, LineJoins etc..
An alternative way is to create it with the default FillMode.Alternate and simply add a smaller figure:
Rectangle r0 = new Rectangle(11, 11, 333, 333);
Rectangle r1 = r0;
r1.Inflate(-6, -6);
GraphicsPath gp = new GraphicsPath();
gp.AddRectangle(r0);
gp.AddRectangle(r1);
Now you can fill the path
g.FillPath(Brushes.Red, gp);
or use it to clip the ClipBounds of a Graphics object g :
g.SetClip(gp);
After this anything you draw including a Clear will only affect the pixels inside the outline.
When you are done you can write:
g.ResetClip();
and continue drawing on the full size of your graphics target.
Or you can use the path as the basis for a Region:
Region r = new Region(gp);
and restrict a Control to it..:
somecontrol.Region = r;
Regions support several set operations so instead of using the above outline path you could also write this with the same result:
Region r = new Region(r0);
r.Exclude(r1);

VisuaBrush Scaling Incorrectly with Canvas as Visual element

I've been fighting with this problem for a few days now and have had zero luck getting it resolved or finding any support on the web. Basically, I am trying to create a VisualBrush with a canvas as the visual element. I want to be able to draw several lines on this canvas and will eventually be mapping it to a 3D surface (2 parallel triangles forming a rectangle). I've set up the texture coordinates and can confirm everything works by simply using an ImageBrush or something of that nature. The problem I am having is that the canvas refuses to maintain its size and is constantly scaled based on the content that is inside it. So for example, if the canvas is 100x100 and I have a single line inside it from (0, 0) to (50, 50), the canvas would be scaled such that only the 50x50 portion with content inside is mapped to the texture coordinates and onto the 3D surface. I have tried many combinations of Viewport/Viewbox/StretchMode/Alignment/etc and can't find anything that stops this from happening. I am setting the Width and Height properties of the canvas so I can't see why it would perform differently in the VisualBrush versus if I simply added it into a grid or some other layout container.
Here's some code to help illustrate the problem.
// create the canvas for the VisualBrush
Canvas drawCanvas = new Canvas();
drawCanvas.Background = Brushes.Transparent; // transparent canvas background so only content is rendered
drawCanvas.Width = 100;
drawCanvas.Height = 100;
// add a line to the canvas
Line l = new Line();
l.X1 = 0;
l.Y1 = 0;
l.X2 = 50;
l.Y2 = 50;
l.Stroke = Brushes.Red;
l.StrokeThickness = 2;
drawCanvas.Children.Add(l);
// create rectangular surface mesh
MeshGeometry3D TableMesh = new MeshGeometry3D();
CreateRectangleModel(
new Point3D(0, 0, 0),
new Point3D(0, 0, 100),
new Point3D(100, 0, 100),
TableMesh);
// need to include texture coordinates for our table mesh to map canvas to 3D model
TableMesh.TextureCoordinates = new PointCollection {
new Point(0, 0), new Point(0, 1), new Point(1, 1),
new Point(1, 1), new Point(0, 1), new Point(0, 0),
new Point(1, 1), new Point(1, 0), new Point(0, 0),
new Point(0, 0), new Point(1, 0), new Point(1, 1)
};
// finally make our visual brush with the canvas we have created
VisualBrush vb = new VisualBrush();
vb.Visual = drawCanvas;
Material TableMaterial = new DiffuseMaterial(vb);
// add our new model to the scene
GeometryModel3D geometry = new GeometryModel3D(TableMesh, TableMaterial);
Model3DGroup group = new Model3DGroup();
group.Children.Add(geometry);
ModelVisual3D previewTable = new ModelVisual3D();
previewTable.Content = group;
MainViewport.Children.Add(previewTable);
And the one function I referenced is
private void CreateRectangleModel(Point3D pStart, Point3D pCorner1, Point3D pEnd, MeshGeometry3D mesh)
{
// pCorner -> O--O <- pEnd
// | /|
// |/ |
// pStart -> O--O <- pCorner2
// find the remaining point for our rectangle
Point3D pCorner2 = new Point3D(
pStart.X + (pEnd.X - pCorner1.X),
pStart.Y + (pEnd.Y - pCorner1.Y),
pStart.Z + (pEnd.Z - pCorner1.Z));
// add necessary triangles to our group (we create 2 facing up, 2 facing down)
CreateTriangleModel(pStart, pCorner1, pEnd, mesh);
CreateTriangleModel(pEnd, pCorner1, pStart, mesh);
CreateTriangleModel(pEnd, pCorner2, pStart, mesh);
CreateTriangleModel(pStart, pCorner2, pEnd, mesh);
}
I can't get the canvas to maintain its set size of 100x100 unless the children content goes all the way to these extremes. For my purposes, the line coordinates could be anywhere within the canvas and I need to have it maintain its desired shape regardless of the content.
Any advice would be greatly appreciated!
Some things to try:
Create an image file of 100x100 pixels by hand and then put it into an ImageBrush, and use that brush in your DiffuseMaterial. This is to confirm that your texture coords, etc are being set up properly.
Instead of using a Transparent Background colour on your Canvas, try Green (this is to see if the texture is being cropped based on transparent pixels)
If the cropping is being done of your texture (due to transparency)...then try putting a rectangle on the Canvas at the dimensions you want the texture to be...maybe that will change something.
Or try using RenderTargetBitmap to create an image from your Canvas visual...then use that image within an ImageBrush. (you can then confirm that the image is of your expected area of 100x100).
Finally, instead of using a Canvas+VisualBrush to create your texture try creating the Brush by using a DrawingBrush+DrawingGroup+GeometryDrawing

Redrawing and custom shaped windows question

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);
}

Merging Shapes and Attaching Partial Drag/Drop in WPF

I would like to draw two shapes in WPF and merge them together. Then, I'd like to attach a drag/drop event to ONE of the original shapes.
So basically, you can only drag if you click on a certain part of the shape, but it will drag the entire shape with you.
Here is some code:
// Set up some basic properties for the two ellipses
Point centerPoint = new Point(100, 100);
SolidColorBrush ellipseColor_1 = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
double width_1 = 10; double height_1 = 10;
SolidColorBrush ellipseColor_2 = new SolidColorBrush(Color.FromArgb(50, 255, 0, 0));
double width_2 = 200; double height_2 = 200;
// Create the first ellipse: A small blue dot
// Then position it in the correct location (centerPoint)
Ellipse ellipse_1 = new Ellipse() { Fill = ellipseColor_1, Width = width_1, Height = height_1 };
ellipse_1.RenderTransform = new TranslateTransform(point.X - width_1 / 2, point.Y - height_1 / 2);
// Create the second ellipse: A large red, semi-transparent circle
// Then position it in the correct location (centerPoint)
Ellipse ellipse_2 = new Ellipse() { Fill = ellipseColor_2, Width = width_2, Height = height_2 };
ellipse_2.RenderTransform = new TranslateTransform(point.X - width_2 / 2, point.Y - height_2 / 2);
// ???
// How should I merge these?
// ???
// Now apply drag drop behavior to ONLY ellipse_1
MouseDragElementBehavior dragBehavior = new MouseDragElementBehavior();
dragBehavior.Attach(ellipse_1); // This may change depending on the above
// ...
// Add new element to canvas
This code creates two circles (a big one and a small one). I would like to only be able to drag if the small one is clicked, but I'd like to have them attached so they'll move together without having to manually add code that will take care of this.
If you put them both in a Grid (or Canvas, StackPanel, etc.), and set the drag behavior on the panel, they will be "merged". If you set IsHitTestVisible to false on ellipse_2, it won't respond to any mouse events, so effectively it won't be draggable.

Categories

Resources