Partial scaling of a composed elements in WPF - c#

I am in the following situation: designing an interactive flow-chart GUI. I stuck with animating a scale down animation of the flow-chart. Composed elements of the flow-chart are minimized, but they keep being active.
I have something like this:
<Canvas Canvas.Left="55" Canvas.Top="720" Height="100" Width="500" Tag="stepDown">
<Line Stroke="#99CCFF" StrokeThickness="8" X1="0" X2="720" Y1="10" Y2="10">
<Polygon Stroke="Black" StrokeThickness="2" Points="0,30 40,0 40,60" Canvas.Left="-20" Canvas.Top="-20" Fill="#99CCFF"></Polygon>
<Polygon Stroke="Black" StrokeThickness="2" Points="0,0 0,60 40,30" Canvas.Left="720" Canvas.Top="-20" Fill="#99CCFF"></Polygon>
<Image Canvas.Left="-50" Canvas.Top="-70" Height="53" Name="image32" Source="img/outlet.png" Stretch="Fill" Width="30" Tag="relative" />
</Canvas>
And I would like to shift the whole canvas and its elements to the left - no problem with Translate Transform. Furthermore I would like to scale down only the Line, no problem with Scale Transform.
But (!) at the same time, I want that the Polygons stick to the two line endings of the line. When scaling down only the line, the Polygons, at least one, floats away.
I don't know how to dock these elements or define them at a relative basis. It works fine with a scale down on the whole Canvas, but this changes the Polygon and Images as well.

In order to me you have to define hanchors points, as attached properties. Even define a behavior that track these points accordingly to poligon placements. Then you can bind these property to the line start/end point. In this way line should stay gripped to the polygons. But I did'nt try, is just a design idea.

Related

Only a certain Pattern of Geometry data is being rendered on Canvas in WPF application

I am trying to Display some Geometry Resources in a Canvas
I have Placed the required data inside a canvas and I noticed that only a certain type of data is being rendered in the View (path with negative values do not get rendered) .
Any Idea why I might be facing this problem and any suggestions ?
A Canvas does not resize its child elements, hence the coordinates of your Paths are used as-is. Obviously the coordinates of the FolderIcon Path are far outside any visible region of your application.
Instead of using a Canvas, you may want to directly use the Path elements as resources. Then also make them stretch automatically by setting Stretch="Uniform", and take care that they can be re-used multiple times by setting x:Shared="False".
<Path x:Key="FileIcon" x:Shared="False" Width="12" Height="12"
Stretch="Uniform" Stroke="Black" x:Shared="False" Fill="White" Data="..."/>
<Path x:Key="FolderIcon" x:Shared="False" Width="12" Height="12"
Stretch="Uniform" Stroke="Black" Fill="White" Data="..."/>

WPF XAML clipping ellipse on canvas when using TranslateTransform

When I run this code in WPF it gives me 1/4 of a circle. When removing the ClipToBounds tag, i get my whole circle.
1. Why is it clipping before rendering?
2. How to i fix that, while keeping clipping?
<Canvas ClipToBounds="True">
<Ellipse Canvas.Left="-10"
Canvas.Top="-10"
Width="20"
Height="20"
Fill="LightSeaGreen"/>
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="4.8"
ScaleY="4.8"
CenterX="0"
CenterY="0"/>
<TranslateTransform X="48"
Y="48"/>
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
Why is it clipping before rendering?
It's not.
As you can see from here:
Your Ellipse is rendered perfectly. The ClipToBounds="True" is what ruins is as you can see from your Canvas:
How to i fix that, while keeping clipping?
This is quite a broad question. Your problem comes from the fact you are putting your Ellipse outside the Canvas (Canvas.Left="-10" Canvas.Top="-10") and then you clip it. Explain what is your goal and I can try to help you out.
This behaviour is by design. The authors of the textbook (Computer Graphics: Principles and Practice - Third Edition) have confusingly introduced ClipToBounds in a way that makes it seem like it is a part of the examples that follow in the book. In fact, they are not using ClipToBounds="True". You can verify that by downloading their lab package from http://sklardevelopment.com/graftext/ChapWPF2D/ .
To illustrate, here is the actual source code for one of their examples:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="ClockCanvas" >
<Ellipse
Width="20.0" Height="20.0"
Canvas.Left="-10.0" Canvas.Top="-10.0"
Fill="lightgray"/>
</Canvas>
Note that there is no ClipToBounds="True" on the canvas.

How do I get a ScrollViewer with a Rectangle inside to stop scrolling when it reaches the end of the rectangle?

I have created a Rectangle inside of a ScrollViewer like this
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Center" Width="728" Canvas.Top="20" d:LayoutOverrides="HorizontalMargin" >
<Rectangle x:Name="musicBG" Fill="#FF0692FD"/>
</ScrollViewer>
During the use of the app, the size of MusicBg changes, sometimes to something around 3,000 pixels width.
musicBG.Width = _songLength*PixelsPerSecond
However, while scrolling the scrollViewer, it allows me to scroll the rectangle all the way off the screen.
For example this line of code gives me the following values when I have moved the rectangle as far as I want to move it.
if (songScrollViewer.HorizontalOffset > songScrollViewer.ScrollableWidth)
HorizontalOffset has a value of ~1200 and ScrollableWidth has a value of about ~2900.
How can I get this to be done properly so that the rectangle is not scrolled completely off the screen?
I would expect a HorizontalOffset of about 1200 to only push the rectangle about halfway through to it's destination, and not make it start going off screen.
ANSWER:
After much frustration, I was able to solve this problem by using Canvas instead of Border or Rectangle.
I'll award points if anyone can explain why this problem happened, and if there is a less processor intensive control that would work better than canvas.
Edit: Screen shots:
Bad Code:
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" Width="720" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Top" Canvas.Top="20" HorizontalAlignment="Left" >
<Border x:Name="musicBG" Background="#FF0692FD" VerticalAlignment="Top" HorizontalAlignment="Left" Height="270" />
</ScrollViewer>
Image of bad scroll with bad code:
Good working code:
<ScrollViewer ManipulationMode="Control" x:Name="songScrollViewer" Width="720" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Height="270" VerticalAlignment="Top" Canvas.Top="20" HorizontalAlignment="Left" >
<Canvas x:Name="musicBG" Background ="#FF0692FD" Height="270" >
<Border Background="#FF0692FD" VerticalAlignment="Top" HorizontalAlignment="Left" Height="270" />
</Canvas>
</ScrollViewer>
Good Scroll: Notice it says 170 seconds on the bottom right instead of the smaller number of 118 seconds in the bad scroll.
I believe your right, wp7 won't render shapes that are bigger then 2048 pixels. So the reason it's scrolling of the page is because it's treating it as if it were bigger then 2048 but you can only see up to a width of 2048px and its just scrolling over to the "ghost" part of the rectangle.
I'm not sure if you can override this but the best solution I could come up with (without overriding) is by splitting up your rectangle into chucks that are smaller then 2000 (just to be safe) and then displaying them seamlessly in a horizontal stack panel inside the scroll viewer. The problem with this is that depending on how you've coded it, this solution might be hard to implement; but you might just be able to split it in your ViewModel when displaying it and your logic would only see it as one big chunk.

Completely Filling Custom Geometries

I'm trying to figure out how to completely fill a custom geometry. It seems like it should be a common question, but I haven't really been able to find any solutions.
I have the following example geometry:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry
Figures="M112,296C112,296 136,296 136,320 M112,344C112,344 136,344 136,320 M112,296L112,296 96,296 96,344 112,344"/>
</Path.Data>
</Path>
Which produces the following result:
This is the result I would like to see:
Any Ideas? I know I could make a single arc and that would resolve this particular case, but in my application the user can draw any type of geometry so the result could be composed of any number of "primitives" (PolyLineSegments, EllipseGeometries, ArcSegments, etc) and in the case that the resultant geometry contains some type of closed area, I'd like to accurately fill that area.
EDIT:
Here is an example of what a CombinedGeometry looks like if I ensure all three adjacent geometries overlap and create a unioned CombinedGeometry with the following code:
<Path Grid.Row="2" Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<PathGeometry
Figures="M111,293C111,296 136,293 136,325"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry
Figures="M111,346C111,344 136,344 136,320"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<PathGeometry
Figures="M125,296L115,296 96,296 96,344 120,344"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
And here is the result:
I was hoping it would union just the strokes and automagically figure out the correct filling for the new contiguous polygon... bummer.
EDIT 2:
Hmm so I think I've come up with a couple possible solutions, neither of which are as easy as I was hoping they would be. The first option would be to combine all of the geometries as above using a CombineGeometry structure, then I called "GetFlattenedPathGeometry" on the resultant "CombineGeometry" in order to get a PathGeometry. Next I iterate over each Figure in the PathGeometry and just remove those which are holes (which I think you should be able to do by getting rid of all the figures which are entirely contained by another, or holes may follow a convention of having either clockwise or counter-clockwise coordinates, not sure..), if all goes well you should be left with a single fully filled geometry.
The second option would be to again call "GetFlattenedPathGeometry" on whatever the resultant path is, so as to get a vertex based polygonal approximation of the path (without all the curve, arc, ellipse, etc notation, we want a path containing only points and lines). After that, you would just combine all the resultant figures/segments into a single figure whose segments are ordered either clockwise or counter-clockwise.
I've tested both approaches and they seem to work with at least the simple test case outlined above, though they will not work with more complex shapes (self-intersecting, concave, etc).. support which I require.. so the question remains, how do I do this??
EDIT 3:
Here is a more complicated geometry in which ordering/combining becomes more difficult:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry
Figures="M104,160C104,160 72,160 72,136
M104,128C104,128 72,128 72,152
M152,232L152,232 152,216 120,216 120,160 128,160
M152,232L152,232 72,232 104,216 104,160 96,160
M104,128L104,128 168,128
M128,160L128,160 168,160
M165,160L168,160 200,128
M200,160L200,160 200,128
M200,160L200,160 168,128 152,128"/>
</Path.Data>
</Path>
Which produces:
Your example geometry is a combination of three adjacent shapes which do not overlap. The stroke is drawn on the outside edges simply because the shapes are not closed and the inner stroke lines do not exist. Although it may appear that the shapes are being merged and the stroke is applied to the geometry as a whole, that is not what is happening.
Closing the hole becomes a more complex problem of programmatically detecting the hole and closing it with the appropriate shape or by creating a new combined shape which doesn't have the hole (possibly by detecting and tracing the outer points). I am not aware of any functionality in WPF which can help you with this task. There does exist a CombinedGeometry class which can produce a union of two overlapping shapes. The shapes in this example are not overlapping.
Without context, it is hard to recommend a solution. If this is a free form drawing program, perhaps the user simply has to draw another shape in the middle to close the geometry.
Treat it as a "connect the points" problem :)
You have 5 points:
96,296 - top left point (corner)
112,296 - top - start of bezier curve
136,320 - far right - end of first bezier curve, start of second one
112,344 - bottom - end of second bezier
96,344 - bottom left point (corner)
And now, we shall connect them.
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="M112,296 C112,296 136,296 136,320 C136,320 136,344 112,344 M112,344 96,344 96,296 112,296"/>
</Path.Data>
</Path>
You can also use <GeometryGroup FillRule="Nonzero" ...> to fill your custom path from 1st post. Default is FillRule="EvenOdd", which produces filling like yours.*
See Path Markup Syntax (http://msdn.microsoft.com/en-us/library/ms752293.aspx) for more information on path mini-language.
Changed <Path> to <GeometryGroup>
EDIT 1:
I reordered your path a bit:
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="2">
<Path.Data>
<PathGeometry Figures="M72,152 C72,152 72,128 104,128 L168,128 200,160 200,128 168,160 120,160 120,216 152,216 152,232 72,232 104,216 104,160 C104,160 72,160 72,136"/>
</Path.Data>
</Path>
Which gives us:
New shape
Drawing path is like using some kind of "draw shape" tool in vector editing software, where you have to select next points, and when you don't have next one - shape is automatically closed (last point is connected to first one, but just for the purpose of filling shape - we can see how it's done, when we look at the 'nose' of new shape).
In your example You have 9 separate shapes (each one in separate line, where M specifies start of new figure), and they are all filled in the said-above-way.
Ofcourse, you can use GeometryGroup (with FillRule) or CombinedGeometry (with CombinedGeometryMode) to connect shapes.
I found an interesting article about creating shapes with wpf using a svg-file created with a graphic-tool like inkscape. This might give you some insight.

Alternative to absolute positioning of shapes' points

I was wondering if there was a way in WPF to create a shape (ex.: polygon, line, ...) but instead of using absolute positioning for the points of the shape, we could use something like percentage.
For example, instead of having a line with absolute positioning like this: (X1=0,Y1=50,X2=100,Y2=50), we could have a line with percentage values (0 to 1) like this (X1=0,Y1=0.5, X2=1, Y2=0.5 where 1 is equivalent to the size of the parent). So no matter what is the size of the parent of the shape, the shape would always be proportional to its parent.
That could be done with dependency properties, but I would find it much cleaner if there was a way to do it with something like I described. I hope I didn't miss something really obvious that does exactly that...
Thanks!
You could achieve a similar effect if you scale it by applying a scale transform on your geometry the size of the control.
<Path Width="100" Height="100" Stroke="Red">
<Path.Data>
<LineGeometry StartPoint="0 0" EndPoint="1 1">
<LineGeometry.Transform>
<ScaleTransform ScaleX="{Binding Path=Width, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}"
ScaleY="{Binding Path=Height, RelativeSource={RelativeSource FindAncestor, AncestorType=Path}}" />
</LineGeometry.Transform>
</LineGeometry>
</Path.Data>
</Path>
This should draw a red line with absolute points (0, 0) to (100, 100).

Categories

Resources