I have content presenter with contented bound to a shape from the templated parent. When the shape is an ellipse, the content presenter shows the ellipse, however when I change the Shape to path and set the data property to ellipse geometry nothing gets displayed, I am setting the stroke and fill same as on the ellipse shape. Here is how I am constructing the path:
Shape = new Path();
Shape.Data = new EllipseGeometry();
Shape.Fill = Brushes.Transparent;
Shape.Stroke = Brushes.CadetBlue;
However when I replace it with this it does work (Assuming Shape is of type Ellipse):
Shape = new Ellipse();
Shape.Fill = Brushes.Transparent;
Shape.Stroke = Brushes.CadetBlue;
The reason why I want to use a path with a geometry as data, is because I want to test intersection on the shape, but I don't know how to get the geometry of a shape object, where as if the shape is of type Path I can test against Shape.Data.
Any help would be appreciated.
The EllipseGeometry behaves a bit differently to the Ellipse shape. Its dimensions are defined by its RadiusX and RadiusY properties, which by default are 0, thus nothing is drawn. You can set these as follows:
Shape.Data = new EllipseGeometry { RadiusX = 1.0, RadiusY = 1.0 };
However, this still probably won't display as your Ellipse does. The Ellipse also defaults to Stretch.Fill for its Stretch property, but the Path has Stretch.None. If you change this, they should look the same:
Shape.Stretch = Stretch.Fill;
You can play around with the other properties of the Path and the EllipseGeometry to size, orient, and locate it correctly.
Related
To improve performances, I want to use DrawingVisual objects instead of Shapes
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/using-drawingvisual-objects?view=netframeworkdesktop-4.8
But I cannot figure out how to make the stroke invariant to scale transformations.
I found information (Invariant stroke thickness of Path regardless of the scale) recommending using Path instead of lines, but Path is a Shape and is incompatible with Drawing.
Is there a way to have a DrawingVisual with children scalable by transformation but maintaining the stroke thickness regardless of the scaling factor?
You would still have to transform Geometries instead of Drawings.
An example:
var transform = new ScaleTransform(10, 10);
using (var dc = drawingVisual.RenderOpen())
{
dc.DrawGeometry(
Brushes.AliceBlue,
new Pen(Brushes.Red, 3),
new RectangleGeometry
{
Rect = new Rect(5, 2, 15, 10),
Transform = transform
});
dc.DrawGeometry(
Brushes.Beige,
new Pen(Brushes.Navy, 3),
new EllipseGeometry
{
Center = new Point(12, 10),
RadiusX = 5,
RadiusY = 5,
Transform = transform
});
}
If the scaling factors are changing later, you do not need to redraw anything. Just set the ScaleX and ScaleY properties of the ScaleTransform that was initially assigned to the Transform properties of the Geometries.
Result:
I am using C#, Silverlight, Visual Studio for Windows Phone 7.
I would like to get the Clip of a UIElement that includes any transform that has been done on the UIElement.
This example gives me the Clip but does not include any transform from the UIElement:
// uie is a UIElement taken from a PhoneApplicationPage
Geometry myClip = uie.Clip;
I tried the following, but it ended up moving the UIElement:
var frame = Application.Current.RootVisual as PhoneApplicationFrame;
var page = frame.Content as PhoneApplicationPage;
GeneralTransform gt = uie.TransformToVisual(page);
uie.RenderTransform = gt as Transform;
Geometry myClip = uie.Clip;
I also tried to add an inverse transform at the end to undo any movement, but that seemed to make it worse:
uie.RenderTransform = gt.Inverse as Transform;
Please help me get the Clip with the original transform of the UIElement without messing with the UIElement itself.
Thanks in advance.
I figured out how to solve this without messing with the original UIElement. I needed to create a new Geometry object with the same data rather than using the original Geometry. Not only does this separate the new data from the old data, it also prevents an object from possibly getting assigned as a child to multiple objects.
Here is the resulting code I used:
var frame = Application.Current.RootVisual as PhoneApplicationFrame;
var page = frame.Content as PhoneApplicationPage;
GeneralTransform gt = uie.TransformToVisual(page);
var place = gt.Transform(new Point());
// declaring new variables to hold these values
Geometry geom;
Path path = new Path();
// here I checked for other restrictions on my UIElements before assigning the variables
geom = new RectangleGeometry();
(geom as RectangleGeometry).Rect = new Rect(place.X, place.Y, uie.RenderSize.Width, uie.RenderSize.Height);
path.Data = geom;
I mentioned that I check for some restrictions on my UIElements before assigning the variables. This is related to another question I posted about clips (here).
in my custom control i have a ContainerVisual object and a DrawingVisual under it.
I override ArrangeOverride and calculate the rectangle that i want to draw in based on the given size and the control's padding.
after that i set my ContainerVisual object's transform to the upper left corner of the rectangle so that the methods that render the drawing would not have to take account of the rectangle and assume that the drawing origin is at point 0,0.
this does not work, and the drawing is displaced. if instead i set transform of the DrawingVisual object it works and the rectangle is displayed the way it is supposed to be.
i thought that if i set transform on the container, it will automatically be applied to the visuals under it. is that so?
thanks for any help
EDIT: Updated the source code to show complete code.
class MyControl : Control
{
private readonly ContainerVisual container = new ContainerVisual();
private readonly DrawingVisual drawing = new DrawingVisual();
private Rect rect;
private void RenderDrawing()
{
using (var c = drawing.RenderOpen())
{
var p = new Pen(new SolidColorBrush(Colors.Black), 1);
c.DrawRectangle(null, p, new Rect(0, 0, rect.Width, rect.Height));
}
}
protected override Size ArrangeOverride(Size s)
{
var h = Math.Max(0, s.Height - Padding.Top - Padding.Bottom);
var w = Math.Max(0, s.Width - Padding.Left - Padding.Right);
var r = new Rect(Padding.Left, Padding.Top, w, h);
if (rect != r)
{
rect = r;
container.Clip = new RectangleGeometry(rect);
container.Transform = new TranslateTransform(rect.Left, rect.Top);
// replace the line above with the following line to make it work
// drawing.Transform = new TranslateTransform(rect.Left, rect.Top);
RenderDrawing();
}
return s;
}
protected override Visual GetVisualChild(int index)
{
return container;
}
protected override Size MeasureOverride(Size s)
{
return new Size();
}
protected override int VisualChildrenCount
{
get { return 1; }
}
public MyControl()
{
container.Children.Add(drawing);
AddVisualChild(container);
}
}
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:c="clr-namespace:MyApp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<c:MyControl Padding="20" />
</Grid>
</Window>
Explanation of strange clipping behavior
Now that you have posted your full source code I was finally able to see what you were seeing. Your problem isn't in the transform at all: It is in the clip!
If you comment out the container.Clip assignment statement, you get identical results no matter whether you put the transform on container or drawing
If you uncommented container.Clip assignment statement, the clipping region is perfectly centered on when the drawing is transformed, but when the container is transformed the clipping area is offset, so that only the lower and right lines of the rectangle were visible (and not all of those)
The reason this occurs is that the geometry specified for container.Clip is part of the container, so it is affected by container.Transform but not drawing.Transform:
This can be better understood by looking at the upper-left corners of the container, drawing, rectangle, and clip area relative to the upper-left corner of the window:
When you set the transform on the drawing:
Container is at (0,0) relative to window (null transform)
Clip area is at (20,20) relative to window (null transform + RectangleGeometry)
Drawing is at (20,20) relative to window (null transform + TranslateTransform)
Rectangle is at (20,20) relative to window (null transform + TranslateTransform + 0,0)
When you set the transform on the container:
Container is at (20,20) relative to window (TranslateTransform)
Clip area is at (40,40) relative to window (TranslateTransform + RectangleGeometry)
Drawing is at (20,20) relative to window (TranslateTransform + null transform)
Rectangle is at (20,20) relative to window (TranslateTransform + null transform + 0,0)
So your problem isn't that the transform isn't happening: It is that the transform is moving the clip area too, so the clip area no longer coincides with the rectangle and you can only see two sides of the rectangle.
Answer given for original code (retained because it has some useful explanation)
In fact, the code you posted never uses "container" so all you will see is a blank screen.
In your actual code you are using "container" incorrectly, preventing the events from occurring in the correct sequence to cause its Transform to be picked up and passed to the MIL layer.
Remember that when a Visual has a Transform set, it is not the visual itself but that Visual's visual parent that actually handles that transform. For example, if you render a page to XPS using ReachFramework or do hit testing, the Transform on the outermost Visual is ignored.
Your understanding is correct: If your visual tree is built following all the rules, it doesn't matter whether your transform is on your "container" or your "drawing".
Since you are using Control anyway, I'm curious why you don't just let the normal UIElement-based layout system handle your layout needs.
First update (retained for the same reason)
Thanks for the code correction. It is as I suspected: You are building your visual tree incorrectly. If you are using AddVisualChild you also must also override GetVisualChild and VisuaChildrenCount. This is because Visual does not store a list of children: It is up to the subclass (your class) to do this. What is happening is:
When you call AddVisualChild the container's transform is null so that is what is passed down to MILCore.
Later when you change the container's transform, it uses its parent pointer (that was set in AddVisualChild) to signal that its transform data must be refreshed. This update requires part of the visual tree to be scanned using GetVisualChild and VisualChildrenCount.
Since you didn't implement these methods this part of the update fails.
You say you are "new to WPF." Are you aware that you are playing with some of WPF's most low-level and esoteric features, ones that would never be used in a most ordinary WPF applications? It is equivalent to starting to learn programming using machine language. Normally you would use templates with Path, Rectangle, etc for this purpose. Sometimes you might go lower level and use a DrawingBrush with a DrawingGroup containing GeometryDrawings, etc. But you would almost never go all the way down to DrawingVisual and RenderOpen! The only time you would do that is when you have huge drawings consisting of millions of individual items and so you want to bypass all the layout and structure overhead of the higher layers for absolute maximum performance.
Manipulating the visual tree yourself (AddVisualChild, etc) is also an advanced feature. I always recommend people new to WPF stick with UIElement and above for the first few months, using Control with templates. I recommend they use Path and other shape subclasses for their drawings, and use VisualBrushes when advanced drawing effects are needed.
Hope this helps.
the problem is with the container.Clip. it should be
container.Clip = new RectangleGeometry(new Rect(0, 0, w, h));
I need a Viewport3D for the sole purpose of doing geometric calculations using Petzold.Media3D.ViewportInfo. I would prefer not to have to place it in a Window or otherwise render it.
I'm attempting to accomplish this by instantiating a Viewport3D and setting a few properties using the following C# method:
private Viewport3D CreateViewport(MainSettings settings)
{
var cameraPosition = new Point3D(0, 0, settings.CameraHeight);
var cameraLookDirection = new Vector3D(0, 0, -1);
var cameraUpDirection = new Vector3D(0, 1, 0);
var camera = new PerspectiveCamera
{
Position = cameraPosition,
LookDirection = cameraLookDirection,
UpDirection = cameraUpDirection
};
var viewport = new Viewport3D
{
Camera = camera,
Width = settings.ViewportWidth,
Height = settings.ViewportHeight
};
return viewport;
}
Later, I'm attempting to use this viewport to convert the mouse location to a 3D location using this method:
public Point3D? Point2dToPoint3d(Point point)
{
var range = new LineRange();
var isValid = ViewportInfo.Point2DtoPoint3D(_viewport, point, out range);
if (isValid)
return range.PointFromZ(0);
else
return null;
}
Unfortunately, it's not working. I think the reason is that the ActualWidth and ActualHeight of the viewport are both zero (and these are read-only properties, so I can't set them manually). (Note: I have tested the exact same method with an actual rendered Viewport3D, and it worked fine, so I know the issue is not with my converter method.)
Any idea how I can get WPF to assign the ActualWidth and ActualHeight of a control based on the Width and Height settings?
I tried setting the HorizontalAlignment and VerticalAlignment to Left and Top, respectively, and I also messed with the MinWidth and MinHeight, but none of these properties had any effect on the ActualWidth or ActualHeight.
From MSDN topic for the ActualWidth property:
This property is a calculated value based on other width inputs, and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties such as Width that are the basis of the input change.
So, this sounds like a rendering pass is necessary for the property to be set. However, you could try to call Measure(Size) and then Arrange(Rect) to simulate the layout process. Maybe this is already sufficient.
How do I implement a tab control with vertical tabs in C#?
Create an instance of System.Windows.Forms.TabControl (one of the standard container controls for Windows Forms) and set the Alignment property to Left.
First set in properties the Alignment property to Left.
Second set SizeMode property to Fixe.
Third set ItemSize property to prefered size example width :30 height :120.
After that you need to set the DrawMode property to OwnerDrawFixed.
Next step is define a handler for the DrawItem event of TabControl that renders the text from left to right.
Example
In form Designers.cs file
TabControl.DrawItem += new DrawItemEventHandler(tabControl_DrawItem);
Definition for tabControl_DrawItem method:
private void tabControl_DrawItem(Object sender, System.Windows.Forms.DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
// Get the item from the collection.
TabPage _tabPage = TabControl.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _tabBounds = TabControl.GetTabRect(e.Index);
_textBrush = new System.Drawing.SolidBrush(Color.Black);
// Use our own font.
Font _tabFont = new Font("Arial", (float)12.0, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
Effect:Ready horizontal tabcontrol
I was based on https://msdn.microsoft.com/en-us/library/ms404305(v=vs.110).aspx