Simple (I think) Horizontal Line in WPF? - wpf

Creating a relatively simple data entry form, and just want to separate certain sections with a horizontal line (not unlike an HR tag in HTML) that stretches the full length of the form.
I have tried this:
<Line Stretch="Fill" Stroke="Black" X2="1"/>
Because the parent control is not a fixed width, this line causes the window to stretch to the full width of the screen.
Is there an easy way to do this without fixing the width of my parent control/window?

How about add this to your xaml:
<Separator/>

I had the same issue and eventually chose to use a Rectangle element:
<Rectangle HorizontalAlignment="Stretch" Fill="Blue" Height="4"/>
In my opinion it's somewhat easier to modify/shape than a separator.
Of course the Separator is a very easy and neat solution for simple separations :)

Use a Border of height 1 and don't set the Width (i.e. Width = Auto, HorizontalAlignment = Stretch, the default)

For anyone else struggling with this: Qwertie's comment worked well for me.
<Border Width="1" Margin="2" Background="#8888"/>
This creates a vertical seperator which you can talior to suit your needs.

To draw Horizontal
************************
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="DarkCyan" Height="4"/>
To draw vertical
*******************
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Center" Fill="DarkCyan" Height="4" Width="Auto" >
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="90"/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>

1st, the op asked for a horizontal line. seperators are vertical (not sure how that got so many up votes---they're not horizontal)
Play around using a simple line tag
<Line
Grid.Row="4"
HorizontalAlignment="Center"
StrokeThickness="1"
X1="0"
X2="300"
Y1="10"
Y2="10" />
That's a horizontal line. Play around with the X & Y axis to make it work...simple enough to make a horizontal, vertical or any other angled line segment that doesn't resize a window

Related

Creating two rectangles on top of each other that resize proportionally

EDIT: I made my example screen grabs more illustrative. They were conflicting with the text description.
I am a complete WPF newbie.
I want to have a thin rectangle with a dividing line that will resize as I expand and contract the window. Since I need to color the rectangle differently above and below the line I thought to do this with one rectangle overlaid on top of the other.
I can get a single rectangle that fills up the xaml control top to bottom ( called tallRectangle ) to resize properly with the window, but I can't figure out how to extend that behavior to the second rectangle:
<Grid>
<Rectangle x:Name="tallRectangle" Fill="#FFF4F4F5" HorizontalAlignment="Left" Margin="23,0,0,0" Stroke="Black" Width="56"/>
<Rectangle x:Name="halfRectangle" Fill="#FF757576" HorizontalAlignment="Left" Margin="84,136,0,0" Stroke="Black" Width="53" StrokeThickness="0" Stretch="Fill" RenderTransformOrigin="0.5,0.5">
<Rectangle.LayoutTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.LayoutTransform>
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
I wish the second one, halfRectangle, to be half the size of the first, but to also scale larger and smaller if the window is resized. But I don't know how to make it do that.
Here is how it starts out:
When I make the window smaller, the top of the second rectangle stays static:
I would like the second rectangle to resize itself proportionally as well, like the orange outline in this:
This is only a very first step to what I'd like to have eventually, but I hope once I understand how to get that behavior in the second rectangle I'd be able to figure out the remainder of it.
Eventually I'd need to set that second rectangle to be a percentage of the height of the first, and I'd also need to add a variable number of hash marks to the first rectangle dynamically, at variable positions. It seems that once I understand how to get that second rectangle resizing, I'd be able to figure out the hash marks too.
EDIT: Once the grid structure is in place as explained in the answer to this question, the code behind can be modified to dynamically set the ratio, for example:
Dim realDepth = 8000
Dim divideDepth = 2600
Dim realScreenHeight = topRectangle.Height.Value + bottomRectangle.Height.Value
Dim realHeightToScreenRatio = realScreenHeight / realDepth
Dim divideScreenPoint = divideDepth * realHeightToScreenRatio
topRectangle.Height = New GridLength(divideScreenPoint, GridUnitType.Star)
bottomRectangle.Height = New GridLength(realScreenHeight - topRectangle.Height.Value, GridUnitType.Star)
The magic sprinkles is the second parameter to GridLength in order to preserve the dynamic resizing of the row's height.
If you are already using a Grid, why not define two equally sized rows:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Grid.RowSpan="2" Fill="#FFF4F4F5" Stroke="Black" Width="56"/>
<Rectangle Grid.Row="1" Fill="#FF757576" Stroke="Black" Width="56"/>
</Grid>
If you need to set the ratio of the row heights at runtime, you can simply assign a name to each row
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="row1"/>
<RowDefinition x:Name="row2"/>
</Grid.RowDefinitions>
<Rectangle Grid.RowSpan="2" Fill="#FFF4F4F5" Stroke="Black" Width="56"/>
<Rectangle Grid.Column="1" Grid.Row="1" Fill="#FF757576" Stroke="Black" Width="56"/>
</Grid>
and change their relative heights like this:
row1.Height = new GridLength(2, GridUnitType.Star); // 2/5
row2.Height = new GridLength(3, GridUnitType.Star); // 3/5

WPF usercontrol combine ellipse and arrow

I want to create a custom usercontrol to represent a player in a 2D map.
I had an ellipse to represent the player but I want to have on the border of the ellipse an arrow to indicate where the player is looking.
This is what I tried :
<Ellipse Width="17" Height="17" Stroke="Black" Fill="White" HorizontalAlignment="Center" />
<Path Data="M5,0 0,5 5,10" Fill="White" Stroke="Black" VerticalAlignment="Center" >
<Path.LayoutTransform>
<RotateTransform Angle="10"/>
</Path.LayoutTransform>
</Path>
the result :
That looks like what I want (it's not properly aligned but that's not the point here).
The problems are :
I know the position of the ellipse's center without the arrow
When the arrow will be on the right the relative position of the ellipse's center will be different --> I could solve this problem using a square control
My Circle has a textblock on top (Horitonzal + vertical center) to
display its id
How to move the arrow depending on the position looked ? I thought the easier might be to calculate an angle and rotate the whole control.
My first idea was to draw using any vector drawing software (illustrator for instance) the path, and get the coordinates of the path, and paste them in WPF.
then just rotate the usercontrol.
But doing this will also rotate the text and I don't want the text to rotate.
I'm stuck on this one, I hope my problem is enough described to be understood.
EDIT My first try :
<ControlTemplate TargetType="ContentControl">
<Grid Width="34" Height="34">
<Path x:Name="contour_forme"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="1"
Stretch="Uniform"
Width="28"
Height="22"
HorizontalAlignment="Left"
Fill="{TemplateBinding Background}"
Data="M28.857,53.500 C24.537,53.487 20.477,52.380 16.938,50.443 C16.938,50.443 16.938,50.500 16.938,50.500 C16.938,50.500 16.785,50.350 16.785,50.350 C12.845,48.157 9.579,44.924 7.317,41.032 C7.317,41.032 -6.176,27.755 -6.176,27.755 C-6.176,27.755 8.206,14.530 8.206,14.530 C10.380,11.316 13.289,8.649 16.681,6.736 C16.681,6.736 16.938,6.500 16.938,6.500 C16.938,6.500 16.938,6.581 16.938,6.581 C20.525,4.615 24.641,3.496 29.021,3.509 C42.835,3.551 53.996,14.775 53.951,28.580 C53.906,42.385 42.670,53.542 28.857,53.500 ZM29.004,8.507 C17.953,8.474 8.965,17.400 8.929,28.443 C8.893,39.487 17.822,48.467 28.873,48.500 C39.924,48.533 48.912,39.608 48.948,28.564 C48.985,17.520 40.056,8.540 29.004,8.507 Z"
>
<Path.LayoutTransform>
<RotateTransform Angle="0" />
</Path.LayoutTransform>
</Path>
<TextBlock Style="{DynamicResource StyleTextes}" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="5"
/>
</Grid>
</ControlTemplate>
With the result :
As you can see I didn't manage to center the text inside my 22px circle.
My arrow is about 6 px height so I've created a control of 22 (circle's size expected) + 2 * 6px depending on the arrow position.
But when I try to rotate my path doing :
<Path.LayoutTransform> <RotateTransform Angle="90" />
</Path.LayoutTransform>
I have the following result :
I'm not sure on how I can keep the circle of my path in the center of the control when I rotate the path.
Just apply the RotateTransform to the "image" but not to the text.
Also I would use a render transform instead of a layout transform.
<Canvas Canvas.Left="206.333" Canvas.Top="119" Height="80" Width="80">
<Path Data="M244,99.333333 L210.16667,109.50034 244.83334,125.50034" Fill="#FFF4F4F5" Stretch="Fill" Stroke="Black" RenderTransformOrigin="0.5,0.5" Height="60" Canvas.Left="3" Canvas.Top="5" Width="60">
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="70"/>
<TranslateTransform/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<TextBlock TextWrapping="Wrap" Text="TextBlock" Height="20" Width="80" Canvas.Top="30" TextAlignment="Center"/>
</Canvas>

WPF / Xaml, vertical aligment of LineGeomtry inside grid not working properly

I try to get into creation of custom controls with for WPF. I found many good
tutorials and advises on the web so I started width a really simple example to get
my hands dirty and get some practice. I figured out that the issue stumbled across
is not really related to the subject of custom controls. So I extracted the xaml code to a simple wpf form.
<Window x:Class="WpfVerticalAigmentTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="200">
<Grid>
<Grid Height="40" Background="LightCyan" VerticalAlignment="Center">
<Path Stroke="Red"
StrokeThickness="20" VerticalAlignment="Center" >
<Path.Data>
<LineGeometry StartPoint="0,0" EndPoint="100,0"></LineGeometry>
</Path.Data>
</Path>
</Grid>
</Grid>
My expectation was to get a line centered in the grid and claiming the half of the stroke thickness on each side from the center. But as the linked image shows differs from my expectation.
"Resulting visualization"
So it look like I missed a detail about the line shape or linegeomtry. How do I get the the line displayed as shown in the following image?
"Expected result"
You need to match the Width and Height of the LineGeometry to the Width and Height of the Path and set the VerticalAlignment property to Bottom:
<Grid Height="20" Width="200" Background="LightCyan" VerticalAlignment="Center">
<Path Stroke="Red" StrokeThickness="20" VerticalAlignment="Bottom">
<Path.Data>
<LineGeometry StartPoint="0,0" EndPoint="200,0"></LineGeometry>
</Path.Data>
</Path>
</Grid>
If your goal is your the expectaions, and not the way how u have reached this, I could prefer to you this:
<Grid>
<Grid Height="40" Background="LightCyan" VerticalAlignment="Center">
<Border BorderThickness="10" VerticalAlignment="Center" BorderBrush="Red" />
</Grid>
</Grid>
The problem here is that the starting point of the XY Coordinates of the Path starts on the top left, and the stroke expands in both directions but thereby only makes the Path bigger to the bottom (I can't really tell you why, but that's just what seems to happen).
You can see this pretty good in the Design View:
To work around this simply move your Y Coordinates down half of the stroke size.
<Grid Height="40"
VerticalAlignment="Center"
Background="LightCyan">
<Path VerticalAlignment="Center"
Stroke="Red"
StrokeThickness="20">
<Path.Data>
<LineGeometry StartPoint="0,10" EndPoint="100,10" />
</Path.Data>
</Path>
</Grid>
Or wrap it in another control (Canvas is the commonly used controls for Paths) with the desired height:
<Grid Height="40"
VerticalAlignment="Center"
Background="LightCyan">
<Canvas Height="20" VerticalAlignment="Center">
<Path Stroke="Red"
StrokeThickness="20">
<Path.Data>
<LineGeometry StartPoint="0,10" EndPoint="100,10" />
</Path.Data>
</Path>
</Canvas>
</Grid>
And you are good to go:

ScaleTransform and CenterX

I have the following code
<Canvas Width="800" Height="600">
...
<local:UpgradeLandDialog x:Name="upgradeDialog" Canvas.Left="250" Canvas.Top="200" HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0">
<local:UpgradeLandDialog.LayoutTransform>
<ScaleTransform ScaleX="0" ScaleY="0" CenterX="400" CenterY="300"/>
</local:UpgradeLandDialog.LayoutTransform>
</local:UpgradeLandDialog>
</Canvas>
In the UserControl I animate the ScaleTranform to 1. I want UserControl to "grow" from its center, but it "grows" from the upper left corner of it. The values in CenterX and CenterY do nothing. How can I make it Scale as I want?
You can use RenderTransformOrigin="0.5,0.5" on the control you want to animate.
You can change your code like this:
<Canvas Width="800" Height="600" RenderTransformOrigin="0.5,0.5">
<local:UpgradeLandDialog x:Name="upgradeDialog" Canvas.Left="250" Canvas.Top="200" HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0">
<local:UpgradeLandDialog.LayoutTransform>
<ScaleTransform ScaleX="0" ScaleY="0"/>
</local:UpgradeLandDialog.LayoutTransform>
</local:UpgradeLandDialog>
</Canvas>
Remove (CenterX="400" CenterY="300") and add (RenderTransformOrigin="0.5,0.5") to the Canvas. This way if you have a container with dynamic width and height, it can scale from the center without problem.
To make it grow from its center, you'll have to animate its margins as well (at half the rate at which you animate the width and height).
I ran into this problem not too long ago as well. I ended up repositioning the user control at every layout update to simulate a custom point based growth.
This does work for me. Did I miss something?
<Rectangle StrokeThickness="1" Stroke="Black" Width="200" Height="200">
<Rectangle.RenderTransform>
<ScaleTransform
ScaleX="{Binding ElementName=slider, Path=Value}"
ScaleY="{Binding ElementName=slider, Path=Value}"
CenterX ="100" CenterY="100"/>
</Rectangle.RenderTransform>
</Rectangle>
Even though this is an old post, I thought I'd share my findings, since it took me way too long to figure out this fairly simple solution.
Flipping the y-axis was easy, but I couldn't get CenterX and CenterY working. I really needed to be able to set the origin at any position I wanted.
Solution: nested canvasses.
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas Canvas.Left="{Binding MyOriginLeft}" Canvas.Bottom="{Binding MyOriginBottom}">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="-1"/>
</Canvas.LayoutTransform>
<!-- This now does what you expect it to do, independent of position of origin -->
<Line X1="10" Y1="20" X2="30" Y2="40" Stroke="Black" StrokeThickness="1"/>
</Canvas>
</Canvas>

How can I avoid anti aliasing with WPF?

One of the big problems with WPF is anti aliasing.
In fact, that's why UseLayoutRending was introduced in WPF 4.0.
However, it does not work for me in the following sample:
<StackPanel UseLayoutRounding="True" TextOptions.TextFormattingMode="Display" >
<Line X1="0" Y1="0" X2="200" Y2="0" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="1.5" X2="200" Y2="1.5" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="3.5" X2="200" Y2="3.5" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="7" X2="200" Y2="7" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
<Line X1="0" Y1="9" X2="200" Y2="9" StrokeThickness="1" Stroke="Black" SnapsToDevicePixels="True" UseLayoutRounding="True"></Line>
</StackPanel>
The last two lines are still blurry.
(I am using Windows 7)
Any solution?
Or is it a bug in the beta of WPF 4.0?
Floele's answer showed the right direction, but the answer was not complete. Just set the y-values to half a pixel, e.g. Y1="7" -> Y1="7.5"
That's the reason the second and third lines are not blurred.
Getting lines to look sharp in WPF can be quite hard! And some times ... it seems like it takes a bit of black magic too!
I think that floele and Kimke's answers are pointing in correct direction. That is, often times you will want to put single pixel lines on a 0.5 pixel boundary ... given the way that it draws the line (half on one side and half on another).
However, it isn't always that simple either. For example, it also depends on the surrounding xaml. For example, try this code out and resize when you do:
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center">
<Line X1="0" Y1="5" X2="200" Y2="5" StrokeThickness="1" Stroke="Black" UseLayoutRounding="True"/>
<Line X1="0" Y1="15" X2="200" Y2="15" StrokeThickness="1" Stroke="Black" UseLayoutRounding="True"/>
</Canvas>
Then, try this code out (again, resize when you do):
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True">
<Line X1="0" Y1="5" X2="200" Y2="5" StrokeThickness="1" Stroke="Black"/>
<Line X1="0" Y1="15" X2="200" Y2="15" StrokeThickness="1" Stroke="Black"/>
</Canvas>
The only difference between the two snippets is that the first uses UseLayoutRounding on the Lines while the second uses UseLayoutRounding on the Canvas container (which then also property inherit to the Lines).
However, that difference yields some interesting results. When UseLayoutRounding is used on the container the single pixel lines consistently stay spread out over 2 pixels and they don't move around. When UseLayoutRounding is used on the Lines directly, and you resize, the lines will sometimes be 1 pixel sharp ... and other times will be spead over 2 pixels.
And that brings me to the sample xaml in the original question. A few comments on it:
First off, you should realize that both UseLayoutRounding and SnapsToDevicePixels property inherit. That is, if you use it on the layout container it will inherit to the items in the layout container.
UseLayoutRounding and SnapsToDevicePixels shouldn't necessarily be used together. They can be ... but I would normally try using them separately ... either one or the other. More info here: When should I use SnapsToDevicePixels in WPF 4.0?
TextOptions.TextFormattingMode options affect text, not lines.
That StackPanel that you are using as the layout container could also affect how the lines are being laid out. Canvas allows you more precise positioning control of your lines. StackPanel will just layout one line after the other line ... and might yield some unexpected results.
More info than what the original poster was wanting. However, I personally know how tricky it is to get lines sharp in WPF. Hope this info helps someone!
The reason is apparently simpler. I only found an explanation in "Pro WPF in VB 2010" and not on MSDN though: http://books.google.de/books?id=F-gMZkAlUDUC&pg=PA334&lpg=PA334
In short, the StrokeThickness will be divided for both sides of a shape, so a StrokeThickness of 1 equals a thickness of 0.5 for each side, and since the line only has one side, it will be 0.5 units thick and thus appear blurry even when using SnapsToDevicePixels=True. So simply use "2" as StrokeThickness.
You can verify this by using a StrokeThickness of 4, the line will have a thickness of 2 then, which is more than an "accidential" single pixel deviation.
Have you tried to change the TextOptions.TextFormattingMode property to Display ? See this post from Lester Lobo for details

Resources