Calculating Scale using Anchors - wpf

At a high-level, I want a usercontrol with a contentpresenter like this:
<UserControl>
<ContentPresenter />
</UserControl>
I want to implement it in my consuming XAML like this:
<Controls:MyControl>
<Image src="http://server/file.png" />
</Controls:MyControl>
Then I want my usercontrol to add resize and rotate anchors like this:
The user can grab the center gainsboro area and move the user control - which will update its transform translate properties. The user can grab the resize anchors (squares) and resize the user control - which will update its transform scale properties. The user can grab the rotate anchor (circle) and rotate the control - which will update its transform rotate property. Seems so simple.
I wrote an article on how to handle manipulations here: http://blog.jerrynixon.com/2013/07/walkthrough-real-world-manipulation.html
There are plenty of articles written about manipulations, but the need for this user control is different. If we only think about scaling the object, the requirements are not just to scale. The requirements would be:
When the user drags a corner anchor, and scaling begins, the anchor itself should not change in size. This prevents the touch target from shrinking or occluding the object.
When the user drags a corner anchor, the render origin should change to the opposite corner so scaling is toward and away from the selected anchor. It should not be centered.
When the user drags a corner anchor, the anchor should remain under the user's pointer. It should not wildly expand the object beyond the pointer's context.
Those three are more difficult than you might think. And they don't even deal with the problem of rotation that. But that's for another time. Another question.
Number one (Solved) Ensuring that the corner anchor does not scale with the rest of the control is really a matter of converting the current scale factor with a simple converter like this:
class ResizeConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, string language)
{ return 1 / System.Convert.ToDouble(value); }
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{ throw new NotImplementedException(); }
}
Number two Wow, this one is harder than it looks. When you change the value of RenderTransformOrigin any existing transform is re-rendered with the new value. I am not 100% how to properly accommodate existing transforms before changing that value.
Number three This is the coupe de gras of the problem. Calculating the current location of the pointer is not too difficult. Calculating how to get the anchor under the pointer - that's also somewhat easy. But accounting for the scale of the underlying object so it properly fills (even skews) it so fit in the newly defined box? No clue.
To be honest, I think it might be important to calculate the distance from the original manipulation starting point and the current manipulation position. To do this, I create this nice method:
private double Distance(Point point1, Point point2)
{
var x1 = point1.X;
var y1 = point1.Y;
var x2 = point2.X;
var y2 = point2.Y;
return Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
}
Reality is, I was only partly sure I was on the right track.
This is already accomplished in a lot of existing code - but not in C#, not using XAML. Visual Studio lets you resize designer elements with anchors. It's not a novel concept. But still, my goal is to figure this out and publish a working sample that any developer can use in their XAML application (including my own). But there are some things here that are stumping me.
Could someone help me solve this part of the problem?

I am not sure, but this project have a lot scale and anchors using for designing diagrams, maybe you get inspiration from here
WPF Diagram designer ;-)

Related

Editable graphics in WPF

I am developing a WPF application that displays complex graphics to a window. I need these graphics to be highly dynamic, meaning that for instance lines can appear, disappear or just be moved at any time (by mouse interaction or programmatically).
Is there a way to achieve that without rebuilding the complete display list every time ? A typical case is that of the plot of a function where the data points can move, but not the rest. The points may be computed on the fly and not remembered in an array, so referring to their graphical representation is tricky.
Code example (on request):
// Add a Line Element
myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 1;
myLine.X2 = 50;
myLine.Y1 = 1;
myLine.Y2 = 50;
myLine.HorizontalAlignment = HorizontalAlignment.Left;
myLine.VerticalAlignment = VerticalAlignment.Center;
myLine.StrokeThickness = 2;
myGrid.Children.Add(myLine);
Update:
Assume I plot three functions. I draw the axis, the legends, the tick marks... and three curves.
Now is there a better way than redrawing everything if I want to remove the second curve ?
Is there a way to achieve that without rebuilding the complete display list every time ?
That's exactly what the MVVM pattern in general, and WPF templates together aim to provide. Specifically, you don't describe much of what you're trying to achieve except for this:
lines can appear, disappear or just be moved at any time
To me that sounds like two model classes, one for points (X and Y coordinates) and one for lines (that hold references to two points). The point model is observable (ie it implements INotifyPropertyChanged correctly and fully), while the line model should never change.
Coming to your view model, what you have is a collection of entities (something like IEntity), from which both models are derived. So simply a read only property of type ObservableCollection<IEntity>. When you want to move a point from a line, simply update its X and/or Y coordinate. Want to add a line? Add the points and the line connecting them to your collection. Same for deletion. Easy!
And the last part, your view. This holds an ItemsControl bound to your view model collection, with the panel template set to <Canvas/> (to be able to set location directly) and <DataTemplate> resources, one for each type of model you have:
Lines are simple, they're simply drawn in place by binding their four location properties to the X and Y coordinates of your two point models in your line model.
Points are where you get fancy, their purpose is to provide manipulators to move them around, ie a user control that responds to drag events for example and that updates its bound properties. Because lines hold references to your points, simply updating the point directly will reposition both the affected point visually, as well as the line that connects it.

WPF MVVM binding controls with helper methods PDFTron PDFViewer

I am struggling with a WPF MVVM problem using a control that has helper functions and exposing those helper functions to my viewmodels.
I am using the PDFTron viewer control that shows a PDFDocument object. Items in that PDFDocument are specified in terms of a PDFTron.Rect structure with the elements measured in points(1/72th inch) and a page number
To help convert the coordinate systems to and from screen position the PDFTron ViewerControl has various function on itself.
e.g. To convert from a screen point
Double x, y;
int page = Control.GetPageNumberFromScreenPt(x, y);
Control.ConvScreenPtToPagePt(ref x, ref y, page);
To convert to a screen point is from the object being examined
PDFTron.Rect r = Control.GetScreenRectForAnnot(embeddedObject, pageNumber);
What the best way of calling/exposing or binding to functions like this between the view model(s) and the PDF control as I would really like to databind to coordinates.
For instance I have an adorner defined in XAML that allows me to move an image I have read from the embeddedobject about the page by dragging and I can also resize the image. The viewmodel knows As my view model knows the image the embedded object its page and Rectangle on that page, but as PDF coordinates. But this needs to be translated to screen coordinates for the X, Y, Width and Height for binding to the XAML Attributes. I cannot quite see how to do this as it seems beyond a dataconverter.
So the control in the xaml has attributes measured in screen units
AdornerLeft="{Binding Data.X, Mode=TwoWay}"
AdornerTop="{Binding Data.Y, Mode=TwoWay}"
AdornerWidth="{Binding Data.Width, Mode=TwoWay}"
AdornerHeight="{Binding Data.Height, Mode=TwoWay}"
If the X and Y change I need the change to be reflected eventually as the page,x,y coordinates of the viewer as they are what are used by the underlying model.
I did wonder would it be wise to make a series of dependency properties so if I change one it ripples the change through the others as a conversion? So for example I have a property ScreenY When that changes it updates PDFY and PDFPage and vice versa but that seems overly complicated. Any suggestions?
Typically what is done, is that during user interaction, so while your user is moving and resizing the image, everything is drawn overtop of PDFViewWPF viewer.
You can get a Canvas object from PDFViewWPF.GetCanvas() and then you can draw your image on that if you like.
At this point, nothing relates to the PDF, you are just dealing with WPF coordinates.
Only once the user is done moving and dragging, and you want to add the image to the PDF page, perhaps as a Stamp annotation, or even injecting the image into the page content, only then would you erase all your graphics, and inject the image into the PDF. Only at this point would you need to translate between coordinate systems.
Please take a closer look at the PDFViewWPFTools project, and see how something like the Rectangle annotations are created.
This post might provide additional clarity for you.
I ended up using the Prism library and the eventAggregator to pass the details back to a the View and get the results, also using events to refresh the view. The PDFWPFViewer MVVM sample from PdfTron beside having some issues with incorrect bindings, essentially uses a tool library that is shared and acts like a windows forms library, no behaviors for controlling adorner drag etc.

Strategy for implementing a complex curve editor with XAML/WPF

I want to implement a rather complex CurveEditor that has to support the usual requirements like:
freely scalable and moveable axis
different interpolation types per curve point (Linear, Cubic, Spline)
Tangents (joined and broken)
Selecting one or several points to edit (move, scale, delete) via Fence or Click
Only show handles and highlights for selected curve points
I don't want to manipulate actual WPF curves but an existing model with key/value/tangents sets and sample the precise shape of the curve from our implementation.
I already gathered some experience on implementing custom UserControls and Templates. But I want to make sure, I don't miss any apparent solution. I planned to have this general XAML-tree:
CurveEditor - Window holding all content
MainThumb : Enable dragging and scaling the editor range
XAxis : UserControl rending some scale on the left side
YAxis : UserControl rending some scale on the bottom
Curves : Canvas holding the curves
Curve : UserControl for a single curve
CurveName - Label of the curve
CurveLine - DrawingVisual that will render the actual curve by sampling the internal implementation of the spline function.
CurveEditPoints - Canvas that holds all edit points
CurveEditPoint - UserControl for a single edit point
LeftTangent - UserControl for the left tangent handle
LeftTangentThumb - For modifying the handle
RightTangent - UserControl for the right tangent handle
RightTangentThumb - For modifying the handle
CurvePointCenter - Visualisation of the actual point, select state and interpolation type.
CurvePointThumb - Thumb to select and drag point around
I know, this is quite a complex question and I am not asking for an actual implementation. I am interested in the following questions:
Can you recommend any tutorials or books that might help me (I already got Illustrated WPF, WPF Control Development Unleashed, and a couple of other)
Should minor elements like the Tangents be individual UserControls?
What container is best suited for hosting the individual "Curves", "EditPoints" and "Tangents". Right now, I use Canvas and Canvas.SetLeft/SetTop to position the children, but that feels "strange".
Should I use "Shapes" like Path or DrawingVisual-Classes to implement actual representation. Path is straight forward, but I am concerned about performance with hundreds of CurvePoints.
Should I use Transforms to rotate the tangents or is just fine to do some triangulation math in the code behind files?
Does the structure roughly make sense, or do you suggest a completely different approach?
you seem to have the right tools at hand, WPF Unleashed is excellent, but I guess you have that one already.
make individual UserControls in one of these cases:
you are using the same xaml all over the place (DRY)
you xaml file gets too big (get some components out)
you need to inherit from some class
this depends on how much codebehind you want.
you can, as you suggested in your comment, use an ItemsControl as a container for wherever you need selection between multiple items. so this could also be done on the level of Curves, not just on the level of points on the curve. Depending on how you want to handle drawing of the actual lines and curves you can even have an ItemsControl for those. (on a side note: you will not have virtualization out of the box though, as your items won't have a constant height)
Path is OK with hundreds of CurvePoints. If you have 10.000, I'd say you could get problems.
can't imagine how a transform should be used here, maybe inside an Adorner.
your structure looks good. you will be able to implement all of this. I will suggest though how I would do it:
first of all use MVVM.
CurveEditor
ListBox(Panel=Canvas)(ItemsSource=Curves)(ItemTemplate=CurveControl)
CurveControl
Canvas(Background=Transparent) <= I'm not sure if standard is white, but you don't want to overlap other Curves...
CurveName
ListBox(Panel=Canvas(Background=Transparent))(ItemsSource=CurveParts)
ListBox(Panel=Canvas(Background=Transparent))(ItemsSource=CurvePoints)(ItemTemplate=>EditPointControl)
EditPointControl
Canvas
Thumb(Template = Ellipse) (Name=CenterHandle) (with some Visualstates for Selection and hiding of Tangents)
Thumb(Template = Ellipse) (Name=LeftHandle)
Thumb(Template = Ellipse) (Name=RightHandle)
Line (Binding X/Y to Centerpoint and LeftHandlePoint)
Line (Binding X/Y to Centerpoint and RightHandlePoint)
I have stated to set ItemTemplate for the ListBox. You can however style the listbox however you want (I think the standard style includes a border, you might want to remove that or set bordersize=0) and set instead of ItemTemplate the ItemContainerStyle and bind to IsSelected so you have control over IsSelected from your ViewModel (look here for what I mean).
So the viewmodel looks like this:
- CurveEditorViewModel
- ObservableCollection<CurveViewModel> Curves
- CurveViewModel
- string Label
- (Point LabelPlacement)
- bool IsSelected
- ObservableCollection<CurvePointViewModel> CurvePoints
- ObservableCollection<CurvePartViewModel> CurveParts
- CurvePointViewModel
- Point Position
- bool IsSelected
- Point LeftHandle
- Point RightHandle
- CurvePartViewModel
- CurvePointViewModel StartPoint
- CurvePointViewModel EndPoint
- Path CurvePath
in here you can subscribe to CurvePointViewModel's PropertyChanged and recalculate the Path you're exposing.
I'd probably improve on it as I go but that'd be my first guess.
There are some details you might want to watch out for. eg: the style for the thumbs might be a visible circle in the middle and an invisible bigger one around that with background=transparent. that way you can have your visible circle small, but have the user use the tumb in an area around the small circle.
EDIT:
here is an Example for the Thumb:
<Thumb Width="8" Height="8" Cursor="Hand" Margin="-4">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Grid>
<Ellipse Fill="Transparent" Margin="-6"/>
<Ellipse Stroke="Red" StrokeThickness="2"/>
</Grid>
</ControlTemplate>
</Thumb.Template>
</Thumb>
as you want to position this at a specific point on a canvas setting the Margin to minus half the width and height will place the center of the circle on that point. Furthermore, having that inner ellipse with a transparent fill and Margin of -6 you will get a 6px bigger radius around the inner (smaller) circle where you can drag the thumb.

Getting the true visual bounding box of a WPF element?

For a simplified version of my problem, I would like to calculate the bounding box of a layout-transformed (possibly even render-transformed) shape, so that I can always fit a rectangle perfectly around the shape, no matter what its rotation or scale may be. If you can solve this, I will be happy.
The more complex problem is that of calculating the visual bounding box of any framework element. By 'visual bounding box' I mean the top-most visible pixel within the framework element determines the top-bound, the right-most visible pixel determines the right-bound, etc. If you can solve this, I will be even more happy.
I have tried playing with LayoutInformation.GetLayoutSlot(), but this did not work in the expected manner. The 'layout slot' was actually MUCH larger than the actual bounds. I also tried using VisualTreeHelper.GetDescendantBounds(), but because of the VisualParent of my test shape being protected I could not manage to access this property, and decided to check here before I go any further into it.
I hope that somebody can provide an easy way of getting the true visual bounding box of an element in WPF, that is calculated AFTER all transforms. If I have not made something clear in my question, please let me know.
private Rect GetRectOfObject(FrameworkElement _element)
{
Rect rectangleBounds = _element.RenderTransform.TransformBounds(
new Rect(_element.RenderSize);
return rectangleBounds;
}
Maybe this will help out.
You will get good results with VisualTreeHelper.GetDescendantBounds() and you can use VisualTreeHelper.GetParent() to gain access to the otherwise protected VisualParent property. However what you probably want to do is call GetDescendantBounds on the shape itself, not its parent, because in spite of its name, the method returns the bounds of the parent and all of its decendants.
The problem is not easy, as a control may draw outside its bounds.
But if you assume this doesn't happen you can solve the problem by using parent.TranslatePoint(point_in_child_coord_system, child) to transform (0,0), (child.ActualWidth,0), (child.ActualWidth, child.ActualHeight) and (0,child.ActualHeight) to the parent coord system. Then sort the x and y coordinates of all points and use minimum and maximum values to find the bounding box.
Note: sorting is necessary because of possible child rotation.

Best way to write a custom gauge control in WPF?

I need to write a gauge control in WPF for a project at work. By gauge control, I mean something akin to a traditional car speedometer: a circular face with numbers and ticks around the circumference, and a needle pointing to the current value. Although I could get my employer to purchase a third-party control, or even find a free one somewhere, I’d still like to write it myself, for curiosity’s sake more than anything else.
My first thought was to ‘skin’ an existing control using a template, something like a ProgressBar for example as it has Minimum, Maximum and Value properties. However, I don’t think this will offer me the flexibility that I need.
I’d like to animate the needle that points to the current value, so that when the gauge’s value changes the needle moves from the old value to the new value. I figured the easiest way to do this would be to have some kind of shape or geometry representing the needle, and then apply a RotateTransform of the relevant number of degrees to get the needle to point to the correct value. The animation’s To property would be set to the ‘value degrees’ property via binding.
It seems to me that there are three basic approaches I could take:
Use one custom FrameworkElement to represent the entire gauge
I could derive from FrameworkElement, override its OnRender method, then use the DrawingContext to draw both the gauge face and the needle. This would mean that I could no longer animate the needle directly using a RotateTransform, but would instead have to redraw the entire gauge every time the value changes. This seems inefficient to me, as the gauge face represents the bulk of the drawing code but would change very rarely. However, this approach is the most lightweight in terms of the number of elements used.
Use two custom FrameworkElements, one for the face and one for the needle
I’m currently leaning towards this approach. One element would represent the face, the other the needle. This would allow me to target the needle element with the RotateTransform. The gauge control would then consist of three elements: the face, the needle, and a Panel container to hold both of them (ie. Canvas, Grid, whatever). So three elements instead of one, hence not as lightweight as the first approach.
Use DrawingVisuals to draw the face and needle
I’ve also read about the DrawingVisual class, but I’m a bit confused as to why anyone would use this as opposed to deriving from FrameworkElement and overriding OnRender, given that DrawingVisual must be hosted in a custom FrameworkElement anyway. It would seem that this approach doesn’t offer any advantages over the second approach, and would require more code, but maybe I’m missing something.
Any thoughts or advice regarding which approach people think is best and why would be most welcome!
Personally I'd recommend making a CustomControl using the second approach. Unless you are going to be showing more than 1000 of the gauges in view at the same time you aren't going to notice the extra element in your visual tree and I think you'll find it's much easier to make and maintain.
You could just style a slider control and feed values into it. You should be able to make a slider look like any kind of gauge you need.

Resources