Stretch Path to fill container - wpf

I have a resource defined like this:
<Canvas x:Key="export"
Width="48"
Height="48">
<Path Fill="{DynamicResource CurrentColor}"
Data="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
</Canvas>
And I'm using it like this:
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10,5,10,10"
Width="Auto"
Height="Auto">
<Button.Template>
<ControlTemplate TargetType="Button">
<Rectangle Width="48"
Height="48"
Fill="{DynamicResource CurrentColor}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill"
Visual="{Binding}" />
</Rectangle.OpacityMask>
</Rectangle>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
As can be seen I'm trying to set the rectangle size to 48x48 but the Path Data coordinates in the resource were defined to draw it as 24x24. No matter what I try I cannot strech the resource to fill the button. How can it be done?
I'm open to change the button implementation if needed. All I need is to be able to show the icon in the button in a certain color.

Remove canvas and use Stretch="Uniform" on the Path. If you want to reduce height/width of the Path, add it into eg. Grid or just set Width and Height of the Path:
<Grid Width="48" Height="48">
<Path Fill="{DynamicResource CurrentColor}"
Stretch="Uniform"
Data="M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" />
</Grid>

Try using HorizontalAlignment="Stretch" and VerticalAlignment="Stretch"
But then again if Width and Height are set they take precedence over the Stretch setting. See Microsoft on HorizontalAlignment.

Related

MaxWitdh of Path

I'm developing an app and I'm facing the same problem.
For some reason, Path does not stretch to the entire area of ​​the program window (I programmatically stretch the application window to two screens), but the Path seems to have its own limit Width. Here is the markup of the Path itself
<Grid >
<InkCanvas Name="inkCanvas"
Cursor="Cross"
UseCustomCursor="True"
EditingMode="{Binding EditingMode}">
<InkCanvas.InputBindings>
<KeyBinding Command="{Binding CloseAppCommand}"
Key="Esc"/>
<KeyBinding Command="{Binding DrawModeCommand}"
Key="F1"/>
</InkCanvas.InputBindings>
<i:Interaction.Behaviors>
<if:MouseBehaviour MouseX="{Binding MouseX, Mode=OneWayToSource}"
MouseY="{Binding MouseY, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
<InkCanvas.Background>
<ImageBrush ImageSource="{Binding Screenshot.Source}"></ImageBrush>
</InkCanvas.Background>
<Path Stroke="Black" Fill="Green" Opacity=".3">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="{Binding BlackoutRect.Rect}" >
</RectangleGeometry>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="{Binding SelectionRect.Rect}" >
</RectangleGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</InkCanvas>
</Grid>
As a result, we see the following picture:
https://imgur.com/nqbqHFO
For example, if we specify Width="300px" for the path
<Path Stroke="Black" Fill="Green" Opacity=".3" Width="300px"...
, then the width of the green area on the screen above will actually be 300px, but if you specify for example 300000px, the picture will be the same as on the screen above. That is, I concluded that the Path element has a maximum width (by the way, if you specify MaxWitdh="30000px", then nothing will change anyway and it will be the same as in the screenshot above)
Hence the main question of this thread: How to change the maximum size of a Path element?
An InkCanvas does not stretch its child elements.
You could add a Panel that performs the desired layout, and bind its Width and Height to the ActualWidth and ActualHeight of the parent InkCanvas:
<InkCanvas ...>
...
<Grid Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=InkCanvas}}"
Height="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=InkCanvas}}">
<Path Stretch="Uniform" ...>
...
</Path>
</Grid>
</InkCanvas>

Draw a line under button

I am trying to draw a line under the button, well in fact, last element within the button. The button has three element inside in vertical: image, label, and finally the line I am trying to put. Line must be the same widht than button. Below the code that is not working (line does not appear):
<Button Name="btnDelete" HorizontalAlignment="Center" Margin="30,10" Command="{Binding DeleteCommand}">
<Button.Template>
<ControlTemplate>
<StackPanel Orientation="Vertical">
<Image Height="36" Width="36" Stretch="UniformToFill" Source="/MyResources;component/PNG/Delete.png"/>
<Label HorizontalAlignment="Center">Delete</Label>
<Line Stroke="Orange" X1="0" Y1="25" X2="{Binding ElementName=btnDelete, Path=Width}" Y2="25" />
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
If you just want a straight Line then use a Border. If you just need it horizontal then use it like :
<Button Name="btnDelete" HorizontalAlignment="Center" Margin="30,10" Command="{Binding DeleteCommand}">
<Button.Template>
<ControlTemplate>
<Border BorderBrush="Orange" BorderThickness="0 0 0 3">
<StackPanel>
<Image Height="36" Width="36" Stretch="UniformToFill" Source="/MyResources;component/PNG/Delete.png"/>
<Label HorizontalAlignment="Center">Delete</Label>
</StackPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
Use Margin and Padding to adjust it - when needed.
Also u should use Paths instead of Image. You can convert them directly in XAML-Paths with Incscape.
Additional Information Path
A Path is a good way to show Icons in XAML. They can look like:
<Path Stroke="Black" Fill="Gray"
Data="M 10,100 C 10,300 300,-200 300,100" />
You can give them different Brushes and they scale very well in the most scenarious. Inkscape has a good feature - to create the Path out of your Image (svg / png / jpg etc).
There are also Icon-Packs that allready have them like font awesome.

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:

Using a Canvas as an icon with bindable color

As many WPFers, I use this pattern to create a vector icon:
1 - Defining a Canvas contains the data:
Resource Item:
<Canvas Width="256" Height="256" ClipToBounds="True" x:Key="SubmitVisualIcon">
<Path Fill="#FFFFFF00">
<Path.Data>
<PathGeometry FillRule="Nonzero" Figures="M44.436129,25.256006L54.222273,25.256006 75.259996,46.29286 70.368799,51.187792 54.094614,67.462006 44.561911,67.462006 44.436129,67.337162 62.016504,49.752106 15.633995,49.752106 15.633995,42.837337 62.016504,42.837337z M45,5.6100006C23.245507,5.6100006 5.6100006,23.245506 5.6100006,45 5.6100006,66.754498 23.245507,84.389999 45,84.389999 66.754499,84.389999 84.389997,66.754498 84.389997,45 84.389997,23.245506 66.754499,5.6100006 45,5.6100006z M45,0C69.852816,0 89.999998,20.147187 89.999998,45 89.999998,69.852814 69.852816,90.000004 45,90.000004 20.147188,90.000004 9.5367432E-07,69.852814 0,45 9.5367432E-07,20.147187 20.147188,0 45,0z"/>
</Path.Data>
</Path>
</Canvas>
2 - Using it in a control template:
ControlTemplate:
<ControlTemplate x:Key="MyButton" TargetType="{x:Type Button}">
<Border x:Name="root">
<Grid>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Width="32" Height="32">
<Rectangle.Fill>
<VisualBrush Stretch="Fill">
<VisualBrush.Visual>
<Binding Path="(ui:UIElement.VisualIcon)"
RelativeSource="{RelativeSource TemplatedParent}"/>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
which the ui:UIElement.VisualIcon property, is an attached property to telling the template which resource to use. For example:
<Button Content="Save" ui:UIElement.VisualIcon="{DynamicResource SubmitVisualIcon}">
Now, as you can see, I have to get the Path in the Canvas a Fill value:
<Path Fill="#FFFFFF00">
The question is, is it possible to bind the Fill value to something on TemplatedParent? e.g. I have an attached property to holding icon brush:
<Button Content="Save" ui:UIElement.VisualIcon="{DynamicResource SubmitVisualIcon}"
ui:UIElement.VisualIconForeground="Some_Brush">
And I tried to use it like below:
<Path Fill="{Binding ui:UIElement.VisualIconForeground,RelativeSource={RelativeSource TemplatedParent}}">
But it doesn't work. Have you any idea to do this? I mean binding a resource item's some property to be set where they will be used? -Bad English, I know, sorry :(
Finally I found a solution that works in case. I should use Rectangle.OpacityMask instead of Rectangle.Fill:
The resource:
<Canvas Width="256" Height="256" ClipToBounds="True" x:Key="SubmitVisualIcon">
<Path Fill="#FFFFFF00">
<Path.Data>
<PathGeometry FillRule="Nonzero" Figures="M44.436129,25.256006L54.222273,25.256006 75.259996,46.29286 70.368799,51.187792 54.094614,67.462006 44.561911,67.462006 44.436129,67.337162 62.016504,49.752106 15.633995,49.752106 15.633995,42.837337 62.016504,42.837337z M45,5.6100006C23.245507,5.6100006 5.6100006,23.245506 5.6100006,45 5.6100006,66.754498 23.245507,84.389999 45,84.389999 66.754499,84.389999 84.389997,66.754498 84.389997,45 84.389997,23.245506 66.754499,5.6100006 45,5.6100006z M45,0C69.852816,0 89.999998,20.147187 89.999998,45 89.999998,69.852814 69.852816,90.000004 45,90.000004 20.147188,90.000004 9.5367432E-07,69.852814 0,45 9.5367432E-07,20.147187 20.147188,0 45,0z"/>
</Path.Data>
</Path>
</Canvas>
The Template:
<ControlTemplate x:Key="MyButton" TargetType="{x:Type Button}">
<Border x:Name="root">
<Grid>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Width="32" Height="32"
Fill="{TemplateBinding ui:UIElement.VisualIconForeground}">
<!-- fill the rectangle with what color do you want, it also can be bounded to every thing -->
<!-- and then, use the Canvas as a OpacityMask on rectangle, just like this: -->
<Rectangle.OpacityMask>
<VisualBrush Stretch="Fill">
<VisualBrush.Visual>
<Binding Path="(ui:UIElement.VisualIcon)"
RelativeSource="{RelativeSource TemplatedParent}"/>
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.OpacityMask>
<!-- this will show the icon with color you defined in Rectangle.Fill -->
</Rectangle>
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
Usage:
<Button Content="Save" ui:UIElement.VisualIcon="{DynamicResource SubmitVisualIcon}"
ui:UIElement.VisualIconForeground="Some_Brush">
Another Usage:
<Button Content="Save" ui:UIElement.VisualIcon="{DynamicResource SubmitVisualIcon}"
ui:UIElement.VisualIconForeground="Some_Another_Brush">
Another Usage 2:
<Button Content="Save" ui:UIElement.VisualIcon="{DynamicResource AnotherVisualIcon}"
ui:UIElement.VisualIconForeground="Some_Another_Brush">
Too long for comment, so I will write here:
If I understand correctly, SubmitVisualIcon is a Resource. Resource Binding does not support, so you do not work Binding with TemplatedParent, because the Resource is not part of the visual tree, or part of the template. Probably you'll have to look for an alternative.
As an alternative, you can use the settings that are supported by the application. Set the color in the settings, and reference it in the resource like that:
xmlns:properties="clr-namespace:MyNamespace.Properties"
<Path Fill="{Binding Source={x:Static properties:Settings.Default}, Path=SomeColor, Mode=TwoWay}" ... />
More information can be found here and here.

Resources