I am making silverlight program where user can add worms to ground. Worms has to modifable. They can have three or more controlpoints where from user can move and edit worm. Worm has to have maximum and minimum length. Also it has to somekind outfit that it looks like worm. I have made pretty good worm with canonical spline but problem is max and minimum lengths and outfit. i am not waiting any ready codes for this but some new ideas how it can be done.
Example made by MSPaint :) http://tinypic.com/r/bgxp3m/7
You can use the Pen tool in Expression Blend to create an open path with bezier curves. Then the Direct Selection tool can modify the points to give you the desired shape. If you do this in a Storyboard, Expression Blend will expand the Path data into uncompressed format, which can be animated, named, and referenced from code behind. Here is a sample worm:
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<Storyboard x:Name="wriggle">
<PointAnimation Duration="0:0:0.7" To="59.4217224121094,11.1413049697876" Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.Segments)[1].(BezierSegment.Point1)" Storyboard.TargetName="wormBody" d:IsOptimized="True"/>
<PointAnimation Duration="0:0:0.7" To="21.5,45.9078826904297" Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.Segments)[0].(BezierSegment.Point2)" Storyboard.TargetName="wormBody" d:IsOptimized="True"/>
<PointAnimation Duration="0:0:0.7" To="9.25,28.6580047607422" Storyboard.TargetProperty="(Path.Data).(PathGeometry.Figures)[0].(PathFigure.Segments)[0].(BezierSegment.Point1)" Storyboard.TargetName="wormBody" d:IsOptimized="True"/>
</Storyboard>
</Grid.Resources>
<Path x:Name="wormBody" HorizontalAlignment="Left" Height="67.316" Margin="167,150.092,0,0" Stroke="Black" StrokeThickness="10" UseLayoutRounding="False" VerticalAlignment="Top" Width="91">
<Path.Data>
<PathGeometry FillRule="EvenOdd">
<PathFigure IsFilled="True" IsClosed="False" StartPoint="5,62.3157653808594">
<BezierSegment Point3="42.2093734741211,24.9581699371338" Point2="4.49375009536743,30.3548755645752" Point1="35.375,53.3601760864258"/>
<BezierSegment Point3="86,5" Point2="73.8499984741211,24.9581699371338" Point1="54.1062507629395,22.6553039550781"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Ellipse HorizontalAlignment="Left" Height="15" Margin="246,146,0,0" Stroke="Black" StrokeThickness="10" VerticalAlignment="Top" Width="15"/>
</Grid>
Related
I am somewhat confused about how relative coordinates work in WPF, especially in scenarios with DrawingBrushes.
Let's say I want to paint the background of a square area, which is flexible in it's size. I want to paint the background with a special "shape", let's say a kind of "T" laying on the side, with the vertical stroke going through the middle of the area.
Using relative coordinates (the size of the area is flexible), I came up with the following XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="722" Width="722" UseLayoutRounding="True">
<Window.Resources>
<DrawingBrush x:Key="EdgeGrid">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<!-- draw a single T laying on the side -->
<GeometryGroup>
<!-- top to bottom -->
<LineGeometry StartPoint="0.5,0.0" EndPoint="0.5,1"/>
<!-- left to right -->
<LineGeometry StartPoint="0.5,0.5" EndPoint="1,0.5"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="0.01" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Window.Resources>
<Grid Name="LayoutRoot">
<Rectangle Width="400" Height="400" Stroke="Black" StrokeThickness="1" Fill="{StaticResource EdgeGrid}">
</Rectangle>
</Grid>
But the result I get looks like this:
(source: bilder-hochladen.net)
Shouldn't the vertical stroke go right through the middle (X coordinate is 0.5)?
And also how can I set the pen thickness to be 1 or 2 pixels in relative mode?
Any ideas?
You'll have to set the ViewboxUnits property of the DrawingBrush to Absolute (instead of the default RelativeToBoundingBox). The Viewbox would still be (0,0,1,1).
See the TileBrush Overview article on MSDN for details about a brush's viewbox and viewport.
<DrawingBrush x:Key="EdgeGrid" ViewboxUnits="Absolute">
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<LineGeometry StartPoint="0.5,0.0" EndPoint="0.5,1"/>
<LineGeometry StartPoint="0.5,0.5" EndPoint="1,0.5"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
<GeometryDrawing.Pen>
<Pen Thickness="0.01" Brush="Black" />
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
Of course this won't let you define stroke widths in pixels. Making the drawing in absolute coordinates and then putting the whole thing in a Viewbox won't also help much, because strokes would still be scaled.
In order to get a scalable drawing with fixed stroke width, you would have to use a Path element, where you set the StrokeThickness and Data properties and assign a ScaleTransform to the Transform property of the Geometry used as Data.
In your special case of drawing a centered, T-shaped figure with fixed stroke width, you may simply draw two (very long) Lines in a Canvas, where the coordinate origin is centered by putting the Canvas in a 2x2 Grid. You may even choose to have different strokes and stroke widths for the two Lines.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Canvas Grid.Column="1" Grid.Row="1">
<Line Y1="-10000" Y2="10000" Stroke="Black" StrokeThickness="1"/>
<Line X2="10000" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Grid>
To get vertical stroke right you need to do it like this:
<GeometryGroup>
<!-- top to bottom -->
<LineGeometry StartPoint="0.75,0.0"
EndPoint="0.75,1" />
<!-- left to right -->
<LineGeometry StartPoint="0.5,0.5"
EndPoint="1,0.5" />
</GeometryGroup>
But that won't help you with pen thickness. In general, if you want to scale a geometry - first create it using absolute coordinates you like (say in 0-100 range), then put that into ViewBox or use ScaleTransform, like this:
<Viewbox Width="400"
Height="400">
<Path Stroke="Black"
StrokeThickness="1">
<Path.Data>
<GeometryGroup>
<!-- top to bottom -->
<LineGeometry StartPoint="75,0"
EndPoint="75, 100" />
<!-- left to right -->
<LineGeometry StartPoint="50,50"
EndPoint="100, 50" />
</GeometryGroup>
</Path.Data>
</Path>
</Viewbox>
Let's see how the proposed solution would look like.
Let's assume we want to show the shapes in a kind of grid and draw various shapes depending on the data (by selecting an appropriate DateTemplate). For simplicity, in this example, let's draw only one kind of shape (a cross, as in my initial question):
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="722" Width="722" UseLayoutRounding="True">
<ItemsControl ItemsSource="{Binding data}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="10" Rows="10">
</UniformGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="Cell">
<Path StrokeThickness="2" Stroke="Blue" SnapsToDevicePixels="True">
<Path.Data>
<GeometryGroup>
<GeometryGroup.Transform>
<ScaleTransform ScaleX="{Binding ElementName=Cell, Path=ActualWidth}" ScaleY="{Binding ElementName=Cell, Path=ActualHeight}"/>
</GeometryGroup.Transform>
<!-- top to bottom -->
<LineGeometry StartPoint="0.5,0.0" EndPoint="0.5,1"/>
</GeometryGroup>
</Path.Data>
</Path>
<Path StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True">
<Path.Data>
<GeometryGroup>
<GeometryGroup.Transform>
<ScaleTransform ScaleX="{Binding ElementName=Cell, Path=ActualWidth}" ScaleY="{Binding ElementName=Cell, Path=ActualHeight}"/>
</GeometryGroup.Transform>
<!-- left to right -->
<LineGeometry StartPoint="0,0.5" EndPoint="1,0.5"/>
</GeometryGroup>
</Path.Data>
</Path>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
#Clemens Is this the solution you had in mind? Is this the correct way of doing it?
The only problem I have with the result is that the lines are blurry and there is even a break to be seen in the horizontal line. Is there any solution for this?
I have an custom UserControl that displays an image, i'm trying to use this user control
in the MainWindow and for some reason the image dosen't fill it's content.
And i can't figure out why.
This is the custom UserControl :
<UserControl x:Class="BookWriter3D.Sheet"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
FlowDirection="RightToLeft">
<Canvas RenderTransformOrigin="0.0,0.0">
<Canvas.RenderTransform>
<RotateTransform x:Name="Angle"/>
</Canvas.RenderTransform>
<Canvas x:Name="LayoutRoot">
<Canvas.Clip>
<RectangleGeometry x:Name="imgRect" Rect="0,0,1500,800"/>
</Canvas.Clip>
<Image x:Name="sheetImage" Source="C:\Users\Eric\Documents\Papa\BookWriter3D\Resources\Images\Page0.jpg" Stretch="Fill"/>
<Image x:Name="shadowSpine" Source="C:\Users\Eric\Documents\Papa\BookWriter3D\Resources\Images\shadowspine.png" Canvas.Left="899" Canvas.Top="0"/>
<Image x:Name="curlShadow" Opacity="0.0" Source="C:\Users\Eric\Documents\Papa\BookWriter3D\Resources\Images\curlshadow.png" Canvas.Top="0" Stretch="Fill"
RenderTransformOrigin="1.0,0.66">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="curlShadowScale" ScaleX="1.0" ScaleY="2.0"/>
<RotateTransform x:Name="curlShadowRotate" Angle="0"/>
<TranslateTransform x:Name="curlShadowTranslate" X="0" Y="0"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Canvas>
</Canvas>
And this is the MainWindow.xaml :
<Canvas x:Name="layoutRoot" Background="White" RenderTransformOrigin=".5,.5">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"/>
</Canvas.RenderTransform>
<local:Sheet x:Name="Page1Sheet"></local:Sheet>
<Canvas x:Name="traca" >
<Canvas x:Name="catrabs" Canvas.Top="0">
<Canvas.Clip >
<PathGeometry x:Name="baclpth" >
<PathGeometry.Figures >
<PathFigure IsClosed="True" x:Name="baclpz" StartPoint="0,0">
<PathFigure.Segments>
<LineSegment x:Name="baclzz" Point="400,0"/>
<LineSegment x:Name="baclzyy" Point="400,400"/>
<LineSegment x:Name="baclzyrq" Point="0,300" />
</PathFigure.Segments>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Canvas.Clip>
<local:Sheet x:Name="Page1TraceSheet" X="0" Y="625"/>
</Canvas>
This code continues, though i doubt it has any influense on this problem.
Any help fellas?
EDIT :
After a bit of debugging i found out that the outerWrapper Canvas fills the screen but the LayoutRoot dosen't fill it's parent but it's ( i guess) same size as image, so problem is different then i first susspected
EDIT 2 :
Problem solved.
As Canvas changes size according to it's content,
All i needed to do is set the width/height of the Image, and it worked perfectly.
A Canvas never resizes its children. Consider using a Grid instead, if you want the Images to automatically fill a certain area. Try this
<Grid>
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg" />
</Grid>
versus this
<Canvas>
<Image Source="C:\Users\Public\Pictures\Sample Pictures\Desert.jpg" />
</Canvas>
to get a feeling for the difference. And try different settings for Image.Stretch on an Image in a Canvas. They'll be ignored.
Problem solved. As Canvas changes size according to it's content, All i needed to do is set the width/height of the Image, and it worked perfectly.
<Image x:Name="sheetImage" Source="C:\Users\Eric\Documents\Papa\BookWriter3D\Resources\Images\Page0.jpg" Width="1280" Height="800" Stretch="Fill" />
Canvas does not alter or constrain other control's size or position. Maybe a Grid is what you need.
I am developing a small silverlight application (using siverlight 4 and c#). In my application I need to draw coordinates based on their X,Y. Then, I need to draw lines between some of the points, based on the connections between them. Since there can be several lines, and I cannot have them all intersecting each other (as it will turn this into a mess), I need to draw some of my lines with an arch.
So, What would be the best way to approach this issue?
Create my own x,y system - position elements in points and draw lines - If so How can I draw a line with an arch?
Use a ready control that provides similar capabilities? If so, what control?
Thank You!
Attached is a small image to illustrate my need (I am no big painter, sorry).
Take a look at drawing Bezier curves (MSDN Link) and learn about the different geometry types (MSDN Link)
Below is a code sample to get you started that will produce the following image:
<Canvas x:Name="LayoutRoot" Background="White">
<Path Stroke="Blue" StrokeThickness="2" >
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure StartPoint="50,50">
<PathFigure.Segments>
<PathSegmentCollection>
<BezierSegment
Point1="50,20"
Point2="120,170"
Point3="350,150"
/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
<Path Fill="Gold" Stroke="Black" StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="50,50" RadiusX="20" RadiusY="20" />
</Path.Data>
</Path>
<Path Fill="Gold" Stroke="Black" StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="350,150" RadiusX="20" RadiusY="20" />
</Path.Data>
</Path>
</Canvas>
I am trying to create a red circle with a black x through it using XAML.
My problem is that they aren't aligned correctly.
What is the right way to do this?
This is what I've got so far:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image>
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="Red">
<GeometryDrawing.Pen>
<Pen Brush="Transparent" Thickness="0"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry Center="8,8" RadiusX="8" RadiusY="8"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="Black" Thickness="2.5"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure StartPoint="4,4">
<LineSegment Point="12,12"/>
</PathFigure>
<PathFigure StartPoint="4,12">
<LineSegment Point="12,4"/>
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
</Grid>
Simply putting an ellipse in the same grid with a black X the X isn't quite centered on the ellipse because the coordinates of each line you draw are really coordinates within the space allotted for it.
I think they needed to be in some sort of geometry or drawing aggregate to give them the same coordinate system. The geometry group and path are aggregators but both require their contents to have the same fill and stroke and the stroke and fill is different for the red circle (no stroke) and the black X (no fill).
The only aggregator that gives common coordinate systems and allows different fills & strokes for its members that I could find was the DrawingGroup.
The string shortcuts that work for creating a Path via its Data property don't appear to work for creating a PathGeometry so all had to be filled in by hand.
OK, so three hundred ways to skin a cat. Without fully understanding your use case I just came up with the fastest way to draw what you requested.
<Grid HorizontalAlignment="Left"
Height="80"
Margin="80,80,0,0"
VerticalAlignment="Top"
Width="80">
<Ellipse Fill="Red"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Path Data="M40,53 L48,69 62,69 49,46 61,24 48,24 C48,24 40,39 40,39 40,39 32,24 32,24 L18,24 30,46 17,69 31,69 z"
Fill="Black"
Margin="15"
Stretch="Fill"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Grid>
This is probably outside what exactly you're looking for, but hopefully it at least gives you another way to think about it.
I had the same issue when trying to center text within an ellipse. The problem with using something like a TextBlock is that the kerning and escapement of each character is slightly different and so while the TextBlock element itself might be technically centered within the ellipse, this does not mean that the character will be centered in the ellipse. The character always appears to be too low and to the right of center in most situations.
I have had some success by wrapping the TextBlock in a ViewBox. While I am not fully versed in the technical implementation of the ViewBox, the ViewBox appears to wrap the visual rendering of the content which allows me to center that rendering more easily than trying to center to layout elements together.
I also seem to have better luck using an outer element that is of odd width/height rather than even width and height.
<Grid Width="19"
Height="19">
<Ellipse Fill="#FFB1413F"
StrokeThickness="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Viewbox HorizontalAlignment="Center"
VerticalAlignment="Stretch">
<TextBlock Text="X"
Margin="1"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Viewbox>
</Grid>
I need to create a repeat symbol in XAML / WPF (procedural code would be ok too, though I'd prefer XAML if possible), something like the following, but I just need the not finished circle with the arrow (the white drawing in the button):
http://www.vista-style-icons.com/libs/phone/repeat.htm
I know how to create a circle in XAML, but I don't know how to create a not finished circle and add an arrow to the open end?
Thank you for any help!
You can create an unfinished circle by using an ArcSegment as a path segment in a Path shape. You specify the start and end point of the arc and the radius of the entire circle. You can render it on top of a blue circle by putting them in a grid:
<Grid Width="160" Height="160">
<Ellipse Fill="Blue"/>
<Path StrokeThickness="5" Stroke="White">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="115,45">
<ArcSegment Point="115,115" Size="50,50" IsLargeArc="True"/>
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Polygon Points="115,115 105,105 125,105 125,125" Fill="White"/>
</Grid>
You can also use the shorter Path Markup Syntax to create a StreamGeometry rather than a PathGeometry:
<Grid Width="160" Height="160">
<Ellipse Fill="Blue"/>
<Path Data="M 115,45 A 50,50 0 1 0 115,115"
StrokeThickness="5" Stroke="White"/>
<Polygon Points="115,115 105,105 125,105 125,125" Fill="White"/>
</Grid>
You'll need to work on it to get exactly the look you want, but that should give you the basic technique for drawing an unfinished circle with an arrow.