How to include raw xaml file which is not a resource?
Asked Answered
B

4

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?

Bellinger answered 6/1, 2018 at 16:47 Comment(0)
D
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>
Douai answered 2/9, 2021 at 8:28 Comment(3)
How do I add them as a StaticResource using their file name instead of pasting them all inline?Marismarisa
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.Douai
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}}"/>Marismarisa
S
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;
Shantishantung answered 6/1, 2018 at 16:51 Comment(3)
So, this controls will not be available in Designer, right? I'm afraid it's not an option.Bellinger
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?Shantishantung
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.Shantishantung
M
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
}
Marismarisa answered 6/1, 2023 at 0:3 Comment(0)
S
-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.

Shantishantung answered 6/1, 2018 at 17:4 Comment(4)
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...Bellinger
Are you sure there's a syntax for that? I can't find any example on the webBellinger
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.Shantishantung
@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. However, it only works if an <icon>.xaml code is a ResourceDictionary. But the author's files are Viewbox and therefore your method doesn't work for them.Douai

© 2022 - 2024 — McMap. All rights reserved.