SL 4: Strange behavior with templated control - silverlight

We have some xaml:
<Style TargetType="local:V_RelLine">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:V_RelLine">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Ellipse x:Name="startArrow" Height="20" Width="60" Fill="Green" Stroke="Blue" Visibility="Visible" />
<Path x:Name="LinePathPart" Visibility="Visible" Stroke="Red" StrokeDashArray="2 2" StrokeThickness="2"
>
<Path.Data>
<PathGeometry x:Name="LinePathGeometry" >
<PathFigure x:Name="linePathBezierFigure" >
<BezierSegment x:Name="linePathBezierSegment" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
<Ellipse x:Name="endArrow" Height="20" Width="20" Fill="Red" Stroke="Red" Visibility="Visible" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And in the code behind:
LinePathBezierFigure.StartPoint = startPoint;
Canvas.SetLeft(startArrow, startPoint.X);
Canvas.SetTop(startArrow, startPoint.Y);
/* similar for endArrow */
At runtime, startArrow and endArrow end up at the same point (even though they were set to different locations), as though they ended up at 0,0.
In fact, a subsequent call to Canvas.GetLeft(startArrow) show that it is at 0,0.
What is going on? Why are different objects in the same template, assigned the same coordinates, ending up in different locations?
Thanks for any insight in to this....

Just a thought but Canvas.Left and Canvas.Top normally only work well when the elements are placed in a Canvas rather than a Grid like you are using currently.

Related

How to create a glass effect on a circle?

I've made a circle for display the status of a connection for my DB, it's working nice but I want to make it look like a bulb with glass effects.
<Canvas Visibility="Visible" x:Name="connection_red" Grid.Column="1">
<Ellipse
Fill="Red"
Height="13"
Width="13"
Margin="0,7,80,0"
StrokeThickness="1"
Stroke="White"/>
</Canvas>
I have visited many online tutorials, but they use too much code and I do not want to use 50 lines of code to achieve this effect. Someone knows how to do to get the effect glass bulb in a few lines of code? You can show me the way?
This is the actual preview:
You need to either specify the opacity seperately...
<Ellipse Fill="#FF0000" Opacity="0.25"
Height="130"
Width="130"
Margin="0,7,80,0"
StrokeThickness="1"
Stroke="White" />
...or use a SolidColorBrush...
<Canvas Visibility="Visible" x:Name="connection_red" Grid.Column="1">
<Ellipse Height="130"
Width="130"
Margin="0,7,80,0"
StrokeThickness="1"
Stroke="White">
<Ellipse.Fill>
<SolidColorBrush>
<SolidColorBrush.Color>
<Color A="25" R="255" G="0" B="0" />
</SolidColorBrush.Color>
</SolidColorBrush>
</Ellipse.Fill>
</Ellipse>
Would be good though if you could clarify exactly what type of effect you're after.
<Canvas Visibility="Visible" x:Name="connection_red" Grid.Column="1">
<Ellipse
Fill="#7FFF0000"
Height="13"
Width="13"
Margin="0,7,80,0"
StrokeThickness="1"
Stroke="White"/>
</Canvas>

Adding line to a shape in XAML

I am modifying the button visual appearance in my XAML code with a hexagon. Now, I want to add 2 lines to the two outer edges of the hexagon like the image below:
https://skydrive.live.com/redir.aspx?cid=204df65b0e6e1655&resid=204DF65B0E6E1655!117&parid=204DF65B0E6E1655!107&authkey=!AEzKZRmwMNhBWxM
Can anyone tell how and where to add it ? My code is something like this
<Page.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Canvas>
<Polygon Canvas.Top="30" Points=
"430,0
400,32
-30,32
-60,0
-30,-32
400,-32"
Stroke="Brown" StrokeThickness="10"/>
<ContentPresenter Canvas.Left="80" Foreground="White" FontSize="40"></ContentPresenter>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="Black">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Row="1" Margin="40,-100 0,-50" HorizontalAlignment="Center">Hello World</Button>
</Grid>
I cant get the line to connect to those edges and spread all the way to the page end.
Any idea ?
Do you want this within your button style? I was able to achieve this through the XAML button control template code below.
I put separators in a grid and then put rectangles on top them to add stroke to the image.
I also added an upper margin of 2 onto the polygon to align with the rectangles.
<ControlTemplate TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="500"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Fill="White" Grid.Column="0"/>
<Rectangle Fill="White" Grid.Column="2"/>
<Separator Background="White" Grid.Column="0"/>
<Separator Background="White" Grid.Column="2"/>
<Canvas Grid.Column="1" Margin="64,2,0,0">
<Polygon Points=
"430,0
400,32
-30,32
-60,0
-30,-32
400,-32"
Stroke="Brown" StrokeThickness="10"/>
<ContentPresenter Canvas.Left="80" Foreground="White" FontSize="40"/>
</Canvas>
</Grid>
</ControlTemplate>

Bind the height of a Polygon to the StackPanel height

I can't figure how to bind the height of a polygon to the height of my stack panel.
If I wanted to add a rectangle, all I had to do is something like that:
<Rectangle Width="75" >
<Rectangle.Fill>
<SolidColorBrush Color="Red" />
</Rectangle.Fill>
</Rectangle>
This one won't brake the height of the panel. but with the polygon it seems like I can't leave some of the points as blank so that will scale with the parent panel.
Thanks
Wrap your polygon with a <Viewbox>.
The Viewbox automatically scales its content to its size. Exactly how it does so can be tweaked with the Stretch and StretchDirection properties.
this solution works too
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="1,1,0,1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TextBlock1" Margin="2" />
<TextBlock Text="TextBlock2" Margin="2" />
<TextBlock Text="TextBlock3" Margin="2" />
<TextBlock Text="TextBlock4" Margin="2" />
<TextBlock Text="TextBlock5" Margin="2" />
</StackPanel>
</Border>
<Path Fill="Yellow" Stroke="Black" StrokeThickness="1"
Width="50" Stretch="Fill">
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="1,0.5">
<LineSegment Point="0,0" IsSmoothJoin="True" />
<LineSegment Point="0,1" IsSmoothJoin="True" />
</PathFigure>
</PathGeometry>
</Path.Data>
</Path>
</StackPanel>
</Grid>

Why does WPF render two identical objects differently?

Take this Window as an example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" ResizeMode="NoResize" SizeToContent="WidthAndHeight" SnapsToDevicePixels="True">
<Grid Width="17" Margin="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="1" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="1" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
</Grid>
</Window>
Once the application has been ran, the topmost RepeatButton is taller than the bottom one (consequently the top triangle is also bigger than the bottom one). Why?
If I create 4 rows of identical RepeatButtons, then the 1-st and 3-rd RepeatButtons are of equal size and are bigger than the 2-nd and 4-th RepeatButton?!?
I'm thinking this must be a bug in the WPF layout system, but how to work around this problem? I can't use fixed heights (which does solve the problem), because I need the RepeatButtons and triangles to strecth as the container gets bigger (the example I provided is simplifed just to show the issue, I know I can't resize the example window...).
Edit:
In reply to Ben's comments:
Yes, with the added style the triangles do come out as 9px and 8px tall (I could just as well through out the RepeatButtons alltogether and leave only the polylines as the grids children, that would give the same result). Because the triangles are equal sided, then giving the grid a width of 17 will indeed cause the height to become 17 as well, which of course is not enough for two equal height triangles..
What I'm actually trying to do is create a NumericUpDown control. I've found that by default a spinner width of 17 and a UserControl MinHeight of 24 looks very good. The only problem is, that if I drop this UserControl into a Grid, then the top triangle always pushes itself 1px to tall, ruining the look. No matter how I've tried to mingle with the internal Margins and Paddings, the top triangle always makes itself 1px taller than necessary. So in essence what I want is to have a NumericUpDown, that when put into a Grid, doesn't distort itself. By default it should look perfect from the get go (no Grid RowHeight="Auto") and scale properly (no fixed heights). It must be possible, because by looking at the pixels physically then everything can fit into the given dimensions nicely.
Here is my NumericUpDown, I've stripped out all the non essential things to make it more compact:
<UserControl x:Class="HRS.NumericUpDown"
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"
MinWidth="40" MinHeight="24" Name="ucNUPD" Background="White" SnapsToDevicePixels="True">
<UserControl.Resources>
<Style TargetType="{x:Type RepeatButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Name="borderOuter" BorderThickness="1" BorderBrush="Red">
<Border Name="borderInner" BorderThickness="1" BorderBrush="Blue">
<ContentPresenter Margin="2,1" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="triangleStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Polyline HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Green" RenderOptions.EdgeMode="Aliased" Stretch="Uniform">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Border BorderThickness="1" BorderBrush="#ABADB3">
<DockPanel>
<UniformGrid Margin="1" DockPanel.Dock="Right" Rows="2" MinWidth="17" Width="17">
<RepeatButton Name="repeatButtonUp" Grid.Row="0">
<ContentControl Style="{StaticResource triangleStyle}" />
</RepeatButton>
<RepeatButton Name="repeatButtonDown" Grid.Row="1">
<ContentControl Style="{StaticResource triangleStyle}" RenderTransformOrigin="0.5, 0.5">
<ContentControl.RenderTransform>
<ScaleTransform ScaleY="-1" />
</ContentControl.RenderTransform>
</ContentControl>
</RepeatButton>
</UniformGrid>
<TextBox BorderThickness="0" VerticalContentAlignment="Center" Text="0" />
</DockPanel>
</Border>
</UserControl>
Here is a picture of what the end result looks like:
(The image doesn't fit a 100%, but you can still see all the relevant details).
On the right side you can see a zoom-in of the NumericUpDowns. The bottom one looks correct, but only because the grid's row Height is set to Auto. The top one is distorted, but by default I want it to look exactly like the bottom one.
Hmmm...
I might just have found a workable solution:
It seems that by setting the Margin of the ContentPresenter in my NumericUpDown to "3,1", everything looks perfect. Preliminary testing is very promising as everything seems to be exactly the way it should be...
I'll test it some more tommorow and if all goes good will mark Ben's answer as correct :)
With SizeToContent="WidthAndHeight" the height will be 17 as you you set the Grid's Width to 17. But with 17/2=8.5 one row will be 9 (rounding occurs becouse of SnapsToDevicePixels="True") but the other will be 8 pixel tall. If you set the Width to 18 they will be equal.
Proof of my theory:
<Grid Width="17" Margin="0">
<Grid.Resources>
<Style TargetType="{x:Type RepeatButton}">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<ContentPresenter Margin="0"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<RepeatButton Grid.Row="0" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True">
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
With this snippet you gain a Triangle that has 9 pixel height, and one with 8 pixels.
But if you want for a solution try this:
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True" Height="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth}" >
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
This way the width and the height of the buttons will be equal.
If think you can write a little converter too, that will can do some nasty things:
<RepeatButton Grid.Row="1" SnapsToDevicePixels="True" Height="{Binding RelativeSource={RelativeSource Self}, Path=ActualWidth, Converter={StaticResource TriangleWidthConverter}}" >
<Polyline RenderOptions.EdgeMode="Aliased" Stretch="Uniform" Margin="0" Fill="Red">
<Polyline.Points>
<Point X="0" Y="3" />
<Point X="3" Y="0" />
<Point X="6" Y="3" />
</Polyline.Points>
</Polyline>
</RepeatButton>
public class TriangleWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int width = 0;
if (int.TryParse(value.ToString(), out width))
return width + 1; // Do some fun here.
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I hope these will help.
No idea why you are seeing this behavior, but this might work to fix it: try setting the height on each of your your rows to .5* if you have 2 rows or .25* if you have 4 rows (or .1* if you have 10 rows, etc.).

Style a border with a different brush color for each corner

I have created a static resource defining the border of a specific item in my xaml, but I can't find a good way to define a unique color for each side!
xaml:
<Border Style="{StaticResource SidePanelBorder}">
<!-- rest of the xaml -->
</Border>
StaticResource:
<Style x:Key="SidePanelBorder">
<Setter Property="Control.BorderBrush" Value="#FF363636" />
<Setter Property="Control.BorderThickness" Value="1" />
</Style>
But I want to define one color for each side of the border, and eventually also a different Border thickness.
Any good techniques out there doing this?
Seems very hacky, but you could define borders within borders, and make only 1 side have a thickness. For example
<Border BorderThickness="0,0,0,10" BorderBrush="Green">
<Border BorderThickness="0,0,10,0" BorderBrush="Blue">
<Grid>
<Button>Hello</Button>
</Grid>
</Border>
</Border>
would give a green border on the bottom and a blue border to the right. Isn't the prettiest piece of Xaml though.
Another solution using one Border and a VisualBrush, allowing setting the Border's CornerRadius and BorderThickness:
<Border BorderThickness="10" CornerRadius="10" HorizontalAlignment="Right" Height="150" VerticalAlignment="Bottom" Width="150" Margin="0,0,92.666,42.667">
<Border.BorderBrush>
<VisualBrush>
<VisualBrush.Visual>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Path x:Name="ColoredBorderLeft" Data="M0,0 L0,0 1,0.5 L1,0.5 0,1" Fill="Blue" Stretch="Fill" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderRight" Data="M1,0 L1,0 0,0.5 L0,0.5 1,1" Fill="Red" Stretch="Fill" Grid.Column="1" Grid.RowSpan="2"/>
<Path x:Name="ColoredBorderTop" Data="M0,0 L0,0 0.5,1 L0.5,1 1,0" Fill="Green" Stretch="Fill" Grid.ColumnSpan="2"/>
<Path x:Name="ColoredBorderBottom" Data="M0,1 L0,1 0.5,0 L0.5,0 1,1" Fill="Yellow" Stretch="Fill" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Border.BorderBrush>
</Border>
The Grid is needed to prevent the tips of the triangle Paths to "push through" into the border.
The Path.Name's can be used for DataBinding or setting the color from code behind.
you can have a DockPanel and can put 4 Borders inside it, each docked to different side.
like:
<DockPanel LastChildFill="true">
<Border DockPanel.Dock="Left" Background="Red"/>
<Border DockPanel.Dock="Top" Background ="Blue"/>
<Border DockPanel.Dock="Right" Background ="Yellow"/>
<Border DockPanel.Dock="Bottom" Background ="Green"/>
<Grid>
...........your control here
</Grid>
</DockPanel>
If you use a Grid you can have Border's overlay on one another to achieve the same affect. Just set the border thickness of the border color you want to show and have the other border thickness be 0.
<UserControl.Resources>
<Style x:Key="GreenBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="1,1,1,0" />
</Style>
<Style x:Key="RedBorder" TargetType="Border">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource GreenBorder}">
<!-- Content goes here -->
</Border>
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource RedBorder}">
</Border>
</Grid>
This will give a Green border to the left, top and right borders, but a Red border to the bottom border of the Grid cell.
there's no easy way to do this without writing your own control or subclassing border

Resources