Wpf hit testing is pretty good but the only method I found to extend the hit zone is to put a transparent padding area around your object. I can't find any method to add a transparent area arround a Path object. The path is very thin and I would like to enable hit testing if the user clicks near the path. I can't find any method to extend the path object with a transparent area like the image below :
alt text http://img175.imageshack.us/img175/6741/linepadding.png
I tried to used a partially transparent stroke brush but I ran into the problem described here : How can I draw a "soft" line in WPF (presumably using a LinearGradientBrush)?
I also tried to put an adorner over my line but because of WPF anti-aliasing algorithms, the position is way off when I zoom in my canvas and interfere with other objects hit-testing in a bad way.
Any suggestion to extend the hit testing zone would be highly appreciated.
Thanks,
Kumar
Path.Data is a geometry object. The Geometry class has several methods that can help you hit test with tolerance:
GetFlattenedPathGeometry(Double, ToleranceType)
GetOutlinedPathGeometry(Double, ToleranceType)
GetRenderBounds(Pen, Double, ToleranceType)
I think GetRenderBounds will work best for you.
Once you have the geometry (plus a little width) you can call
geometry.FillContains(Point, Double, ToleranceType)
or
geometry.StrokeContains(Pen, Point, Double, ToleranceType)
Out of all of that you should tune the desired hit from your hit test;
You can wrap the Path inside a transparent Border.
In WPF you can create another path with its geometry databound to the first (using Element Binding), but with transparent brush and increased thickness.
Something more or less like this:
<Path x:Name="backPath" Data="{Binding Data, ElementName=mainPath}" StrokeThickness="10" Stroke="Transparent"/>
<Path x:Name="mainPath" Data="{Binding DataFromViewModel}" StrokeThickness="1" Stroke="Red"/>
Note that the main path comes after in XAML, so that it is rendered on top.
Related
First time working on a GUI project.. and first time doing work on Windows so apologies in advance if this is a really noob question.
I'm taking baby steps into windows programming starting with vb.net WPF. Working in Visual Studio Express 2012.
I'm trying to work out how I can scale all the elements in a window with the window itself.
So for example, I'd create a window, say 1280x720, and place some images in the window. Say one at the top and one in the corner. (this is a basic media based application)
When I resize that window, I want the entire window to scale with it, so image 1 & 2 will get larger if the window gets larger, however this has to happen proportionally so that if I make the window a lot bigger in one direction one image can't overlap the other. Imagine the window is an image and I'm trying to resize it. (The overlap thing is the closest I've gotten to getting this working in my current attempts).
The layout in produciton will be more complex, comprising of mediaelements (video), images, text etc and all must scale accordingly.
This isn't something the user interacts with and so there are no form elements etc, and so I don't need form fields etc to stay the same size throughout scaling. I just need everything to scale like I'm scaling a picture. If for example I displayed this 1280x720 (16:9) layout on a 1920x1080 screen, maximised it should look identical only larger.
Hoping someone can point me in the right direction with this.
What I've tried so far- the few articles I did find on google relating to this (I may well be searching the wrong things) lead me to put all the elements in a viewbox, this lead to the overlap I mentioned earlier.
Ideas ?
I think you could use ViewBox container. The basic idea is as follows: ViewBox scales its content just as if it was an image scaled. This seems to be the closest result to what you've described in your question. Just put a Grid with absolutely-sized columns and rows into the ViewBox and set its Stretch to be Uniform:
<Viewbox Stretch="Uniform">
<Grid>
<..>Your controls, MediaElements, etc
<Grid>
</Viewbox>
You could also combine it (or entirely replace) with (e.g.) Grid Container : it gives you an ability to specify cell width and size usign star-syntax which is similair to html's percent syntax.
Another way is to use the DockPanel.
All-in-all there are plenty ways to achieve something similair and the way to go largely depends on the nuances of your particulair requirements.
Have a look at This tutorial to see a good overview of WPF containers and how to use them.
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).
Ok, I've created a PNG-24 with transparency. It's basically a grayscale image that uses 'colors' in between black and transparent instead of black and white. I did this so I can use this as the Opacity Mask of a colored rectangle, thus rendering the image in whatever color I want using only a single graphic.
However, for the life of me, I can't get WPF to stop anti-aliasing the da*n image!!
I've set 'SnapesToDevicePixels' on the rectangle to which the brush is applied... I've set the ImageBrush's Scale to 'None'... I've set its ViewPort and the ViewBox to absolute units and sized them exactly to the source image. But no matter what I try, WPF still insists on trying to smooth things out! This is VERY frustrating!!!
So... anyone know how to use an image as an opacity mask but not lose the pixel-precise drawing that we have done? I just want WPF to render the damn thing as we drew it, period!
I have tried to reproduce your problem. Simply like this:
<Rectangle Width="200" Height="200" Fill="Red">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="/mask.png"/>
</Rectangle.OpacityMask>
</Rectangle>
mask.png contains a simple diagonal mask, like that half of rectange is visible and other half is 100% transparent.
And recrangle is rendering pixel perfect (and aliased, as you want).
I think, that you may a DPI setting, that is not native to your monitor, and WPF just can`t render images correctly.
GOT IT! It's a layout issue that for some reason, there's no easy way to change. However, there's a value you can set called UseLayoutRounding that fixes it. I just set it at the root level (for this fauxample, a grid...)
<Grid UseLayoutRounding="True">
....
</Grid>
...and BAM! Works like a charm! "Sort of" like a 'SnapsToDevicePixels' but for positioning of elements (i.e. it rounds all layout-related values like left, width, etc. whereas SnapsToDevicePixels snaps the layout to the on-screen pixels when rendering.)
M
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.
I have some images (32x32 .png) that I want to display in my Windows Phone 7 application. Right, now, I am able to scale them, etc... I was wondering if there was a way to scale them without any smoothing algorithms applied (so when I double the image size, it creates a blocky image look).
Right now in my XAML I have the following:
<Image Height="64" Width="64" Margin="12,0,9,0" Name="{Binding itemName}"
Stretch="Uniform" VerticalAlignment="Center" Source="{Binding imageName}" />
Where imageName is just a path to the .png images. Is there a simple way to do this in just XAML, or should I be loading the image into a different format to play with it in the code (while keeping the transparency of the png).
Thanks in advance,
-Jeff
Theres currently no way to do this in XAML. The only way to achieve a nearest neighbor scaling without any interpolation (smoothing) is the open source library WriteableBitmapEx.
http://writeablebitmapex.codeplex.com
First load the PNG into a WriteableBitmap, then use the Resize extension method the WriteableBitmapEx provides. Use the NearestNeighbor as value for the last parameter of the method. Then assign the return value of the Resize method to the Source property of your Image control.
You can wrap this functionality in an IValueConverter implementation so you can use it in XAML.