This question aims to be a best practice on how to use images created with Inkscape and saved as XAML.
There are many articles on the internet but not many of them show the pro and cons of each solution. Example 1, Example 2
When you create an image with Inkscape and save as XAML you'll only have a Viewbox inside an XAML.
<?xml version="1.0" encoding="UTF-8"?>
<!--This file is NOT compatible with Silverlight-->
<Viewbox xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Stretch="Uniform">
<Canvas Name="svg8" Width="210" Height="297">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0"/>
</Canvas.RenderTransform>
<Canvas.Resources/>
<!--Unknown tag: sodipodi:namedview-->
<!--Unknown tag: metadata-->
<Canvas Name="layer1">
<Rectangle xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Canvas.Left="55.184521" Canvas.Top="113.30357" Width="33.261906" Height="33.261906" Name="rect4485" Fill="#FFFFFFFF" StrokeThickness="5.29166651" Stroke="#FF000000" StrokeMiterLimit="4"/>
</Canvas>
</Canvas>
</Viewbox>
To use this file I'd normally have to wrap it in a ResourceDictionary. This is annoying because I cannot edit the file in Inkscape anymore without having to remove the ResourceDictionary before.
The image will be shown multiple times, for example in a ListViewItem or any other element that has an ItemsSource.
Is there a way to have only one ResourceDictionary where I can import all of these XAML files?
Let's assume I have wrapped it in a ResourceDictionary and given the Viewbox a x:Key="SquareIcon" value.
What is most resource effective way of showing this element?
Should I use a ContentPresenter and set its Content property and set the Canvas x:Shared="false"?
Is it more effective to use a Label (or some other control)?
Maybe remove the Viewbox and always use the Canvas in a Viewbox in my own view?
Should I wrap the Viewbox in a ControlTemplate and use it in ContentPresenter Template property? This way I don't need to use the x:Shared attribute on Canvas.
When saving an asset from Inkscape as xaml, you can copy it to Blend and then convert it to Path. Then you have a vector representation of that asset, which you can easily incorporate as a style.
You can read about converting to path here
Keep in mind that you can also combine several elements and then convert them. Basically, any kind of image that is in one color can be converted to a single path.
As far as best practice goes, I like to keep all of my Path/Image/Icon related styles in a single ResourceDictionary and then reference it globally in App.xaml file.
I think you can create it manually by reading all your files. Steps are next:
1) You have your resources in your project with xaml or svg
2) Create your markup extension which will provide a value of Geometry by a path of your embedded resource file with an icon. (it should parse and provide value)
3) Create a custom control IconControl for displaying icons with dependency property IconPath (String) and bind it in your template by template binding
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:IconControl">
<Viewbox ><!--Could be other container -->
<Path Data="{TemplateBinding IconPath}"
Fill="{StaticResource DefaultBrush}" />
</Viewbox>
</ControlTemplate>
Viewbox isnt so good, because it applies transformations to content. So you should use simpliest container to achive good performance.
4)Use your control in markup
<IconControl IconPath={locl:IconSvgMarkupExtension PathToYourEmbededResourceHere}/>
I wrote it without VS, from memory, so there could be some mistakes.
Should I wrap the Viewbox in a ControlTemplate and use it in
ContentPresenter Template property? This way I don't need to use the
x:Shared attribute on Canvas.
It is also a possible case, a few years ago I've used the same approach without so each of my icons was ContentControl with different Template for it depending on Icon.
I had a similar issue : Inkscape svg saved as XAML does not work for me.
After some long search I found this very quick but useful video on Youtube :
https://www.youtube.com/watch?v=otEV-skBqyI
combine with the converter available on Github here : https://github.com/BerndK/SvgToXaml
used to convert svg to a DrawingImage ressource
(available thanks BerndK)
It solves my issue !
Just then have to save the ressource file as .xaml in my project
content of Xaml file
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingImage x:Key="MyVectorImageName">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V297 H210 V0 H0 Z">
.... //this part is the DrawingImage content Iget in converter and I copied pasted
</ResourceDictionary>
And to use it :
<Grid x:Name="gridImage" Margin="0,62.8,0,0" >
<!-- Add vector ressource and display it -->
<Grid.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="images/MyVectorImageName.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Grid.Resources>
<Image x:Name="imgName" Source="{StaticResource MyVectorImageName}"/>
</Grid>
Note : I added it as a local ressouce not a global one (but i is possible too)
Brief
I am trying to programmatically change the colour of specific elements at runtime. The project currently uses Telerik and I am able to change the theme at runtime: This works as expected with no issues. I can't, however, figure out how to change the fill or stroke colour at runtime of custom shape elements in XAML.
Within my project I have a ResourceDictionary file named _Icons.xaml that contains vector shapes to use as the content for other controls (such as buttons).
Code
App.xaml.cs
I am using the following code to change the theme's marker colours at runtime.
GreenPalette.Palette.MarkerColor = (Color)ColorConverter.ConvertFromString("#FF000000");
_Icons.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyNamespace">
<ControlTemplate x:Key="Box">
<Viewbox>
<Rectangle Width="357" Height="357" Fill="#000000"/>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="BoxOutline">
<Viewbox>
<Rectangle Width="357" Height="357" StrokeThickness="45" Stroke="#000000"/>
</Viewbox>
</ControlTemplate>
</ResourceDictionary>
MainWindow.xaml
<telerik:RadButton>
<StackPanel>
<ContentControl Template="{StaticResource Box}" Height="58"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0">Box</TextBlock>
</StackPanel>
</telerik:RadButton>
<telerik:RadButton>
<StackPanel>
<ContentControl Template="{StaticResource BoxOutline}" Height="58"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0">BoxOutline</TextBlock>
</StackPanel>
</telerik:RadButton>
Question
In _Icons.xaml I have the following lines:
<Rectangle Width="357" Height="357" Fill="#000000"/>
<Rectangle Width="357" Height="357" StrokeThickness="45" Stroke="#000000"/>
Given the following line in App.xaml.cs:
GreenPalette.Palette.MarkerColor = (Color)ColorConverter.ConvertFromString("#FF000000");
How can I either...
Programmatically change the values of Fill and/or Stroke (an element that only has Fill set should only change the Fill value and not add a Stroke attribute) from the App.xaml.cs file? Or ...
Bind the values in XAML for Fill or Stroke to receive the value given by my App.xaml.cs file?
Thank you for taking the time to read my question. Any help regarding this is greatly appreciated.
First i advise you to eject that controls off your resource sheet so you can actually control them properly.
When you do that, go the code behind your control and just use dependency property of type 'Color' of the 'SolidColorBrush' that is used by the background and then bind it by element name, you gotta build the project at least once before attempting to bind.
Here is how you write a dependency property
hint: in VS write 'propdp' and hit tab twice to bring up a template, but you can use mine for now.
public Color _color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("_color", typeof(Color), typeof(Fileentity), null);
after you build once go to the xalm and put this inside your rectangle:
<Grid.Background>
<SolidColorBrush Color="{Binding
_color,ElementName=YourControlName" />
</Grid.Background>
if you do it right you will be able to access this property when inserting the control on you Page like
<local:YourcontrolName _color="{x:Bind MyColorProperty }"/>
where 'MyColorProperty' is a property that implements INotifyPropertyChanged.
An alternative way is to use a datacontext directly on the usercontrol and just bind your color to one of its properties like:
public YourControl(){
this.InitializeComponent();
this.DataContext = new MyClassDataContext();
var myContext= (MyClassDataContext)this.DataContext;
_color=MyContext.MyColorProperty;}
Where MyClassDataContext is any given class that contains a Color property(MyColorProperty) of your choosing.
You need a Dependency property here as well that binds to your Controls xalm like i showed before.
I know all this is might too hard to grasp at once, thats cause it requires basic knowledge of MvvM.
In our project we have a ResourceDictionary with some Icons that looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas x:Key="Icon.Refresh"
x:Shared="False"
Width="32"
Height="32"
Clip="F1 M 0,0L 32,0L 32,32L 0,32L 0,0">
<Path .../>
<Path .../>
<Path .../>
</Canvas>
</ResourceDictionary>
The x:Shared="False" attribute is needed, because else the icon would disappear when I use it in multiple views.
Now we want to make another project with the same icons, so we decided to put them in a library project that is referenced by both projects.
But when we try to run the application we always get the error:
Shared attribute in namespace "http://schemas.microsoft.com/winfx/2006/xaml" can be used only in compiled resource dictionaries."
but we can't get rid of the x:Shared="False" attribute, because as far as I know it's the only way to stop the icons from disappearing.
So we what can we do, to share the icons over multiple projects with a project reference and without disappearing icons?
Judging by this error, we can understand that x:Shared attribute can be used only for compiled ResourceDictionary. Quote from MSDN x:Shared Attribute:
The ResourceDictionary that contains the items with x:Shared must be compiled. The ResourceDictionary cannot be within loose XAML or used for themes.
Compiled ResourceDictionary is one that Build action to set Page, as in this case, it is converted to BAML (Binary Application Markup Language) at run-time. This attribute usually be set by default when creating new ResourceDictionary.
BAML is simply XAML that has
been parsed, tokenized, and converted into binary form to increase performance for working with XAML files. Quote from Adam Nathan WPF book:
BAML is not like Microsoft intermediate
language (MSIL); it is a compressed declarative format that is faster to load and parse (and
smaller in size) than plain XAML. BAML is basically an implementation detail of the
XAML compilation process.
Therefore it is always advisable to check this flag in ResourceDictionary, because if it will be set Resource, in the memory will be stored not packaged version of XAML, which later may affect to the performance of the whole WPF application.
Could you try setting the Build action to "Page" instead of "Resources", as mentioned here:
-https://connect.microsoft.com/VisualStudio/feedback/details/776631/using-x-shared-in-a-resourcedictionary-prevents-you-from-setting-the-file-build-action-to-resource
For Reference:
In WPF, x:Shared is only valid under the following conditions:
The ResourceDictionary that contains the items with x:Shared must be compiled. The ResourceDictionary cannot be within loose XAML or used
for themes.
The ResourceDictionary that contains the items must not be nested within another ResourceDictionary. For example, you cannot use
x:Shared for items in a ResourceDictionary that is within a Style that
is already a ResourceDictionary item.
https://learn.microsoft.com/en-us/dotnet/framework/xaml-services/x-shared-attribute
I am not sure if I ran into something similar but this strikes a chord. The advice learned and now given is to change the container. Use a DrawingImage which will contain the multiple vectors which itself resides in the shared dictionary.
<DrawingImage x:Key="diSingle">
<DrawingImage.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#FF22BAFD" Geometry="F1 M 14.72,15.68L 12.38,15.68L 7.205,5.92L 7.11,5.92L 2.29,15.68L 0,15.68L 6.58,2.56L 7.595,2.56L 14.72,15.68 Z "/>
<GeometryDrawing Brush="#FF22BAFD" Geometry="F1 M 21.585,25.6C 21.1017,25.6 20.69,25.4275 20.35,25.0825C 20.01,24.7375 19.84,24.3267 19.84,23.85C 19.84,23.37 20.01,22.955 20.35,22.605C 20.69,22.255 21.1017,22.08 21.585,22.08C 22.0783,22.08 22.4975,22.255 22.8425,22.605C 23.1875,22.955 23.36,23.37 23.36,23.85C 23.36,24.3267 23.1875,24.7375 22.8425,25.0825C 22.4975,25.4275 22.0783,25.6 21.585,25.6 Z "/>
<GeometryDrawing Brush="#FF22BAFD" Geometry="F1 M 28.625,25.6C 28.1417,25.6 27.73,25.4275 27.39,25.0825C 27.05,24.7375 26.88,24.3267 26.88,23.85C 26.88,23.37 27.05,22.955 27.39,22.605C 27.73,22.255 28.1417,22.08 28.625,22.08C 29.1183,22.08 29.5375,22.255 29.8825,22.605C 30.2275,22.955 30.4,23.37 30.4,23.85C 30.4,24.3267 30.2275,24.7375 29.8825,25.0825C 29.5375,25.4275 29.1183,25.6 28.625,25.6 Z "/>
<GeometryDrawing Brush="#FF22BAFD" Geometry="F1 M 35.665,25.6C 35.1817,25.6 34.77,25.4275 34.43,25.0825C 34.09,24.7375 33.92,24.3267 33.92,23.85C 33.92,23.37 34.09,22.955 34.43,22.605C 34.77,22.255 35.1817,22.08 35.665,22.08C 36.1583,22.08 36.5775,22.255 36.9225,22.605C 37.2675,22.955 37.44,23.37 37.44,23.85C 37.44,24.3267 37.2675,24.7375 36.9225,25.0825C 36.5775,25.4275 36.1583,25.6 35.665,25.6 Z "/>
<GeometryDrawing Brush="#FF22BAFD" Geometry="F1 M 48.96,25.155L 48.96,28.48L 47.36,28.48L 47.36,25.28C 45.1267,25.28 43.3133,24.8217 41.92,23.905L 41.92,21.12C 42.5267,21.6633 43.3567,22.1192 44.41,22.4875C 45.4633,22.8558 46.4467,23.04 47.36,23.04L 47.36,15.14C 45.08,14.04 43.6033,13.0258 42.93,12.0975C 42.2567,11.1692 41.92,10.0717 41.92,8.805C 41.92,7.30167 42.4325,6.0025 43.4575,4.9075C 44.4825,3.8125 45.7833,3.15667 47.36,2.94L 47.36,9.53674e-007L 48.96,9.53674e-007L 48.96,2.88C 51.12,2.94333 52.6133,3.23333 53.44,3.75L 53.44,6.4C 52.3167,5.60667 50.8233,5.18 48.96,5.12L 48.96,13.24C 51.1733,14.27 52.6867,15.2658 53.5,16.2275C 54.3133,17.1892 54.72,18.2833 54.72,19.51C 54.72,20.9867 54.2117,22.2267 53.195,23.23C 52.1783,24.2333 50.7667,24.875 48.96,25.155 Z M 47.36,12.37L 47.36,5.215C 46.4733,5.38833 45.7717,5.76917 45.255,6.3575C 44.7383,6.94583 44.48,7.66 44.48,8.5C 44.48,9.38 44.6908,10.1017 45.1125,10.665C 45.5342,11.2283 46.2833,11.7967 47.36,12.37 Z M 48.96,15.945L 48.96,22.915C 51.0933,22.4817 52.16,21.4133 52.16,19.71C 52.16,18.29 51.0933,17.035 48.96,15.945 Z "/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
Then access the image as a dynamic resource (bound at runtime) in a container such as this ribbon button:
<RibbonToggleButton x:Name="btnSingleline"
IsChecked="{Binding RegexOption_Single, Mode=TwoWay}"
Label="Single Line"
LargeImageSource="{DynamicResource diSingle}"
SmallImageSource="{DynamicResource dilines}"
ToolTipImageSource="{DynamicResource dilines}"/>
See my answer below for a different example using DrawingImage.
Best way to use a vector image in WPF?
Windows 8 Style Apps (ex. "Metro"), Visual Studio 2012, XAML.
I have a UserControl derived from Canvas. It has one child element - a Polygon with its Data bound to a property (with INotifyPropertyChanged implemented):
<Canvas x:Name="MyPolygon">
<Polygon Points="{Binding ElementName=MyPolygon,Path=MyPoints}" ... />
</Canvas>
The property is set and the Polygon is correctly rendered, both at design-time and run-time, if I instantiate that control elsewhere in XAML, passing in a string:
<local:MyPolygon MyPoints="..." />
However, changing the values in that string is tedious. A designer would prefer to have a collection of some UI knots (like Ellipses) visible at design-time but invisible at run-time, so that they could drag them in the designer and have the Polygon reconstruct its geometry on the fly:
<local:MyPolygon>
<Ellipse Canvas.Left="204" Canvas.Top="57" ... />
<Ellipse Canvas.Left="166" Canvas.Top="30" ... />
...
</local:MyPolygon>
Basically I want to keep the geometry information in (extended) .Children. Is this possible?
(There could be some event/constructor where the control could examine its .Children (after those Ellipses are inserted), retrieve their coordinates, and build MyPoints. The designer would have to trigger that event for the geometry to be visible at design time)
Have you looked at design data like this.
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
GetSampleData();
}
else GetRealData();
or
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding ItemGroups,
Source={d:DesignInstance Type=data:SampleDataSource,
IsDesignTimeCreatable=True}}"/>
So, I ended up creating a Polygon on the same level where I have the Ellipses.
<Polygon Points="{Binding ElementName=MyPoints,Converter={StaticResource PolygonConverter}}" ... />
<Canvas x:Name="MyPoins">
<Ellipse Canvas.Left="228" Canvas.Top="69" ... />
<Ellipse Canvas.Left="166" Canvas.Top="30" ... />
...
</Canvas>
The binding converter converts coordinates of all .Children of the object to a string.
This works at both design time and run time.
Unfortunately, one must rebuild the project after moving the Ellipses around in order for VS designer to refresh the view and pick up the changes, which makes the design process much less intuitive than it could have been. :/