Why is a GeometryDrawing displayed on Canvas with clipped coordinates? - c#

I have the following simple code that draws rectangle
<Canvas Name="MainImageLayer" >
<Image >
<Image.Source >
<DrawingImage xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >
<DrawingImage.Drawing >
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="#FF1acc33" Thickness="1" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush>Red</SolidColorBrush>
</GeometryDrawing.Brush>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="300,480,287,83" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Canvas>
The result looks like this - note that the rectangle is in (0,0), even that the Rect is defined as
<RectangleGeometry Rect="300,480,287,83" />
I want it to start at (300,480), like that:
I can achieve that by inspecting my DrawingImage and doing :
<Canvas.Top>300</Canvas.Top>
<Canvas.Left>480</Canvas.Left>
But isn't there a better way, considering the fact that this data is encoded into the Geometry?

Your problem comes from the fact that you have all of your geometry wrapped in an 'Image' object. By default, the .Height and .Width properties of an Image object are set to 'Auto', and the .Stretch property is set to 'Uniform'. This guarantees that your rectangle will always appear in the top left corner of your Canvas.
If you really need to encapsulate your geometry in an 'Image' object (which I would caution you not to do) you will need to set the Margin of your Image object to 300,480,0,0. in order to get your rectangle to appear where you want it. This is required because of the way an 'Image' object handles its contents.
An Image object does not behave like a Canvas object, even if it is inside one.
Unless there is some overwhelming reason to keep the Image object, you will have much better success if you discard the Image and draw directly on the Canvas.
EDIT
Why should an Image object not be used in this case?
An Image object is primarily used for displaying...well, images, like bitmaps and such. It is not suited for drawing geometry in it at a specific location (and size). Like most WPF controls, it is what I would call a 'relative' control, meaning it is well suited for automatically resizing and positioning itself in relation to both its contents and its parent. A Canvas on the other hand is an example of an 'absolute' control. Its entire reason for existence is to allow content to be drawn upon it in an exact location with an exact size. Adding an Image inside a Canvas and then drawing the geometry inside the Image just adds an unnecessary layer of complexity between the Canvas and the geometry that needs to be drawn.
How can the geometry be added directly?
One of the easiest ways would be to use the Path object given in Clemens' answer. Just replace your entire Image object and all its contents with the 5 lines of that Path and your rectangle will appear exactly where it should be. You can also do it with a single line and a Rectangle object:
<Rectangle Height="83" Width="287" Margin="300,480,0,0" Stroke="#FF1acc33" StrokeThickness="1" Fill="Red" />
but I would recommend the Path since it contains the size and position of the rectangle in one set of numbers. The Path also allows you much more flexibility if you are working with shapes other than rectangles.

Seems like the DrawingImage (or the GeometryDrawing?) is somehow adjusted to its visible rectangle. Anyway, wouldn't this be a much simpler solution, instead of Image?
<Path Stroke="#FF1acc33" StrokeThickness="1" Fill="Red">
<Path.Data>
<RectangleGeometry Rect="300,480,287,83" />
</Path.Data>
</Path>
You can also add another non-empty GeometryDrawing that covers (0,0) (e.g. a RectangeGeometry with Rect="0,0,1,1") with a transparent brush to the DrawingGroup.
EDIT: As i understood, the DrawingImage is adjusted to Drawing.Bounds

Related

How to clip an element fron another one in WPF? [duplicate]

Anyone know a good way to create this object from Xaml? It also has to work at .5 Opacity when layered on top of other controls.
It also has to be resizable via Horizontal or Vertical Alignment.
I'm having some difficulty. The closest I get is with 2 borders, one having a negative margin--but it doesn't work when Opacity is applied.
Code that works:
<Path Fill="Black">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="0,0,200,100" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry RadiusX="5" RadiusY="5" Rect="105,5,90,90" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Use a GeometryGroup with an EvenOdd FillRule, or a CombinedGeometry with a GeometryCombineMode of Xor or Exclude. The geometries to combine will both be RectangleGeometry objects, with an appropriate RadiusX and RadiusY. The result will be the outer rectangle with a "hole" in it where the inner rectangle was located. (I assume this is what you want rather than a white rectangle within the black one.)
You can then assign this composite geometry to a Path as its Data property, and set the Fill and Opacity as required.

How to override all Shapes in a specific area WPF

Currently I am filling my MainWindow with a slightly transparent black:
But I want it to have a "hole" where this effect doesn't take place which should look like the following:
So this needs to be done at runtime since the area which the hole represents is going to change multiple times while the program is running.
What I thought I could do
So at first I thought I could just cut the area in the middle out
like you could do with a Graphics object, but the slightly
transparent black is nothing but a rectangle which is added as a child on a canvas which is currently done like this:
var background = new System.Windows.Shapes.Rectangle
{
Fill = new SolidColorBrush(System.Windows.Media.Color.FromArgb(150, 0, 0, 0)),
Width = ScreenInfo.Width,
Height = ScreenInfo.Height
};
MainCanvas.Children.Add(background);
But I couldn't fine any way to achieve this cut effect.
Creating 4 Rectangles which would look something like this: but this way of doing it didn't seem to me as the most effecient way of achieving this.
Thanks for any kind of help!
Create a CombinedGeometry by cutting a smaller square out of a larger one and then use that with a path. How you size it will depend on your application, a Viewbox will probably be good enough for most cases:
<Grid>
<TextBlock Text="Hello World!" FontSize="200" Foreground="Red" TextWrapping="Wrap" TextAlignment="Center"/>
<Viewbox Stretch="UniformToFill">
<Path Fill="#C0000000">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,4,4" />
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry x:Name="cutRect" Rect="1,1,2,2" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Viewbox>
</Grid>
Then to change the size of the inner geometry you can either bind its Rect to a view model property or change it directly in code-behind:
cutRect.Rect = new Rect(1, 1, 1, 1);

Use a GeometryDrawing object to remove margins and set the size of a vector icon

I'm trying to build a GridView with a list of file objects and their properties. One of the properties is the logo of the fieldbus protocol, supported by the file.
After the protocol logos were converted from .png to .xaml, the current UI looks like the following:
Current UI state
The example of one .xaml logo file:
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingImage x:Key="EtherCAT_design">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFFEFEFE"
Geometry="F1 M0,601.33335 L0,0 947.33335,0 1894.6667,0 1894.6667,601.33335 1894.6667,1202.6667 947.33335,1202.6667 0,1202.6667 z" />
<GeometryDrawing Brush="..."
Geometry="..." />
<GeometryDrawing Brush="..."
Geometry="..." />
...
...
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</ResourceDictionary>
Now, I would like to remove the margins and make the background transparent for each logo on the list.
What I've already tried:
Removing the first GeometryDrawing from the DrawingGroup.Children produces different default sizes for each picture (Removing the first GeometryDrawing)
Setting another value for the Geometry property of the first GeometryDrawing object: Geometry="F1M16,16L0,16 0,0 16,0z" still leaves a margin above the picture (Adjusting the Geometry property)
After reading the MSDN page it is still unclear to me, how to define the Geometry property so that I wouldn't have any margins and still have all the pictures of the same size on the list.
Apparently the first geometry is an overcomplicated version of the rectangle (0, 0, 1894.6667, 1202.6667).
Assuming that the center of the icon is at the center of the "frame" rectangle, i.e. at about (947, 601), you may simply create a smaller rectangle centered at the same point. Put that into a transparent GeometryDrawing:
<GeometryDrawing Brush="Transparent">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="547,201,800,800"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
The example above define a 800 x 800 rectangle, still centered at (947, 601)

Aligning text to a path in WPF

I'm working on a small part of an application that requires us to draw shapes for the user to see. I'm using a path to display the shape, specifically binding a PathSegmentCollection to the PathGeometry.Figures like this --
<Path Name="Shape"
Stretch="Uniform"">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure IsClosed="True"
Segments="{Binding Segments}"
StartPoint="{Binding StartPoint}" />
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
Somewhere on that shape, a logo will be shown, but due to the variance in the shapes, I'm struggling to think of a solution where the logo text will always be shown on the shape (the shapes can be anything, even from a DXF file).
Here's an illustration of the problem --
The left image is what I have, but the right image is what I want.
Right now I'm drawing the logo as just a textblock on a grid that's bound to the size of the shape. I ended up going this route to keep the text automatically scaled to the correct height no matter the scaled units of the shape.
Ideally what I'd like to do is place a textblock inside a path, but I know that's not possible. I was wondering if anyone had any good ideas on how to align this text to the shape no matter what shapes come up.
In theory I'd like to be able to align it to all the regular orientations (top-left, top-center, bottom-right etc).
Thanks in advance!

Wrap image around canvas in WPF C#

How can I wrap an image around the canvas like this? The obvious way I can think of is to duplicate the image, and offset by the width/height of the image in the opposite direction. Is there another way to achieve this?
You could fill a Rectangle with an ImageBrush with this image, and set its TileMode and Viewport properties as needed.
For example:
<Rectangle Width="128" Height="128">
<Rectangle.Fill>
<ImageBrush ImageSource="Images\Tile.png" TileMode="Tile"
ViewportUnits="Absolute" Viewport="64,64,128,128"/>
</Rectangle.Fill>
</Rectangle>
The above XAML creates the following output:
from this source image:
No. There is no other way around. You will have to draw all images. Because a control doesn't split up.
You can calculate and then generate Image boxes to show no. of images based on offset.

Categories

Resources