Creating a Canvas Background Dynamically - wpf

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>

Related

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:

Canvas as Image in Button WPF

I am in the process of moving all images from our project into a ResourceDictionary to be used across all projects. When using Syncfusion Metro Studio I am able to get the XAML source of the images.
<Viewbox x:Shared="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid>
<Grid Name="backgroundGrid" Width="48" Height="48" Visibility="Collapsed" />
<Path Data="M0,4.1309996L20.362437,4.1309996C20.038338,4.8125897,19.782439,5.5301299,19.594339,6.2769008L2.1451931,6.2769008 2.1451931,23.445208 30.042807,23.445208 30.042807,20.78887C30.342306,20.81167 30.642406,20.834471 30.949104,20.834471 31.367603,20.834471 31.781102,20.81027 32.188001,20.76737L32.188001,25.590239 20.922435,25.590239 20.922435,27.736771 23.605427,27.736771 23.605427,29.882 8.5839529,29.882 8.5839529,27.736771 11.265565,27.736771 11.265565,25.590239 0,25.590239z M29.967411,3.9921243L29.967411,8.1359167 25.823251,8.1359167 25.823251,10.100034 29.967411,10.100034 29.967411,14.243865 31.931586,14.243865 31.931586,10.100034 36.075645,10.100034 36.075645,8.1359167 31.931586,8.1359167 31.931586,3.9921243z M30.948448,0C35.985142,1.0841802E-08 40.067997,4.0825729 40.067997,9.1171007 40.067997,14.153367 35.985142,18.236 30.948448,18.236 25.912456,18.236 21.830998,14.153367 21.830998,9.1171007 21.830998,4.0825729 25.912456,1.0841802E-08 30.948448,0z" Stretch="Uniform" Fill="#FF666666" Width="26" Height="26" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
<Path.RenderTransform>
<TransformGroup>
<TransformGroup.Children>
<RotateTransform Angle="0" />
<ScaleTransform ScaleX="1" ScaleY="1" />
</TransformGroup.Children>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</Viewbox>
The issue here is that I am creating buttons with the image next to my text as follows:
<corecontrols:IdyllicButton Name="btnClose" Height="30">
<StackPanel Orientation="Horizontal">
<Image Source="/MHA.Modules.PolicyAdmin;component/Images/Cancel.png"/>
<TextBlock VerticalAlignment="Center" Margin="5,0" Text="Close"/>
</StackPanel>
</corecontrols:IdyllicButton>
But I cannot set my Image.ImageSource to my StaticResource that I have created in a ResourceDictionary as I get the following error:
An object of the type "System.Windows.Controls.Viewbox" cannot be
applied to a property that expects the type
"System.Windows.Media.ImageSource".
I have also tried using a canvas.
Any ideas how to do this?
Hoping this helps someone:
I found the solution, instead of using a Image, use the Rectangle control in its place and set the OpacityMask to the Viewbox resource.
<Rectangle Width="15" VerticalAlignment="Center"
Height="15"
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}">
<Rectangle.OpacityMask>
<VisualBrush Stretch="Uniform" Visual="{StaticResource Close}"/>
</Rectangle.OpacityMask>
</Rectangle>
I found this scrolling MahApps.Metro source code for the WindowCommand Buttons.

Set the icon color in a MahApp application

I want to set the icon color in a MahApp application, but the brush is not working. In this example the icoun should be white, but still it is black.
<Rectangle Width="20" Height="20">
<Rectangle.Resources>
<SolidColorBrush x:Key="BlackBrush" Color="White" />
</Rectangle.Resources>
<Rectangle.Fill>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_cupcake}" />
</Rectangle.Fill>
</Rectangle>
This is the resource icons.xml file in my app.
<Canvas Width="48" Height="48" Clip="F1 M 0,0L 48,0L 48,48L 0,48L 0,0" x:Key="appbar_cupcake">
<Path Width="24" Height="25" Canvas.Left="13" Canvas.Top="11" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 32,14C 33.1046,14 34,14.8954 34,16C 34,16.3643 33.9026,16.7058 33.7324,17L 34,17C 35.1046,17 36,17.8954 36,19C 36,20.1046 35.1046,21 34,21L 35,21C 36.1046,21 37,21.8954 37,23C 37,24.1046 36.1046,25 35,25L 15,25C 13.8954,25 13,24.1046 13,23C 13,21.8954 13.8954,21 15,21L 16,21C 14.8954,21 14,20.1046 14,19C 14,17.8954 14.8954,17 16,17L 16.2676,17C 16.0974,16.7058 16,16.3643 16,16C 16,14.8954 16.8954,14 18,14C 19,14 21,12 25,11C 29,14 31,14 32,14 Z M 15,26L 35,26L 32,36L 18,36L 15,26 Z " />
</Canvas>
What I'm doing wrong?
If you want to dynamically set the Fill color, you can do that by setting the Fill property. As you can see, you are already using the Fill property for the VisualBrush. Fortunately you can use the VisualBrush also in the OpacityMask property.
<Rectangle Fill="Black">
<Rectangle.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_cupcake}" Stretch="Fill" />
</Rectangle.OpacityMask>
</Rectangle>
Hope that helps.

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.

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

Resources