WPF MVVM binding controls with helper methods PDFTron PDFViewer - wpf

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.

Related

Winforms semi-transparent PNG over semi-transparent PNG

I think I must be missing something obvious, but I'm unable to find this after several hours of searching. Is there no way to use a PictureBox or other control to contain an image with partial transparent/alpha-blended pixels, and place that over another image and have the blending be based on the image under it?
For example, this produces the results I want:
Place a panel on a form.
Add an OnPaint handler.
In the OnPaint handler draw 1 PNG, then draw another PNG over it, using Graphics.DrawImage for both.
This does not:
Place a PictureBox on a form and set it to a PNG.
Place another PictureBox on the form and set it to a PNG.
Place the 2nd picture box over the first.
...even if the 2nd picture box is just empty and has a background color of Transparent, it still covers the picture below it.
I've read this stems from all winform controls being windows, so by nature they aren't transparent.
...but even the 15 year old platform I'm migrating from, Borland's VCL, had several windowless controls, so it's hard to imaging winforms doesn't at least have some easy solution?
My first example above is one answer, true, but that adds a lot of work when you can only use one big panel and draw all of your "controls" inside of it. Much nicer if you can have separate controls with separate mouse events/etc. Even if not an image control, and a control I have to draw myself, that would be fine, as long as I can just put one image in each control. In VCL they called this a "paint box", just a rectangle area you could place on a form and draw whatever you want on it. Has it's own mouse events, Bounds, etc. If you don't draw anything in it, it is like it's not even there (100% transparent) other than the fact it still gets mouse events, so can be used as a "hot spot" or "target" as well.
The PictureBox control supports transparency well, just set its BackColor property to Transparent. Which will make the pixels of its Parent visible as the background.
The rub is that the designer won't let you make the 2nd picture box a child of the 1st one. All you need is a wee bit of code in the constructor to re-parent it. And give it a new Location since that is relative from the parent. Like this:
public Form1() {
InitializeComponent();
pictureBox1.Controls.Add(pictureBox2);
pictureBox2.Location = new Point(0, 0);
pictureBox2.BackColor = Color.Transparent;
}
Don't hesitate to use OnPaint() btw.
Sorry, I just found this... once I decided to Google for "winforms transparent panel" instead of the searches I was doing before, the TransPictureBox example show seems to do exactly what I need:
Transparency Problem by Overlapped PictureBox's at C#
Looks like there are 2 parts to it:
Set WS_EX_TRANSPARENT for the window style
Override the "draw background" method (or optionally could probably make the control style Opaque).

Issues with rendering a large tiled map in WPF

What is the best way to manage a very large amount of images (10,000+) in WPF? This is for a 2d tile map editor similar to this : http://www.mapeditor.org/ .
At the moment i have a canvas with all tiles as an image and a list box which contains the different images to choose from. Each tile is added to the canvas as children and then stored in a list for later access. You paint into the canvas by setting the Source property of a tile to the one selected in the listbox. It works well with around 50x50 tile maps but anything above that causes loading delays, in general slow application.
Any suggestions on this? Would QT maybe be more suited instead of wpf?
Thanks in advance
Check out Implementing virtualized panel series of articles.
Virtualized panels are efficient, because:
Only the displayed elements (and a few extra around the borders to enable smooth scrolling) are in the memory (and rendered).
Elements are reused, instead of being repeatedly created and discarded - an old cell is simply filled with new content (supplied with new DataContext) and used in new location.
You might also try to use WPF's DataGrid for this, it supports virtualization out of the box and is essentially what are you trying to do.
WPF is certainly able to do this, if implemented properly (if you can do that in JavaScript, you can certainly do it in WPF as well).

Drawing a XAML map in WPF

I'm working on a module that displays DWG files in WPF. I've managed to use CadLib library but it's working very slowly and I want to make it faster. I found out that if I convert that DWG file to SVG format and then print it to my XPS printer and rename the file to ZIP, I can get .page file which is basically a XAML file that displays the original SVG object in XAML.
I want to display this XAML code on a custom control and be able to pan / zoom it around. I tried to place this XAML code in a Canvas and it did manage to show up there, but now I'm stuck trying to pan / zoom the shape that was drawn there. Also, the mouse events are fired only when you click the actual drawing itself, and not the Canvas, which will be hard for the user to click...
Any help would be highly appreciated :)
You could try creating nested Canvases : One that holds the vectors and is moved on demand, one that is fixed and serves as the viewport. Haven't tried that, but it should be feasible...

What is the best way in Silerlight to make areas of a large graphic clickable?

In a Silverlight application I have large images which have flow charts on them.
I need to handle the clicks on specific hotspots of the image where the flow chart boxes are.
Since the flow charts will always be different, the information of where the hotspots has to be dynamic, e.g. in a list of coordinates.
I've found article like this one but don't need the detail of e.g. the outline of countries but just simple rectangle and circle areas.
I've also found articles where they talk about overlaying an HTML image map over the silverlight application, but it has to be easier than this.
What is the best way to handle clicks on specific areas of an image in silverlight?
Place the Image and a Canvas in a Grid so that the Canvas overlays the Image.
Add shapes of appropriate sizes and placed as needed to the canvas. All shapes will a transparent fill and no border, hence the user only sees the Image. On the Canvas MouseDown (or Up events) use OriginalSource to determine which shape generated the click. Use the Tag property of each shape to associate it with some object that represents the flowchart element being mapped.
I found an easy way to do this without a canvas:
How to get the coordinates of an image mouse click in the event handler?

Rotating a .NET panel in Windows Forms

We use Windows Forms and custom user controls, and I would like to be able to rotate the panel hosting the userControl in a particular form. I have seen similar functionnalities with WPF, but I can't use it for the moment. Is it possible to achieve the rotation of a panel and its children using possibly built-in .NET methods or GDI+?
I have seen some pretty cool visual effect with menus that are displayed in game development, so I was wondering if it would be possible to create similar effects using Windows Forms.
Rotating a panel and its children in Windows Forms is not something directly supported, and I think it will end up being a buggy headache that could easily suck up lots of time. It's especially painful to think about when you could do this in WPF with zero lines of C# code and only a tiny bit of XAML.
You can use rotations in GDI+ by calling the RotateTransform method on a Graphics object.
However, rotating an entire control is not so simple, and will depend heavily on how the control is implemented.
If it's a composite UserControl that has other controls inside of it, you're out of luck.
If it's a sinlge control that paints itself, try inheriting the control, overriding the OnPaint method, and calling RotateTransform on the Graphics object. However, you will probably have trouble with it. In particular, you will probably need to override all of the mouse events and call the base control's events with rotated coordinates.
You can get halfway there by calling the DrawToBitmap method on your panel, then rotating the bitmap and displaying it e.g. in a PictureBox:
var bitmap = new Bitmap(panel.Width, panel.Height);
panel.DrawToBitmap(bitmap, new Rectangle(Point.Empty, panel.Size));
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
var pictureBox = new PictureBox();
pictureBox.Location = panel.Location;
pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;
pictureBox.Image = bitmap;
Controls.Remove(panel);
Controls.Add(pictureBox);
Rotation angles other than 90-degree increments are also possible, if you draw the bitmap into another bitmap using GDI:
var bitmap2 = new Bitmap(bmp.Width + 75, bmp.Height + 100);
var graphics = Graphics.FromImage(bmp2);
graphics.TranslateTransform(bitmap2.Width / 2, bitmap2.Height / 2);
graphics.RotateTransform(-15f);
graphics.TranslateTransform(-bitmap.Width / 2, -bitmap.Height / 2);
graphics.DrawImageUnscaled(bitmap, Point.Empty);
graphics.Dispose();
The problem of course is that you're only displaying an image of your panel, and not the panel itself, so it's no longer possible to interact with the controls inside.
That could probably be done as well, but you would have to mess with window messages, which gets quite a bit more complicated. Depending on your needs you might also be able to get away with handling click and key events on the PictureBox, manipulating the controls in the panel, and then updating the image.

Resources