Editable graphics in WPF - 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.

Related

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.

Calculating Scale using Anchors

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

xaml schedule control how to cope with items of vastly differing durations

I have to create a schedule control using WPF XAML for displaying items with duration as little as 1 seconds and potentially as large as couple of hours.
First thing which seems clear is that I will need some kind of zoom capability, for drilling into the items with very short durations and zooming out to view the items with very large durations.
I have a UI style in mind where by when the control is zoomed to view the large items, any small duration items which generally occur between the large duration items are some how collected in to a 'bucket' which has a height >= a minimum display height. So that I can perhaps hover a mouse over this bucket and get a tool tip which renders the contained items into a time ordered list view.
I am at a loss as to where to begin. I feel that I could perhaps achieve something in the Measure/Arrange overrides of a custom Panel. But I think this could adversely affect render performance. If I were to do it this way, I guess I would need to inject a 'bucket' view into the panels children collection, but that would break the use of ItemTemplate in my Custom Items Control?
My second though is that I create a custom observable collection which has a bindable Scale property which then organises the collection into 'buckets' of a duration large enough to be displayable.
Does anyone have any thoughts on how I should best approach this problem?
I made a similar sounding control which was actually used for inputting employee time shifts. It uses a simple data type in an ObservableCollection data bound to a ListBox.ItemsSource property. I defined a DataTemplate to design the look of each time segment in the UI. It looks like this:
There are certain ComboBoxes along the top which, when changed, change the number of items in the collection. So for example, when the Minutes/segment ComboBox value is changed to Thirty, twice the number of (same sized) items appear in the UI, each now relating to thirty minutes, not sixty. This is done by simply calling a method that repopulates the collection from the relevant property setters. Here is an example:
public TimeSegmentDivision MinutesPerSegment
{
get { return minutesPerSegment; }
set
{
minutesPerSegment = value;
InitializeTimeSegmentsRowViewModels();
NotifyPropertyChanged("MinutesPerSegment");
}
}
private void InitializeTimeSegmentsRowViewModels()
{
if (DayCount == 5) AdjustStartDate(); // (DayCount == 5 is 'Monday - Friday')
DateTime tempDate = Date;
AllTimeSegments = new TimeSegmentsCollection();
for (int m = 0; m < DayCount; m++)
{
TimeSegmentsRowViewModel viewModel = new TimeSegmentsRowViewModel();
viewModel.TimeSegments = InitializeTimeSegments();
AllTimeSegments.Add(viewModel);
date = Date.AddDays(1);
}
NotifyPropertyChanged("AllTimeSegments");
date = tempDate;
}
The specifics of the InitializeTimeSegmentsRowViewModels method is unimportant here, but you should be able to get the right idea from this example.
I worked on custom schedule for Silverlight. It has similar layout loading process so I would try to answer.
I guess your main problem lies in the 'time bar'... Stop thinking about schedule as the big consistent control, it's some pack of custom controls. Take a look on grid. Such controls has a lot peaces.
At start we have to solve first problem - time bar. Right implementation will lead you to painless future))) So time bar it's some control that contains total time, or duration of the employee labors(this value can be set by user). And such thing like step , exactly step and duration will tell you size and position of labors. For time bar control step will tell where are visual ticks should be displayed. I mean time bar will look like rulers, but with time values instead of inches. So we are getting next problem - how to translate time into pixels? Unfortunately I didn't found best solution - I assumed 1min is equal to 1.6 pixels It was perfect for me... But you will have dynamic value that will dynamically increase or decrease pixel length of step. So we have total duration in time, we can convert it into pixels length. But total length could be much bigger then available size. So, now we have another time bar property - visual duration...
okay, I guess you understand my way of thinking... Changing pixel size of step you will get perfect zoom(not step time size, but pixel length).
And yes, you are on the right way if you want create custom panels with overrated measure\arrange methods. And don't worry about item template. It's easiest part. Your parent schedule control will have items source property of object type. All your labors will be content controls....

WPF Custom Panels - Split the final element's visual between the end and start of a panel region

I am currently attempting to write a control(s) that will display a collection of elements, each of which have a start time between 0 and 1. The desired visual look we're aiming for is something akin to a simple timeline.
I've already created a FrameworkElement that renders an individual element as a line (this line represents the transition between one element and the next, e.g. y(x) = x) and my intention is to use this element as part of the DataTemplate for my custom ItemsControl. By flipping the odd elements horizontally and butting them together in a panel, it should be possible to see a continuous series of transitions between the elements. I'm having difficulty in deciding how to implement this panel, in particular with filling 'dead space' that might occur at the start of the panel.
The collection of elements that I'm trying to draw represents a looping effect, but the first element may not start at time T=0. Because the effect loops back on itself this means that the gap between T=0 and the first effect's start time is actually made up of the final part of the last element in the collection. This means I have to find a way of chopping up the last visual element in the panel so that the front part sits at the end of the panel and the back part sits at the start.
Another way to think of this is in terms of a circle/pie chart - if element A starts at 15% and element B starts at 50%, element B would occupy the regions 50-100% and 0%-15% continuously.
I'm really looking for a way to get this done in WPF visually rather than by modifying the collection of items (e.g. by adding a fake 'padding' element between 0 and the first element) as this would create complications down the line when it comes to things like modifying/selecting items, etc.
Someone has suggested drawing this as a 0-1 effect (removing any offset on the first element if there is one) then recreating the offset using a pair of cameras/viewports, which is something I'm not familiar with and seems a bit overkill. Can anyone suggest a simpler or more elegant way?
The 3D XAML question posed below contains code that achieves the kind of effect I was looking for, with some modifications. I set it up as an orthographic camera and made the position and texture co-ordinates of the MeshGeometry3D into dependency properties:
Why does TextureCoordinates work as expected for a Viewport2DVisual3D, but not for a GeometryModel3D?

WPF Line, path ..etc custom drawing style

In WPF is there a way that you can modify the way any path is drawn APART from Dash-Dot sequences? Say I want draw a triple line for any path I am drawing or small triangles,waves..etc on the drawing path itself. I have tried Brushes but it will not follow the Path. Please help
thx
WPF's Geometry classes have all the primitives you need to accomplish this easily, but you will need to do it in code. When I need to do custom lines I usually construct a Drawing based on the Geometry, but in your case you can simply build a Geometry that has three lines in parallel and stroke that.
Start with PathGeometry.CreateFromGeometry() to get a PathGeometry for the input path
Use GetWidenedPathGeometry(), passing in the desired spacing, to get a new geometry whose edges correspond to the side lines
(optional) Remove segments at the end of the widened geometry, if desired
Combine the side line geomerty with original geometry using a CombinedGeometry
Stroke the combined geometry to get a triple line
More explanation on step 3: The widened geometry has line segments at the end of the original line. This causes a line to be drawn across the end of your line, which actually looks aesthetically pleasing in many situations. If your situation would look better without it, remove it by iterating the side line geometry and removing all line segments that pass through the endpoints of the original path.
The above takes about 8 lines of code if you don't strike off the ends, or 15 if you do.
A trick to make this convenient is to create an attached property which effectively coerces the Data property of the Path control it is attached to. With such an attached property, all you need to write is:
<Path TripleStroke.Enable="true" Data="..." />
If you know how to implement attached properties and register handlers in them, this is a piece of cake. If not, plan on spending several hours learning how to code attached properties to simulate value coercion before implementing the attached property approach.
Update
The basic technique I describe above can also be extended to allow an arbitrary pattern to be applied along a path. For an example, see custom brushes in the Expression Design tool. There is nothing built into WPF to do this for you, however, so you'll need to create it yourself, and I can tell you from experience that it is a lot of work. Here are the basic steps:
First create a method that takes a Geometry an existing Drawing, and some parameters for end caps, etc and creates a new Drawing that repeats the given Drawing along the path given by the Geometry. Then it is easy to draw a stroked path: Create a Drawing to describe the custom stroke, then display the stroke using a DrawingVisual that contains a Binding with a converter that calls your conversion method.
To actually implement the conversion method:
Convert the source drawing into a set of GeometryDrawing objects (I also supported ImageDrawing but that is more complicated since you need to use the 3D system to stretch the images). This is done by recursing through DrawingGroup objects, keeping track of transforms as you go, and constructing GeometryDrawings with appropriate transform.
Remove portions of geometry in the "end cap" areas of the original drawing and set them aside.
Iterate along the path, duplicating the GeometryDrawing objects repeatedly with appropriate coordinate transformations applied to all coordinates in the geometry.
Process the "end cap" sections of the geometry using the same procedure.
Also note in step 1 that any GlyphRunDrawings are handled using FormattedText.BuildGeometry to create an equivalent GeometryDrawing.
There is no supported method for doing this in WPF. The solution is going to involve either composite Path objects or fancy code-behind gymnastics. Are you specificly looking for a triple-line path implementation?

Resources