5

I have a lot of XAML files from Visual Studio Image Library. Here's the content of one of them - Add_16xMD.xaml

<!-- This file was generated by the AiToXaml tool.-->
<!-- Tool Version: 14.0.22307.0 -->
<Viewbox Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Rectangle Width="16" Height="16">
    <Rectangle.Fill>
      <DrawingBrush>
        <DrawingBrush.Drawing>
          <DrawingGroup>
            <DrawingGroup.Children>
              <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
              <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M10,7L14,7 14,10 10,10 10,14 7,14 7,10 3,10 3,7 7,7 7,3 10,3z" />
              <GeometryDrawing Brush="#FF388A34" Geometry="F1M13,8L9,8 9,4 8,4 8,8 4,8 4,9 8,9 8,13 9,13 9,9 13,9z" />
            </DrawingGroup.Children>
          </DrawingGroup>
        </DrawingBrush.Drawing>
      </DrawingBrush>
    </Rectangle.Fill>
  </Rectangle>
</Viewbox>

I want include it to my WPF project and use in my application. I don't want copy-paste code and I don't want to modify these files wrapping them with ResourceDictionary.

Is there any way how I can achieve that?

astef
  • 8,575
  • 4
  • 56
  • 95

4 Answers4

3

Option 1: Use .svg icons

Visual Studio Image Library contains SVG versions of all icons. You can simply use them with the help of SharpVectors nuget package (more details in this answer).

  1. Add SVG files to your project for example in a "Icons" subfolder and set their Build Action property to Resource
  2. Use it in your code:
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <Button Height="100">
                <svgc:SvgViewbox Source="/Icons/Checkmark_16x.svg"/>
            </Button>
            <ContentControl Height="100">
                <svgc:SvgViewbox Source="/Icons/CollapseAll_16x.svg"/>
            </ContentControl>
            <Label Height="100">
                <svgc:SvgViewbox Source="/Icons/Refresh_16x.svg"/>
            </Label>
        </StackPanel>
    </Grid>
</Window>

Option 2: Use .xaml icon files directly

  1. Add icon.xaml files to your project for example in a "Icons" subfolder and set their Build Action property to Resource
  2. Create a converter:
using System;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;

namespace TestApp
{
    class XamlIconToViewBoxConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var stream = System.Windows.Application.GetResourceStream(new Uri((string)parameter, UriKind.Relative)).Stream;
            var viewBox = XamlReader.Load(stream) as Viewbox;

            // Optional:
            // we set Height and Width to "Auto" to let an icon scale, because in the <icon>.xaml file its size is explicitly specified as 16x16
            viewBox.Height = double.NaN;
            viewBox.Width = double.NaN;

            return viewBox;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
  1. Use the icons in your XAML code in the following way:
<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:XamlIconToViewBoxConverter x:Key="XamlIconToViewBoxConverter"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Height="100" Content="{Binding Converter={StaticResource XamlIconToViewBoxConverter},ConverterParameter='/Icons/EditInput_16x.xaml'}"/>
            <ContentControl Height="100" Content="{Binding Converter={StaticResource XamlIconToViewBoxConverter},ConverterParameter='/Icons/ErrorSquiggleInactive_16x.xaml'}"/>
            <Label Height="100" Content="{Binding Converter={StaticResource XamlIconToViewBoxConverter},ConverterParameter='/Icons/FirstOfFourColumns_16x.xaml'}"/>
        </StackPanel>
    </Grid>
</Window>

Also there is a similar answer but instead of ViewBox it works with icons specified in XAML as DrawingImage

Option 3: Use a ResourceDictonary

  1. Add Icons.xaml ResourceDictionary to your project and copy paste content from XAML icons into it and assign a unique key to every icon using x:Key property
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- Checkmark_16x.xaml -->
    <Viewbox x:Key="Checkmark_16x" Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <Rectangle Width="16" Height="16">
            <Rectangle.Fill>
                <DrawingBrush>
                    <DrawingBrush.Drawing>
                        <DrawingGroup>
                            <DrawingGroup.Children>
                                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
                                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M6.0003,9.1717L2.7073,5.8787 0.000300000000000189,8.5857 0.000300000000000189,8.8277 6.0003,14.8277 16.0003,4.8287 16.0003,4.5857 13.2933,1.8787z" />
                                <GeometryDrawing Brush="#FF388A34" Geometry="F1M14.707,4.707L6,13.414 1.293,8.707 2.707,7.293 6,10.586 13.293,3.293z" />
                            </DrawingGroup.Children>
                        </DrawingGroup>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Viewbox>

    <!-- Refresh_16x.xaml -->
    <Viewbox x:Key="Refresh_16x" Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <Rectangle Width="16" Height="16">
            <Rectangle.Fill>
                <DrawingBrush>
                    <DrawingBrush.Drawing>
                        <DrawingGroup>
                            <DrawingGroup.Children>
                                <GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
                                <GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M16,8C16,12.411 12.411,16 8,16 3.589,16 0,12.411 0,8 0,6.597 0.384,5.212 1.088,4L0,4 0,0 8,0 8,8 4,8C4,10.206 5.794,12 8,12 10.206,12 12,10.206 12,8 12,6.656 11.331,5.41 10.21,4.666L9.377,4.112 11.592,0.78 12.425,1.333C14.663,2.822,16,5.314,16,8" />
                                <GeometryDrawing Brush="#FF00529C" Geometry="F1M15,8C15,11.859 11.859,15 8,15 4.14,15 1,11.859 1,8 1,6.076 1.801,4.292 3.121,3L1,3 1,1 7,1 7,7 5,7 5,4.002C3.766,4.931 3,6.401 3,8 3,10.757 5.243,13 8,13 10.757,13 13,10.757 13,8 13,6.321 12.164,4.763 10.764,3.833L11.871,2.167C13.83,3.469,15,5.649,15,8" />
                            </DrawingGroup.Children>
                        </DrawingGroup>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Viewbox>

    <!-- CollapseAll_16x.xaml -->
    <Viewbox x:Key="CollapseAll_16x" Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <Rectangle Width="16" Height="16">
            <Rectangle.Fill>
                <DrawingBrush>
                    <DrawingBrush.Drawing>
                        <DrawingGroup>
                            <DrawingGroup.Children>
                                <GeometryDrawing Brush="#00f6f6f6" Geometry="M16,16H0V0H16Z" />
                                <GeometryDrawing Brush="#FFf6f6f6" Geometry="M1,15V2H2V0H16V14H14v1Z" />
                                <GeometryDrawing Brush="#FF424242" Geometry="M2,14H13V3H2ZM3,4h9v9H3ZM15,1V13H14V2H3V1Z" />
                                <GeometryDrawing Brush="#FF00539c" Geometry="M11,9H4V8h7Z" />
                            </DrawingGroup.Children>
                        </DrawingGroup>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Rectangle.Fill>
        </Rectangle>
    </Viewbox>
</ResourceDictionary>
  1. Use the icons in your XAML code in the following way:
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Icons.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Height="100" Content="{StaticResource Checkmark_16x}"/>
            <ContentControl Height="100" Content="{StaticResource CollapseAll_16x}"/>
            <Label Height="100" Content="{StaticResource Refresh_16x}"/>
        </StackPanel>
    </Grid>
</Window>
PolarBear
  • 1,117
  • 15
  • 24
  • How do I add them as a `StaticResource` using their file name instead of pasting them all inline? – Shimmy Weitzhandler Jan 01 '23 at 20:14
  • 1
    Hello @ShimmyWeitzhandler, thank you for the question. I'm not sure if it is possible. At least I was trying to find a way how to do it and couldn't figure it out. In XAML you can include ResourceDictionaries XAML files, but you can't include an arbitrary XAML file inside another XAML file. If you find a solution, please share I will update my answer. – PolarBear Jan 02 '23 at 20:05
  • What I ended up doing is adding those XAML files as a Page and loading them as a compoent using `Application.LoadComponent(Uri)`, and I also changed the converter to use `value` instead of `parameter` and defined an `enum` with all image keys: `Content="{Binding Source={x:Static views:ImageKey.ClassLibrary}, Converter={StaticResource imageConverter}}"/>` – Shimmy Weitzhandler Jan 06 '23 at 00:03
1

Just include them as simple file resources in your app. Then you can simply pass them through a XamlReader instance which will give you the instance of the root item (the ViewBox in this case). From there, do with it whatever you want.

https://blogs.msdn.microsoft.com/ashish/2007/08/14/dynamically-loading-xaml

In short, do this...

StreamReader mysr = new StreamReader("SomeFile.xaml");
ViewBox myLoadedViewBox = XamlReader.Load(mysr.BaseStream) as ViewBox;
Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • So, this controls will not be available in Designer, right? I'm afraid it's not an option. – astef Jan 06 '18 at 16:55
  • I don't think you have a choice. These aren't backed by classes so you can't load them up that way. The only identifier you have *is* the file name. How else would it be identified to the designer? – Mark A. Donohoe Jan 06 '18 at 17:01
  • To avoid the XAMLReader, you may be able to mark them as XAML resources here, then reference them by the ID you give them. Haven't tried this, but that's how you refer to resources anyway, so I can't imagine it wouldn't work. Not sure about design-time though. – Mark A. Donohoe Jan 06 '18 at 17:31
0

I ended up using a variation of @PolarBear's Option 2 above:

XAML (ImageKey refers to an enum):

<RadioButton 
    ToolTip="Container"
    VerticalContentAlignment="Bottom" 
    Content="{Binding Source={x:Static views:ImageKey.ClassLibrary}, Converter={StaticResource imageConverter}}"/>

Converter:

public class ImageConverter : IValueConverter
{
    private readonly Dictionary<ImageKey, object> _LoadedComponents = new();

    public object Convert(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture)
    {
        if (value is not ImageKey imageKey)
            return Binding.DoNothing;

        var resourceUrl = $"/images/{value}.xaml";
        var resourceUri = new Uri(resourceUrl, System.UriKind.Relative);
        if (!_LoadedComponents.TryGetValue(imageKey, out var component))
        {
            component = Application.LoadComponent(resourceUri);
            _LoadedComponents[imageKey] = component;
        }
        else
        {
            component = XamlReader.Parse(XamlWriter.Save(component));
        }


        return component;
    }

    public object ConvertBack(
        object value,
        Type targetType,
        object parameter,
        CultureInfo culture) =>

        throw new NotSupportedException();
}

public enum ImageKey
{
    None,
    CarIcon,
    UserIcon
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
-2

The other thing you can do is add a resource dictionary, then add references to the files you're interested in. In other words, you're not opening up each file and wrapping it, you're creating a single resource dictionary with references to all the resource files you're interested in.

<ResourceDictionary>
    [Ref to file 1.xaml]
    [Ref to file 2.xaml]
    [Ref to file 3.xaml]
</ResourceDictionary>

I don't have the exact syntax handy, but that should be easy to find as references are used all the time.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • Yes, that's an option. I planned to write a script which will generate one ResourceDictionary including all xaml files in it, but this references are better choise. I'll try to find a syntax... – astef Jan 06 '18 at 17:07
  • Are you sure there's a syntax for that? I can't find any example on the web – astef Jan 06 '18 at 17:11
  • Been a while, but I thought I remember there was some syntax where you specify the file, then the ID that file refers to (since the file doesn't have any ID itself.) This is different than a file which contains a resource dictionary. I'll let you know if I come across it. – Mark A. Donohoe Jan 06 '18 at 17:32
  • 1
    @MarkA.Donohoe, could please provide more details? Because as far as I know there is no syntax like this which would allow to reference files which are specified in the question. I think the solution you mention is described in details [here](https://msadowski.github.io/WPF-vector-graphics-tutorial/). However, it only works if an .xaml code is a `ResourceDictionary`. But the author's files are `Viewbox` and therefore your method doesn't work for them. – PolarBear Sep 02 '21 at 08:12