XAML images as StaticResources throw an error in Windows Universal app - xaml

I have some vector icons I want to use throughout my windows universal application. I've added them to a resource dictionary, added the dictionary to a merged dictionary in app.xml, and tried using the resources in a page. The images actually show up in the editor preview window, but throw an error when I attempt to debug the application.
ManaSymbols.xaml
<?xml version="1.0" encoding="UTF-8"?>
<xaml:ResourceDictionary
xmlns:xaml="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="BlueManaSymbol" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Stretch="Uniform">
<Canvas Width="100" Height="100">
<Canvas.RenderTransform>
<TranslateTransform X="0" Y="0"/>
</Canvas.RenderTransform>
<Canvas>
<Canvas>
<Ellipse Width="100" Height="100" Fill="#FFC1D7E9"/>
</Canvas>
<Path Fill="#FF0D0F0F">
<Path.Data>
M 68.91657 83.71021 C 58.968869 94.408441 39.101769 93.367284 30.985174 80.955583 23.700186 70.338629 25.060135 55.965661 30.782622 44.970201 37.43962 31.696018 47.119757 19.99635 58.53257 10.53421 c -1.779599 7.330526 -2.971221 15.369494 0.5678 22.406733 4.282692 9.098857 12.226705 16.065144 15.7407 25.557642 2.950311 8.612064 0.582748 18.823437 -5.9245 25.211625 z m -0.129 -27.362 c -0.82319 -2.47714 -5.460323 -8.506164 -4.125006 -2.813916 -0.362035 4.191263 -1.937779 8.124558 -3.178994 12.106916 -0.269255 7.254198 11.007675 4.685165 9.226 -1.795 -0.01849 -2.611632 -0.877381 -5.133602 -1.922 -7.498 z
</Path.Data>
</Path>
</Canvas>
</Canvas>
</Viewbox>
</xaml:ResourceDictionary>
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/ManaSymbols.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Page.xaml
<ContentControl Content="{StaticResource BlueManaSymbol}" Grid.Row="0" Margin="70,0,0,0" Width="30" Height="30" />
Error throw when debugging:
A first chance exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in MagicStats.Windows.exe
WinRT information: Failed to assign to property 'Windows.UI.Xaml.Controls.ContentControl.Content'. [Line: 79 Position: 25]
The error message seems to indicate that the binding failed, but it actually works in the design window. Any thoughts are appreciated.

Related

WinUI3: Why does the top area of the NavigationView look different?

The default appearance of the NavigationView in WinUI3 Gallery or an app created with a template studio has a space at the top. However, it looks different in apps created with Visual Studio default templates.I don't think it's controlled by the ViewModel or anything else. Why does it look different?
<!--In Template studio or WinUI3 Gallery-->
<Page>
<Grid>
<NavigationView PaneDisplayMode="LeftCompact"/>
</Grid>
</Page>
<!--In My App created with Visual Studio default templates-->
<Page>
<Grid>
<NavigationView PaneDisplayMode="LeftCompact"/>
</Grid>
</Page>
In Template studio or WinUI3 Gallery
In My App created with Visual Studio default templates
Even if you modify the ShellPage of an app created with a Template Studio as follows, there is still a difference in appearance.
public sealed partial class ShellPage : Page
{
public ShellPage()
{
InitializeComponent();
}
}
<Page
x:Class="TemplateStudioApp.Views.ShellPage"
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">
<NavigationView PaneDisplayMode="LeftCompact"/>
</Page>
That space at the top is actually the AppTitleBar. This code should create the same look.
App.xaml
<Application
x:Class="WinUI3App.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUI3App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
<SolidColorBrush
x:Key="WindowCaptionBackground"
Color="Transparent" />
<SolidColorBrush
x:Key="WindowCaptionBackgroundDisabled"
Color="Transparent" />
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml.cs
<Window
x:Class="WinUI3App.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:local="using:WinUI3App"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid
x:Name="AppTitleBar"
Height="{Binding ElementName=NavigationViewControl, Path=CompactPaneLength}"
VerticalAlignment="Top"
Canvas.ZIndex="1"
IsHitTestVisible="True">
<Image
Width="16"
Height="16"
HorizontalAlignment="Left"
Source="/Assets/WindowIcon.ico" />
<TextBlock
x:Name="AppTitleBarText"
Margin="28,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
TextWrapping="NoWrap" />
</Grid>
<NavigationView
x:Name="NavigationViewControl"
Canvas.ZIndex="0"
DisplayModeChanged="NavigationView_DisplayModeChanged"
ExpandedModeThresholdWidth="1280"
IsBackButtonVisible="Visible"
IsSettingsVisible="True" />
</Grid>
</Window>
MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace WinUI3App;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
ExtendsContentIntoTitleBar = true;
SetTitleBar(this.AppTitleBar);
}
private void NavigationView_DisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
{
AppTitleBar.Margin = new Thickness()
{
Left = sender.CompactPaneLength * (sender.DisplayMode == NavigationViewDisplayMode.Minimal ? 2 : 1),
Top = AppTitleBar.Margin.Top,
Right = AppTitleBar.Margin.Right,
Bottom = AppTitleBar.Margin.Bottom
};
}
}
<Thickness x:Key="NavigationViewContentGridBorderThickness">1,1,0,0</Thickness>
<CornerRadius x:Key="NavigationViewContentGridCornerRadius">8,0,0,0</CornerRadius>
<Thickness x:Key="NavigationViewContentMargin">0,48,0,0</Thickness>
<Thickness x:Key="NavigationViewHeaderMargin">56,34,0,0</Thickness>
<Thickness x:Key="NavigationViewPageContentMargin">56,24,56,0</Thickness>
In Template Studio, these resources from Styles/Thickness.xaml were the solution.

NavigationView does not show PaneHeader when IsPaneToggleButtonVisible is false

I have an issue with the NavigationView control in my UWP app. When I set the IsPaneToggleButtonVisible to false, my PaneHeader collapses too. Offically this bug was solved, am I doing something wrong?
<NavigationView PaneDisplayMode="Left" IsPaneToggleButtonVisible="False" IsBackButtonVisible="Collapsed" OpenPaneLength="200" IsSettingsVisible="False" Height="923">
<NavigationView.PaneHeader>
<Image x:Name="Header" Source="/Assets/Header.png" Stretch="UniformToFill" Margin="0,0,0,0" HorizontalAlignment="Left" Width="216" Height="53"/>
</NavigationView.PaneHeader>
<NavigationView/>
Based on this thread, it mentions
This issue was addressed in #1083, which has now been successfully
released as Microsoft.UI.Xaml v2.2.190731001-prerelease.
This means the bug has solved in the Windows UI Library version of NavigationView, so if you want to show your PaneHeader, you need to install the Microsoft.UI.Xaml nuget package and then add <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls"/> to your Application.Resources.
.App.xaml:
<Application>
<Application.Resources>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</Application.Resources>
</Application>
.MainPage.xaml:
<Page
......
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
>
<Grid>
<muxc:NavigationView PaneDisplayMode="Left" IsPaneToggleButtonVisible="False" IsBackButtonVisible="Collapsed" OpenPaneLength="200" IsSettingsVisible="False" Height="923">
<muxc:NavigationView.PaneHeader>
<Image x:Name="Header" Source="Assets/StoreLogo.png" Stretch="UniformToFill" Margin="0,0,0,0" HorizontalAlignment="Left" Width="53" Height="53"/>
</muxc:NavigationView.PaneHeader>
</muxc:NavigationView>
</Grid>
</Page>

How to use a path data from a resource dictionary in UWP

This is trivial thing but yet it does not work.
I have something like this (it is in its own folder)
<ResourceDictionary>
<Path x:Key="Test"
Stroke="Black"
Fill="Gray"
Data="M 10,100 C 10,300 300,-200 300,100" />
</ResourceDictionary>
Now I want to use it
<Page>
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergeDictionaries>
<ResourceDictionary Source="MyFolder/MyResourceDictionary.xaml/>
</ResourceDictionary.MergeDictionaries>
</ResourceDictionary>
</Page.Resources>
<ContentPresenter Content="{StaticResource Test}"/>
<Page/>
This will throw an exception, but I don't understand why. Exactly the same scenario in wpf works fine.
What about this solution?
Declare your GeometryData
<x:String x:Key="TestPathGeomerty">M 10,100 C 10,300 300,-200 300,100</x:String>
And use Path, instead ContentPresenter
<Path Data="{StaticResource TestPathGeomerty}"
Fill="Red"/>
The Path.Data property is of type Geometry so define it as a Geometry instead of a string
<Geometry x:Key="TestPathGeomerty">M 10,100 C 10,300 300,-200 300,100</Geometry>
<Path Data="{StaticResource TestPathGeomerty}"
Fill="Red"/>
In WPF, you can share the same instance within multiple controls. Unfortunately this is not possible in UWP.
The only solution that is guaranteed to work in UWP, is to define a DataTemplate in your resource containing the icon.
It is also better to use PathIcon instead of Path. PathIcon makes use of the Foreground property that will be inherited from your parent controls.
Here's an example on how to share Data paths for icons that will automatically scale (by using a Viewbox).
<Page.Resources>
<DataTemplate x:Key="MagnifyingGlassPathIconCT">
<Viewbox Stretch="Uniform">
<PathIcon Data="M44,12 C32,12 22,22 22,34 22,46 32,56 44,56 56,56 66,46 66,34 66,22 56,12 44,12z M44,0 C63,0 78,15 78,34 78,53 63,68 44,68 40,68 36.5,67.5 33,66 L32.5,66 14,90 0,79.5 18,55.5 17,55 C13,49 10,42 10,34 10,15 25,0 44,0z" />
</Viewbox>
</DataTemplate>
</Page.Resources>
<StackPanel Padding="40" HorizontalAlignment="Left">
<!-- Plain icon -->
<ContentPresenter
Width="40"
Height="40"
ContentTemplate="{StaticResource MagnifyingGlassPathIconCT}"
Foreground="Purple" />
<!-- Icon with a border -->
<Border
Width="40" Padding="7"
Height="40"
BorderBrush="Black"
BorderThickness="2">
<ContentPresenter ContentTemplate="{StaticResource MagnifyingGlassPathIconCT}" Foreground="Red" />
</Border>
<!-- Icon in a normal Button -->
<Button
Width="40"
Height="40"
ContentTemplate="{StaticResource MagnifyingGlassPathIconCT}"
Foreground="RoyalBlue" />
<!-- Icon in an AppBarButton -->
<AppBarButton
Width="40"
ContentTemplate="{StaticResource MagnifyingGlassPathIconCT}"
Foreground="Black"
Label="Search" />
</StackPanel>
For a solution that lets you define it in a Style, try writting an attached property like this:
public static string GetPathData(DependencyObject obj)
{
return (string)obj.GetValue(PathDataProperty);
}
public static void SetPathData(DependencyObject obj, string value)
{
obj.SetValue(PathDataProperty, value);
}
public static readonly DependencyProperty PathDataProperty =
DependencyProperty.RegisterAttached("PathData", typeof(string), typeof(ElementExtensions), new PropertyMetadata(null, (d, e) =>
{
if (d is Path path)
{
Binding b = new Binding { Source = e.NewValue };
path.SetBinding(Path.DataProperty, b);
}
}));
And now you can define a style like so:
<Style x:Key="BasePathStyle" TargetType="Path">
<Setter Property="e:ElementExtensions.PathData" Value="M 10,100 C 10,300 300,-200 300,100" />
</Style>
And then use it like so:
<Path Style="{StaticResource BasePathStyle}" />

How to create WPF UserControl having a Canvas as the "main panel"?

I want to create a UserControl having a Canvas inside it. Then I can use it like this in XAML:
<Window ...>
<Grid>
<local:MyCanvasLikeControl>
<Path .../>
<Path .../>
<Polygon.../>
</local:MyCanvasLikeControl>
</Grid>
</Window>
I have tried some things, but always get this error: "Property Content can only be set once". I know of ControlTemplates and such, but could not find my way by reading the docs by myself.
My goal is to have an equivalent to this:
<Window ...>
<Grid ...>
<Border ....>
<Canvas ...>
<Path ...>
<Path ...>
<Polygon ...>
</Canvas>
</Border>
</Grid>
</Window>
But moving the "Border / Canvas" to a UserControl named "MyCanvasLikeControl"
It's the same type of scenario as if you to stuff a bunch of elements of any other type into a control that would produce the same error like for example;
<UserControl>
<Grid/>
<StackPanel/>
<Canvas/>
<!-- you get the idea -->
</UserControl>
It's just telling you that you need to specify a parent capable of holding your content to act as the single content of its parent, which in this case would be your control. So to fix this, it's luckily simple;
<local:MyCanvasLikeControl>
<Grid>
<Path .../>
<Path .../>
<Polygon.../>
</Grid>
</local:MyCanvasLikeControl>
Or switch Grid with Canvas or whatever you want so long as it can host children elements. Hope this helps.
EDIT:
Ok, so I think the definition of requirement may have been a bit over-complicated in its explanation. If I'm understanding you correctly then here's why you're breaking.
You have your first layer of elements that contain your UserControl, but the way you're trying to insert content into it isn't the way that works, you'll need to provide that ability by specifying how you wish to allow it to be added to your control via a ContentPresenter or ContentControl etc. So instead it would be more like this inside your external UserControl;
<Border ....>
<Canvas ...>
<ContentPresenter/> or <ContentControl/> etc.
</Canvas>
</Border>
So then you can inject that stuff in there the way you want.
<Window ...>
<Grid>
<local:MyCanvasLikeControl>
<local:MyCanvasLikeControl.Content>
<Path .../>
<Path .../>
<Polygon.../>
</local:MyCanvasLikeControl.Content>
</local:MyCanvasLikeControl>
</Grid>
</Window>
Make sense?

Application Level Resources XAML

How do I create an application level resources in XAML? I'm developing a Windows Phone 8 app btw.
Below I have a rectangle, I want to create a resources that can be used to change the colour of the rectangle when tapped:
<Rectangle Fill="#FFF4F4F5"
HorizontalAlignment="Left"
Height="100"
Stroke="Black"
VerticalAlignment="Top"
Width="100"
x:Name="pad1"
Tap="pad1_tap"
/>
I have read some similar posts that say to use:
<Application.Resources>
<!-- Resources Here !-->
</Application.Resources>
... but there is no object under the name 'Application' within my application. When trying to use 'Application.Resources' I get an error stating: The member Resources is not recognized or accesssible.
The Application object is in your app.xaml file. But it's useful only if you want your resource to be shared by the whole application. If you need it only in one page, you can declare your resource in the PhoneApplicationPage element:
<phone:PhoneApplicationPage.Resources>
<!-- your resource -->
</phone:PhoneApplicationPage.Resources>
You'd need to import a mscorlib library and then refer to your resources using StaticResource keyword, like so:
<Application
x:Class="AppClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone">
<Application.Resources>
<sys:Int32 x:Key="Test">80</sys:Int32>
</Application.Resources>
</Application>
Usage:
<Rectangle Fill="#FFF4F4F5"
HorizontalAlignment="Left"
Height="100"
Stroke="Black"
VerticalAlignment="Top"
Width="{StaticResource Test}"
x:Name="pad1"
Tap="pad1_tap"
/>