Custom control sizing/placement issue - wpf

I'm trying to create a custom control with C#/WPF. See below for the XAML of my control, and a window I'm trying to put it into.
The problem: When I set the HorizontalAlignment or vertical alignment properties to Center, the upper-left corner of the control is centered, but extends down and to the right. The bounding box of the control as shown in the designer is very small (zero width/heigh I think).
It seems like I have a problem with my control not reporting its size properly when the layout is doing its thing. Also, it doesn't seem to resize when Height and Width are adjusted. I have nothing in the code-behind (yet) that alters the appearance of the control (e.g. no Measure overrides).
This is my first attempt at a custom control - probably better ways of doing it (TextBlock comes to mind), but hey, this is how I learn! :D
The XAML defining my control:
<UserControl x:Class="LCD.LiquidCrystalDisplay"
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"
d:DesignWidth="300" d:DesignHeight="122">
<Canvas>
<Rectangle Fill="#FFD1D1D1" Width="300" Height="122" />
<Rectangle Fill="#FF345534" Margin="12,8,12,8" Width="276" Height="106" />
<Rectangle Fill="#FF293E29" Margin="15,11,15,11" Width="270" Height="100" />
<Line X1="32" X2="32" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="50" X2="50" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="67" X2="67" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="84" X2="84" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="101" X2="101" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="117" X2="117" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="134" X2="134" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="151" X2="151" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="168" X2="168" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="184" X2="184" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="201" X2="201" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="218" X2="218" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="235" X2="235" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="251" X2="251" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="268" X2="268" Y2="111" Y1="11" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="15" X2="285" Y1="36" Y2="36" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="15" X2="285" Y1="61" Y2="61" StrokeThickness="1" Stroke="#FF345534" />
<Line X1="15" X2="285" Y1="86" Y2="86" StrokeThickness="1" Stroke="#FF345534" />
</Canvas>
</UserControl>
And the XAML including it in a window:
<Window x:Class="TestJunk.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:LCD;assembly=LCD"
Title="MainWindow" Height="341" Width="544">
<Grid Name="MainGrid">
<my:LiquidCrystalDisplay Name="lcd" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Window>

This is the behaviour of a Canvas. Here is what MSDN has to say:
Canvas is the only panel element that has no inherent layout
characteristics. A Canvas has default Height and Width properties of
zero, unless it is the child of an element that automatically sizes
its child elements. Child elements of a Canvas are never resized, they
are just positioned at their designated coordinates. This provides
flexibility for situations in which inherent sizing constraints or
alignment are not needed or wanted. For cases in which you want child
content to be automatically resized and aligned, it is usually best to
use a Grid element.
If you want to have the UserControl resize, wrap it around a Viewbox and set a Height and Width to the Canvas.

Related

Opacity Lines In Window store app

I have problem when draw lines have opacity.
Source i writing:
<Line X1="50" Y1="50" X2="100" Y2="100" Stroke="Red" StrokeThickness="10" Opacity="0.5" StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
<Line X1="100" Y1="100" X2="150" Y2="100" Stroke="Red" StrokeThickness="10" Opacity="0.5" StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
i would like picture as
you can help me?
thanks
There are many ways to achieve what you're after, but the basis of what you need to do is essentially to group each line inside one container then reduce the opacity of that container rather than the lines themselves.
For example, if you place the Line's inside a Canvas like so:
<Canvas Opacity="0.5">
<Line X1="50" Y1="50" X2="100" Y2="100" Stroke="Red" StrokeThickness="10" StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
<Line X1="100" Y1="100" X2="150" Y2="100" Stroke="Red" StrokeThickness="10" StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
</Canvas>
You can achieve what you're after:
The Canvas is a great tool for dealing with such things, but if really needed it can be substituted with just about any container that can deal with multiple children (such as a grid).
The key is to make the container control the opacity, not the children themselves.

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:

Horizontal or vertical WPF Lines limited to 125,000 pixels?

Are horizontal or vertical WPF Lines limited to 125,000 pixels? Looking at the following code the Green line displays correctly but the Red one does not display at all despite being just 0.01 longer. Any idea why?
<Window x:Class="DCView.Window11"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window11" Height="300" Width="300">
<ScrollViewer>
<Grid Width="150000">
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green"></Line>
<Line X1="0" X2="125000.01" Y1="20" Y2="20" StrokeThickness="1" Stroke="Red"></Line>
</Grid>
</ScrollViewer>
</Window>
Andrew
This seems to be a limitation in WPF's handling of vector graphics.
To make it more complicated, try changing the StrokeThickness - if you set the StrokeThickness of your red line from 1 to 2, it displays again... until you increase the length above 250000.. Then it vanishes again.
<Grid>
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green"></Line>
<Line X1="0" X2="250000.00" Y1="20" Y2="20" StrokeThickness="2" Stroke="Red"></Line>
<Line X1="0" X2="250000.01" Y1="30" Y2="30" StrokeThickness="2" Stroke="Blue"></Line>
</Grid>
The max length goes up as you increase your stroke thickness.
Also Note that if the line wasn't perfectly horizontal or vertical, the length limit seems to vanish:
<Grid>
<Line X1="0" X2="125000.00" Y1="10" Y2="10" StrokeThickness="1" Stroke="Green" />
<Line X1="0" X2="125000.01" Y1="20" Y2="20.0001" StrokeThickness="1" Stroke="Red" />
</Grid>
You can find the bug written up on connect: Disappearing Path (WPF)
It definitely draws past 150,000 pixels, It is a bit strange that the line is not seen in this case, because for example if you do this
<Line X1="0" X2="125000.01" Y1="20" Y2="20" StrokeThickness="2" Stroke="Red"></Line>
or this
<Line X1="0" X2="125000.01" Y1="21" Y2="20" StrokeThickness="1" Stroke="Red"></Line>
all works fine, There is probably a answer somewhere as to why, but good find as this would cause significant flicker if you were animating the value of X2.

Reusable polygon

I want to have a canvas in xaml where i place some icons. These icons are polygons like this one:
<Polygon Points="0,0 20,50, 0,50 20,0" Fill="Red" Stretch="Uniform"/>
But i want to use an icon several times, so i want to define it in the resources and include it by reference into the canvas at a certain position, someway like this:
<Page.Resources>
<Polygon Key="icon1" Points="0,0 20,50, 0,50 20,0" Fill="Red" Stretch="Uniform"/>
<Polygon Key="icon2" Points="0,0 10,30, 10,60 20,0" Fill="Blue" Stretch="Uniform"/>
...
</Page.Resources>
<Canvas>
<Polygon Reference="icon1" X="0" Y="0"/>
<Polygon Reference="icon2" X="10" Y="10"/>
<Polygon Reference="icon1" X="20" Y="20"/>
...
</Canvas>
I found a possible solution on http://www.codeproject.com/KB/WPF/GraphicInXAMLAndWPF.aspx where the polygons are stored in a drawing image, but seems to be to much overhead.
Someone has an better idea how to solve this?
Probably the most obvious and flexible method is to create a UserControl. You can add a new file of type UserControl from the solution explorer, add your Polygon to the 'LayoutRoot' Grid that Visual Studio will create. You can then create as many instances as you like of your user control!
However, checking for similar problems on SO, you could use a content control to render the polygon, note, you would have to use x:Shared="false" to ensure that you are not trying to re-use the same polygon each time.
<Page.Resources>
<Polygon x:Key="icon1" x:Shared="False"
Points="0,0 20,50, 0,50 20,0" Fill="Red" Stretch="Uniform"/>
<Polygon x:Key="icon2" x:Shared="False"
Points="0,0 10,30, 10,60 20,0" Fill="Blue" Stretch="Uniform"/>
...
</Page.Resources>
<Canvas>
<ContentControl Content="{StaticResource icon1}" Canvas.Top="0" Canvas.Left="0"/>
<ContentControl Content="{StaticResource icon2}" Canvas.Top="0" Canvas.Left="10"/>
<ContentControl Content="{StaticResource icon1}" Canvas.Top="0" Canvas.Left="20"/>
...
</Canvas>
See the following:
Vector image as reusable XAML fragment

Creating a Canvas Background Dynamically

I currently have a canvas which contains a bunch of sqaures as its children.
These sqaures sit on different lines.
I would like to draw a background for the canvas which draws lines (like a notepad would have feint blue lines on the paper)
I would like to draw this dynamically by binding it to a collection of "lines"
So if there are 2 lines in the collection, 2 lines will be drawn on the background of the canvas.
I was looking into using DrawingBrush, but i am not sure if this is the correct way forward
<DrawingBrush>
<DrawingBrush.Drawing>
<Line Name=Line1/>
<Line Name=Line2/>
</DrawingBrush.Drawing>
</DrawingBrush>
(BTW The above code does not work, it is just to explain the conecpt)
Try this approach. Use a new class for your canvas:
internal class SpecialCanvas : Canvas
{
...
ObservableCollection<Line> Lines {get; set;}
DrawingVisual backgroundVisual = new DrawingVisual;
public SpecialCanvas()
{
this.Background = new VisualBrush(backgroundVisual);
}
private void OnLinesChanged(...)
{
using (DrawingContext dc = this.backgroundVisual.RenderOpen())
{
// Draw your lines to dc here.
}
}
}
There are a lot of ways you could possibly do what you want to do. For a simple XAML only solution, you could just use an itemscontrol.
<Window.Resources>
<x:Array x:Key="Lines" Type="{x:Type Line}">
<Line X1="0" X2="400" Y1="25" Y2="25" Stroke="Black" />
<Line X1="0" X2="400" Y1="25" Y2="25" Stroke="Black" />
</x:Array>
</Window.Resources>
<Canvas>
<ItemsControl ItemsSource="{StaticResource Lines}" />
<Rectangle Height="20" Width="20" Canvas.Left="20" Canvas.Top="5" Stroke="Blue" Fill="Blue" />
<Rectangle Height="20" Width="20" Canvas.Left="120" Canvas.Top="5" Stroke="Blue" Fill="Blue" />
<Rectangle Height="20" Width="20" Canvas.Left="20" Canvas.Top="30" Stroke="Blue" Fill="Blue" />
<Rectangle Height="20" Width="20" Canvas.Left="120" Canvas.Top="30" Stroke="Blue" Fill="Blue" />
</Canvas>

Resources