View Issue Details

IDProjectCategoryView StatusLast Update
0005167NoesisGUIUnitypublic2026-08-13 18:41
Reporterstonstad Assigned Tojsantos  
PrioritynormalSeverityblock 
Status feedbackResolutionopen 
Product Version4.0 
Summary0005167: 4.0 RC1. GatherDependenciesFromSourceFile. ArgumentException: The specified path is not of a legal form
Description

I'm unable to load or view any XAML files in 4.0 RC1. Every XAML control and screen errors out.

ArgumentException: The specified path is not of a legal form (empty).
System.IO.Path.InsecureGetFullPath (System.String path) (at <816f70bc2a9c4332b0bd119906a8d40e>:0)
System.IO.Path.GetFullPath (System.String path) (at <816f70bc2a9c4332b0bd119906a8d40e>:0)
NoesisXamlImporter.GatherDependenciesFromSourceFile (System.String path) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Editor/NoesisXamlImporter.cs:244)
UnityEngine.Debug:LogException(Exception)
NoesisXamlImporter:GatherDependenciesFromSourceFile(String) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Editor/NoesisXamlImporter.cs:252)
UnityEditor.AssetImporters.ScriptedImporter:GatherAllImportedAssetDependencyGUIDs(Type, String)

Attached Files
image.png (875,630 bytes)
BorderControlBorderless.cs (8,636 bytes)   
using Noesis;
using NoesisGUIExtensions;
using StellarConquest.Model.Universe;

namespace StellarConquest.Presentation.Unity.UI
{
    public partial class BorderControlBorderless : ContentControl
    {
        private Path _PanelBlur;
        private Path _PanelColor;

        private Path _DecorTop;
        private Path _DecorBottom;

        public event EventHandler Ready;

        private float _ContentWidth = 0;
        private float _ContentHeight = 0;

        public static readonly DependencyProperty PanelFillBrushProperty = DependencyProperty.Register("PanelFillBrush", typeof(Brush), typeof(BorderControlBorderless));
        public static readonly DependencyProperty PanelStrokeBrushProperty = DependencyProperty.Register("PanelStrokeBrush", typeof(Brush), typeof(BorderControlBorderless));
        public static readonly DependencyProperty BlurLayerProperty = DependencyProperty.Register("BlurLayer", typeof(FrameworkElement), typeof(BorderControlBorderless), new PropertyMetadata(null));

        private bool _TemplateApplied = false;

        public Brush PanelFillBrush
        {
            get { return GetValue(PanelFillBrushProperty) as Brush; }
            set { SetValue(PanelFillBrushProperty, value); }
        }

        public Brush PanelStrokeBrush
        {
            get { return GetValue(PanelStrokeBrushProperty) as Brush; }
            set { SetValue(PanelStrokeBrushProperty, value); }
        }

        public FrameworkElement BlurLayer
        {
            get { return (FrameworkElement)GetValue(BlurLayerProperty); }
            set { SetValue(BlurLayerProperty, value); }
        }

        public BorderControlBorderless()
        {
            Style = FindResource("BorderControlBorderless") as Style;
            Loaded += (sender, e) => Build();
            SizeChanged += (sender, e) => Build();

            PanelFillBrush = FindResource("DefaultUI.Brush.Bottom.Gradient") as Brush;
            PanelStrokeBrush = FindResource("DefaultUI.Brush.Border") as Brush;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (Template != null)
            {
                _PanelBlur = GetTemplateChild("_PanelBlur") as Path;
                _PanelColor = GetTemplateChild("_PanelColor") as Path;

                _DecorTop = GetTemplateChild("_DecorTop") as Path;
                _DecorBottom = GetTemplateChild("_DecorBottom") as Path;

                _TemplateApplied = true;
            }
        }

        public void Resize()
        {
            if (_TemplateApplied && Parent != null && View != null)
            {
                _ContentWidth = 0;
                _ContentHeight = 0;

                _PanelBlur.Data = null;
                _PanelColor.Data = null;

                UpdateLayout();

                Build();
            }
        }

        private void Build()
        {
            if (!_TemplateApplied)
                return;

            FrameworkElement content = (FrameworkElement)Content;

            float contentWidth;
            float contentHeight;

            if (content != null)
            {
                contentWidth = content.ActualWidth + content.Margin.Left + content.Margin.Right - 2;
                contentHeight = content.ActualHeight + content.Margin.Top + content.Margin.Bottom - 2;
            }
            else
            {
                contentWidth = Width;
                contentHeight = Height;
            }

            // we should compare via equals.  But here we do a less than comparison due to rounding
            // errors in the path data. The ideal solution is to recreate the path using integer values
            // to eliminate rounding errors
            if (contentWidth <= _ContentWidth && contentHeight <= _ContentHeight)
                return;

            // Noesis Bug FIX. Noesis sometimes resets ResourcesCommon which
            // affects TopBlurRadius and BottomBlurRadius. To prevent a zero value
            // from affecting background effects, here, we reapply the values.
            PlayerPreferences.Instance.ApplyBackgroundBlur();

            _ContentWidth = contentWidth;
            _ContentHeight = contentHeight;

            // primary dimensions
            float width = _ContentWidth;
            float height = _ContentHeight;

            // main panel
            float bevelSize = 10.0f;

            StreamGeometry geometry = new StreamGeometry();
            using (StreamGeometryContext ctx = geometry.Open())
            {
                // Top Left Bevel
                ctx.BeginFigure(new Point(bevelSize, 0.0f), true, true);
                ctx.LineTo(new Point(width - bevelSize, 0.0f), true, false);  // Top Edge

                // Top Right Bevel
                ctx.LineTo(new Point(width, bevelSize), true, false);
                ctx.LineTo(new Point(width, height - bevelSize), true, false);  // Right Edge

                // Bottom Right Bevel
                ctx.LineTo(new Point(width - bevelSize, height), true, false);
                ctx.LineTo(new Point(bevelSize, height), true, false);  // Bottom Edge

                // Bottom Left Bevel
                ctx.LineTo(new Point(0.0f, height - bevelSize), true, false);
                ctx.LineTo(new Point(0.0f, bevelSize), true, false);  // Left Edge

                ctx.Close();
            }

            _PanelBlur.Data = geometry;
            _PanelColor.Data = geometry;

            // top decor
            float thickness = 1.5f;
            StreamGeometry topGeometry = new StreamGeometry();
            using (StreamGeometryContext ctx = topGeometry.Open())
            {
                ctx.BeginFigure(new Point(0f, bevelSize), true, true);
                ctx.LineTo(new Point(bevelSize, 0f), true, false);
                ctx.LineTo(new Point(width - bevelSize, 0f), true, false);
                ctx.LineTo(new Point(width, bevelSize), true, false);
                ctx.LineTo(new Point(width - bevelSize, thickness), true, false);
                ctx.LineTo(new Point(bevelSize, thickness), true, false);
                ctx.Close();
            }
            _DecorTop.Data = topGeometry;

            // bottom decor
            StreamGeometry bottomGeometry = new StreamGeometry();
            using (StreamGeometryContext ctx = bottomGeometry.Open())
            {
                ctx.BeginFigure(new Point(0f, thickness - bevelSize), true, true);
                ctx.LineTo(new Point(bevelSize, thickness), true, false);
                ctx.LineTo(new Point(width - bevelSize, thickness), true, false);
                ctx.LineTo(new Point(width, thickness - bevelSize), true, false);
                ctx.LineTo(new Point(width - bevelSize, 0f), true, false);
                ctx.LineTo(new Point(bevelSize, 0f), true, false);
                ctx.Close();
            }
            _DecorBottom.Data = bottomGeometry;

            Ready?.Invoke(this, EventArgs.Empty);
        }

        public Geometry ContentGeometry
        {
            get { return _PanelColor.Data; }
        }

        public void SetAlertLevel(AlertLevelType value)
        {
            switch (value)
            {
                case AlertLevelType.Nominal:
                    PanelFillBrush = FindResource("DefaultUI.Brush.Bottom.Gradient") as Brush;
                    PanelStrokeBrush = FindResource("DefaultUI.Brush.Border") as Brush;
                    //DecorFillBrush = FindResource("DefaultUI.Brush.Decor.Gradient") as Brush;
                    break;
                case AlertLevelType.Medium:
                    PanelFillBrush = FindResource("DefaultUI.Brush.Bottom.Gradient.HighAlert") as Brush;
                    PanelStrokeBrush = FindResource("DefaultUI.Brush.Border.HighAlert") as Brush;
                    //DecorFillBrush = FindResource("DefaultUI.Brush.Decor.Gradient.HighAlert") as Brush;
                    break;
                case AlertLevelType.High:
                    PanelFillBrush = FindResource("DefaultUI.Brush.Bottom.Gradient.HighAlert") as Brush;
                    PanelStrokeBrush = FindResource("DefaultUI.Brush.Border.HighAlert") as Brush;
                    //DecorFillBrush = FindResource("DefaultUI.Brush.Decor.Gradient.HighAlert") as Brush;
                    break;
                default:
                    break;
            }
        }

    }
}
BorderControlBorderless.cs (8,636 bytes)   
PlatformAny

Relationships

related to 0005166 resolvedjsantos Package Errors Installing 4.0 RC1. 

Activities

stonstad

stonstad

2026-08-12 20:27

reporter   ~0012487

Full file path is

E:\Source\StellarConquest\StellarConquest.Presentation.Unity\Assets\User Interface\Controls\Border Control Borderless\BorderControlBorderless.cs

InvalidOperationException: Resource not found 'BorderControlBorderless'
Noesis.FrameworkElement.FindResource (System.Object key) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Proxies/FrameworkElementExtend.cs:33)
StellarConquest.Presentation.Unity.UI.BorderControlBorderless..ctor () (at Assets/User Interface/Controls/Border Control Borderless/BorderControlBorderless.cs:46)
(wrapper dynamic-method) System.Object.lambda_method(System.Runtime.CompilerServices.Closure)
Noesis.Extend.CreateInstance (System.IntPtr nativeType, System.IntPtr cPtr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/Extend.cs:6249)
UnityEngine.Debug:LogException(Exception)
NoesisUnity:OnUnhandledException(Exception) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/NoesisUnity.cs:371)
Noesis.Error:UnhandledException(Exception) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/Error.cs:18)
Noesis.Extend:CreateInstance(IntPtr, IntPtr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/Extend.cs:6269)
Noesis.GUI:Noesis_LoadComponent(HandleRef, String)
Noesis.GUI:LoadComponent(Object, String) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/NoesisGUI.cs:373)
StellarConquest.Presentation.Unity.UI.CrewControl:InitializeComponent() (at Assets/User Interface/Screens/Game/Overlay/Crew Control/CrewControl.cs:71)
StellarConquest.Presentation.Unity.UI.CrewControl:.ctor() (at Assets/User Interface/Screens/Game/Overlay/Crew Control/CrewControl.cs:54)
System.Object:lambda_method(Closure)
Noesis.Extend:CreateInstance(IntPtr, IntPtr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/Extend.cs:6249)
Noesis.GUI:Noesis_LoadStreamXaml(HandleRef, String)
Noesis.GUI:LoadXaml(Stream, String) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/API/Core/NoesisGUI.cs:321)
NoesisXaml:Load() (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Runtime/NoesisXaml.cs:25)
NoesisPostprocessor:Update() (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/package/Editor/NoesisPostprocessor.cs:125)
UnityEditor.EditorApplication:Internal_CallUpdateFunctions()

stonstad

stonstad

2026-08-12 20:29

reporter   ~0012488

image-2.png (75,564 bytes)   
image-2.png (75,564 bytes)   
jsantos

jsantos

2026-08-13 01:53

manager   ~0012495

There are a few errors that I have detected in our new Xaml importer. Please, try replace the following file.

Apart from this, there is something new in v4.0, that is still not documented (Unity and Unreal tutorials hasn't been updated yet).

In your classes, if you have dependencies to assets (for example, UserControl loading a XAML), they attribute AssetDependency must be used to make this dependency explicit.

For example:

[AssetDependency(Packages/com.noesis.noesisgui/Runtime/API/Shaders/Vignette.noesiseffect")]
public class VignetteEffect : ShaderEffect

Do you have many usercontrols (or shaders) in your project?

NoesisXamlImporter.cs (16,248 bytes)   
//#define DEBUG_IMPORTER

using UnityEditor;
using UnityEngine;
using UnityEditor.AssetImporters;
using UnityEngine.Video;
using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;

[ScriptedImporter(2, "xaml")]
class NoesisXamlImporter : ScriptedImporter
{
    [UnityEditor.InitializeOnLoadMethod]
    static void RegisterApplicationResourcesHash()
    {
        string projectPath = System.IO.Path.GetDirectoryName(Application.dataPath);
        string filename = System.IO.Path.Combine(projectPath, "Library", "Noesis", "ApplicationResources_Hash");

        if (File.Exists(filename))
        {
            try
            {
                string hash = File.ReadAllText(filename);
                AssetDatabase.RegisterCustomDependency("Noesis_ApplicationResources", Hash128.Parse(hash));
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }
        } 
    }

    [Serializable]
    struct Dependencies
    {
        public string hash;
        public long timestamp;
        public List<string> items;
    }

    static string HashFile(string path)
    {
        var hash = new Hash128();
        hash.Append(File.ReadAllText(path));
        return hash.ToString();
    }

    static string HashStr(string str)
    {
        var hash = new Hash128();
        hash.Append(str);
        return hash.ToString();
    }

    static string DependenciesPath()
    {
        string projectPath = System.IO.Path.GetDirectoryName(Application.dataPath);
        return System.IO.Path.Combine(projectPath, "Library", "Noesis", "Dependencies2");
    }

    static bool LoadDependencies(string path, ref Dependencies deps)
    {
        string folder = DependenciesPath();
        string filename = Path.Combine(folder, HashStr(path));

        if (File.Exists(filename))
        {
            try
            {
                string json = File.ReadAllText(filename);
                deps = JsonUtility.FromJson<Dependencies>(json);
                return true;
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }
        }

        return false;
    }

    static void SaveDependencies(string path, Dependencies deps)
    {
        try
        {
            string folder = DependenciesPath();
            System.IO.Directory.CreateDirectory(folder);

            string json = JsonUtility.ToJson(deps);
            File.WriteAllText(Path.Combine(folder, HashStr(path)), json);
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }
    }

    static IEnumerable<string> FindFonts(string uri)
    {
        int index = uri.IndexOf('#');
        if (index != -1)
        {
            string folder = uri.Substring(0, index);
            if (Directory.Exists(folder))
            {
                string family = uri.Substring(index + 1);
                var files = Directory.EnumerateFiles(folder).Where(s => IsFont(s));

                foreach (var font in files)
                {
                    using (FileStream file = File.Open(font, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        if (NoesisUnity.HasFamily(file, family))
                        {
                            yield return font;
                        }
                    }
                }
            }
        }
    }

    static Dependencies GetCachedDependencies(string path)
    {
        Dependencies deps = new Dependencies();
        bool cached = LoadDependencies(path, ref deps);

        long timestamp = 0;

        try
        {
            timestamp = File.GetLastWriteTime(path).Ticks;
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }

        if (cached)
        {
            if (timestamp == deps.timestamp)
            {
                return deps;
            }

            deps.timestamp = timestamp;

            string hash = HashFile(path);
            if (hash == deps.hash)
            {
                SaveDependencies(path, deps);
                return deps;
            }

            deps.hash = hash;
        }
        else
        {
            deps.timestamp = timestamp;
            deps.hash = HashFile(path);
        }

      #if DEBUG_IMPORTER
        Debug.Log($"=> Dependencies {path}");
      #endif

        deps.items = new List<string>();

        bool typesWithAttributeRequested = false;
        TypeCache.TypeCollection typesWithAttribute;

        using (FileStream file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            Noesis.GUI.GetXamlDependencies(file, path, (uri_, type) =>
            {
                try
                {
                    string assembly = Noesis.UriHelper.GetAssembly(uri_);
                    string uri = Noesis.UriHelper.GetPath(uri_);

                    if (assembly == "Noesis.GUI.Extensions")
                    {
                        uri = $"Packages/com.noesis.noesisgui/{uri}";
                    }

                    if (type == Noesis.XamlDependencyType.Filename)
                    {
                        deps.items.Add(uri);
                    }
                    else if (type == Noesis.XamlDependencyType.Font)
                    {
                        foreach (var font in FindFonts(uri))
                        {
                            if (!deps.items.Contains(font))
                            {
                                deps.items.Add(font);
                            }
                        }
                    }
                    else if (type == Noesis.XamlDependencyType.Class)
                    {
                        if (!typesWithAttributeRequested)
                        {
                            typesWithAttributeRequested = true;
                            typesWithAttribute = TypeCache.GetTypesWithAttribute<Noesis.AssetDependencyAttribute>();
                        }

                        foreach (var cachedType in typesWithAttribute)
                        {
                            if (cachedType.FullName == uri)
                            {
                                object[] attributes = cachedType.GetCustomAttributes(typeof(Noesis.AssetDependencyAttribute), false);
                                foreach (Noesis.AssetDependencyAttribute attribute in attributes)
                                {
                                    if (String.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(attribute.AssetPath)))
                                    {
                                        Debug.LogWarning($"[{cachedType.FullName}] Invalid AssetDependency '{attribute.AssetPath}'");
                                    }
                                    else
                                    {
                                        deps.items.Add(attribute.AssetPath);
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            });
        }

        SaveDependencies(path, deps);
        return deps;
    }

    static string[] GatherDependenciesFromSourceFile(string path)
    {
        NoesisUnity.InitCore();
        List<string> deps = new List<string>();

        try
        {
            foreach (var dep in GetCachedDependencies(path).items)
            {
                if (File.Exists(dep))
                {
                    deps.Add(dep);
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }

        return deps.ToArray();
    }

    static bool AddFont(string uri, ref List<NoesisFont> fonts)
    {
        NoesisFont font = AssetDatabase.LoadAssetAtPath<NoesisFont>(uri);

        if (font != null)
        {
            fonts.Add(font);
            return true;
        }

        return false;
    }

    static bool AddTexture(string uri, ref List<Texture> textures, ref List<Sprite> sprites)
    {
        if (AssetImporter.GetAtPath(uri) is TextureImporter textureImporter)
        {
            if (!AssetDatabase.GetLabels(textureImporter).Contains("Noesis"))
            {
                Debug.LogWarning($"{uri} is missing Noesis label");
            }

            if (textureImporter.textureType == TextureImporterType.Sprite &&
                textureImporter.spriteImportMode == SpriteImportMode.Single)
            {
                Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(uri);

                if (sprite != null)
                {
                    sprites.Add(sprite);
                    return true;
                }
            }
            else
            {
                Texture texture = AssetDatabase.LoadAssetAtPath<Texture>(uri);

                if (texture != null)
                {
                    textures.Add(texture);
                    return true;
                }
            }
        }

        return false;
    }

    static bool AddAudio(string uri, ref List<AudioClip> audios)
    {
        AudioClip audio = AssetDatabase.LoadAssetAtPath<AudioClip>(uri);

        if (audio != null)
        {
            audios.Add(audio);
            return true;
        }

        return false;
    }

    static bool AddVideo(string uri, ref List<VideoClip> videos)
    {
        VideoClip video = AssetDatabase.LoadAssetAtPath<VideoClip>(uri);

        if (video != null)
        {
            videos.Add(video);
            return true;
        }

        return false;
    }

    static bool AddRive(string uri, ref List<NoesisRive> rives)
    {
        NoesisRive rive = AssetDatabase.LoadAssetAtPath<NoesisRive>(uri);

        if (rive != null)
        {
            rives.Add(rive);
            return true;
        }

        return false;
    }

    static bool AddXaml(string uri, ref List<NoesisXaml> xamls)
    {
        NoesisXaml xaml = AssetDatabase.LoadAssetAtPath<NoesisXaml>(uri);

        if (xaml != null)
        {
            xamls.Add(xaml);
            return true;
        }

        return false;
    }

    static bool AddShader(string uri, ref List<NoesisShader> shaders)
    {
        NoesisShader shader = AssetDatabase.LoadAssetAtPath<NoesisShader>(uri);

        if (shader != null)
        {
            shaders.Add(shader);
            return true;
        }

        return false;
    }

    static void ScanDependencies(AssetImportContext ctx,
        out List<NoesisFont> fonts_, out List<Texture> textures_, out List<Sprite> sprites_,
        out List<AudioClip> audios_, out List<VideoClip> videos_, out List<NoesisRive> rives_,
        out List<NoesisXaml> xamls_, out List<NoesisShader> shaders_)
    {
        List<NoesisFont> fonts = new List<NoesisFont>();
        List<Texture> textures = new List<Texture>();
        List<Sprite> sprites = new List<Sprite>();
        List<AudioClip> audios = new List<AudioClip>();
        List<VideoClip> videos = new List<VideoClip>();
        List<NoesisRive> rives = new List<NoesisRive>();
        List<NoesisXaml> xamls = new List<NoesisXaml>();
        List<NoesisShader> shaders = new List<NoesisShader>();

        string filename = ctx.assetPath;

        try
        {
            if (HasExtension(filename, ".xaml"))
            {
                // Add dependency to code-behind, just the source, we don't need the artifact
                // Even if the file doesn't exist we add it to get a reimport first time code-behind is created
                ctx.DependsOnSourceAsset(filename + ".cs");
            }

            var dependencies = GetCachedDependencies(filename);

            foreach (var dep in dependencies.items)
            {
                // Register the dependency even if it doesn't currently exist. 
                // This ensures we are re-evaluated if it is imported later.
                ctx.DependsOnArtifact(dep);

                if (!String.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(dep)))
                {
                    if (!AddXaml(dep, ref xamls))
                    {
                        if (!AddTexture(dep, ref textures, ref sprites))
                        {
                            if (!AddFont(dep, ref fonts))
                            {
                                if (!AddAudio(dep, ref audios))
                                {
                                    if (!AddVideo(dep, ref videos))
                                    {
                                        if (!AddShader(dep, ref shaders))
                                        {
                                            AddRive(dep, ref rives);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogException(e);
        }

        fonts_ = fonts;
        textures_ = textures;
        sprites_ = sprites;
        audios_ = audios;
        videos_ = videos;
        rives_ = rives;
        xamls_ = xamls;
        shaders_ = shaders;
    }

    public override void OnImportAsset(AssetImportContext ctx)
    {
        NoesisUnity.InitCore();

      #if DEBUG_IMPORTER
        Debug.Log($"=> Import {ctx.assetPath}");
      #endif

        NoesisXaml xaml = (NoesisXaml)ScriptableObject.CreateInstance<NoesisXaml>();
        xaml.uri = ctx.assetPath;
        xaml.content = System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(ctx.assetPath));

        // Add dependencies
        List<NoesisFont> fonts;
        List<Texture> textures;
        List<Sprite> sprites;
        List<AudioClip> audios;
        List<VideoClip> videos;
        List<NoesisRive> rives;
        List<NoesisXaml> xamls;
        List<NoesisShader> shaders;

        ScanDependencies(ctx, out fonts, out textures, out sprites, out audios, out videos, out rives, out xamls, out shaders);

        xaml.xamls = xamls.Select(x => new NoesisXaml.Xaml { uri = AssetDatabase.GetAssetPath(x), xaml = x }).ToArray();
        xaml.textures = textures.Select(x => new NoesisXaml.Texture { uri = AssetDatabase.GetAssetPath(x), texture = x }).ToArray();
        xaml.sprites = sprites.Select(x => new NoesisXaml.Sprite { uri = AssetDatabase.GetAssetPath(x), sprite = x }).ToArray();
        xaml.audios = audios.Select(x => new NoesisXaml.Audio { uri = AssetDatabase.GetAssetPath(x), audio = x }).ToArray();
        xaml.videos = videos.Select(x => new NoesisXaml.Video { uri = AssetDatabase.GetAssetPath(x), video = x }).ToArray();
        xaml.rives = rives.Select(x => new NoesisXaml.Rive { uri = AssetDatabase.GetAssetPath(x), rive = x }).ToArray();
        xaml.fonts = fonts.Select(x => new NoesisXaml.Font { uri = AssetDatabase.GetAssetPath(x), font = x }).ToArray();
        xaml.shaders = shaders.Select(x => new NoesisXaml.Shader { uri = AssetDatabase.GetAssetPath(x), shader = x} ).ToArray();

        // Depends on global dictionary
        ctx.DependsOnCustomDependency("Noesis_ApplicationResources");

        ctx.AddObjectToAsset("XAML", xaml);
        ctx.SetMainObject(xaml);
    }

    static bool HasExtension(string filename, string extension)
    {
        return filename.EndsWith(extension, StringComparison.OrdinalIgnoreCase);
    }

    static bool IsFont(string filename)
    {
        return HasExtension(filename, ".ttf") || HasExtension(filename, ".otf") || HasExtension(filename, ".ttc");
    }
}
NoesisXamlImporter.cs (16,248 bytes)   
jsantos

jsantos

2026-08-13 02:29

manager   ~0012498

The AssetDependency attribute was needed to optimize XAML imports in complex projects with a large number of assets. Previously, we had to “guess” the dependencies across the entire asset database, including packages.

The main issue with AssetDependency (apart from having to add it manually) is that the path becomes invalid if the referenced asset is moved. You will get a warning in that case, but it’s still not ideal.

We are still investigating whether this can be improved

stonstad

stonstad

2026-08-13 15:00

reporter   ~0012501

I'll retry with the updated file. I do not have any shader effects, but I do have plenty of user controls. It isn't clear to me what dependencies my customer user controls might have because they are very simple. They inherit from UserControl.

jsantos

jsantos

2026-08-13 15:25

manager   ~0012503

Thanks!

If for example, your UserControl loads a XAML, you have a dependency to that asset.

Are your UserControls loading XAMLs?

stonstad

stonstad

2026-08-13 15:31

reporter   ~0012505

I uninstalled 3.2.12.
Closed Unity.
Deleted \Library\Noesis.
Installed 4.0.

NoesisGUI v4.0.0-rc1 successfully installed
UnityEngine.Debug:Log (object)
NoesisUpdater:CheckVersion () (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/Editor/NoesisUpdater.cs:46)
UnityEditor.EditorApplication:Internal_CallUpdateFunctions ()

I now have 15 Noesis errors instead of 999:

Assets/User Interface/Screens/Game/Overlay/Spedometer/SpedometerControl.xaml(15,9): Xaml not found 'Assets/User Interface/Controls/Radial Range Control/RadialRangeControl.xaml'
UnityEngine.Debug:LogError (object,UnityEngine.Object)
NoesisPostprocessor/<>c__DisplayClass4_0:<Update>b__1 (string) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/Editor/NoesisPostprocessor.cs:120)
Noesis.GUI:LoadComponent (object,string) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/Runtime/API/Core/NoesisGUI.cs:373)
StellarConquest.Presentation.Unity.UI.RadialRangeControl:InitializeComponent () (at Assets/User Interface/Controls/Radial Range Control/RadialRangeControl.cs:208)
StellarConquest.Presentation.Unity.UI.RadialRangeControl:.ctor () (at Assets/User Interface/Controls/Radial Range Control/RadialRangeControl.cs:203)
(wrapper dynamic-method) object:lambda_method (System.Runtime.CompilerServices.Closure)
Noesis.Extend:CreateInstance (intptr,intptr) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/Runtime/API/Core/Extend.cs:6249)
Noesis.GUI:LoadComponent (object,string) (at E:/Source/StellarConquest/StellarConquest.Utilities/Noesis/4.0 RC1/Runtime/API/Core/NoesisGUI.cs:373)
StellarConquest.Presentation.Unity.UI.SpedometerControl:InitializeComponent () (at Assets/User Interface/Screens/Game/Overlay/Spedometer/SpedometerControl.cs:48)
StellarConquest.Presentation.Unity.UI.SpedometerControl:.ctor () (at Assets/User Interface/Screens/Game/Overlay/Spedometer/

I'll apply the patch and try dependencies.

stonstad

stonstad

2026-08-13 15:36

reporter   ~0012506

Last edited: 2026-08-13 18:28

If for example, your UserControl loads a XAML, you have a dependency to that asset.
Ah, OK. That's like everything. 100+ files.

Here is how I am implemented AssetDependency. Is this correct?

  [AssetDependency("Assets/User Interface/Screens/Game/Overlay/Menu/TitleMenuControl.xaml")]
  public partial class TitleMenuControl : OverlayControl
  {
      public event EventHandler<ScreenSelectedEventArgs> ScreenSelected;
      private FrameworkElement _Root;
      private ListBox _ListBox;

      public TitleMenuControl()
      {
          Name = nameof(TitleMenuControl);
          Initialized += OnInitialized;
          InitializeComponent();
      }

      protected override void InitializeComponent()
      {
          base.InitializeComponent();

          GUI.LoadComponent(this, "Assets/User Interface/Screens/Game/Overlay/Menu/TitleMenuControl.xaml");
          _Root = Content as FrameworkElement;
          _ListBox = _Root.FindName("_ListBox") as ListBox;
      }

Is there a significant cost to do a FIle.Exists for a XAML file of the same name beside the CS file?

** I haven't applied the patch yet because things are working, and I was thinking it is diagnostically useful to show what works and doesn't incrementally.

stonstad

stonstad

2026-08-13 15:41

reporter   ~0012507

Last edited: 2026-08-13 18:28

OK, so the class definition now needs the same thing as the XAML file? Or can I just reference the XAML file and declared dependencies are used? Example, see below:

<UserControl
    x:Class="StellarConquest.Presentation.Unity.UI.GameScreen"
    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" mc:Ignorable="d"
    xmlns:noesis="clr-namespace:NoesisGUIExtensions"
    xmlns:local="clr-namespace:StellarConquest.Presentation.Unity.UI">
    <noesis:Xaml.Dependencies>
        <noesis:Dependency Source="/Assets/User Interface/Controls/Decor Line Control/DecorLineControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Designer/Overlay/Parts/PartsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Access/AccessControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Building/BuildControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Color/ColorControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Crew Control/CrewControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Commerce Report/CommerceReportControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Communications/CommunicationsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Diplomacy/DiplomacyControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Events/EventControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Examine/ExamineControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Exchange/ExchangeControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Hitpoints/HitpointsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/InputControls/InputControls.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/InterstellarMarket/InterstellarMarketControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Market/MarketControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Menu/ContextMenuControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Menu/OptionsMenuControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Menu/TitleMenuControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Messages/MessagesControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Missions/MissionsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Navigation/NavigationControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Notification/NotificationControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Scan/ScanControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Settings/SettingsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Targeting/TargetDetailControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Shipyard/ShipyardControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Spedometer/SpedometerControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Tutorial/TutorialControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Weapons/WeaponsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/NPC/NPCNeedsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/NPC/NPCBiographyControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/NPC/NPCScheduleControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/NPC/NPCPrioritiesControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Overlay/Weather/WeatherControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Transient/Transfer Colonists/TransferColonistsControl.xaml"/>
        <noesis:Dependency Source="/Assets/User Interface/Screens/Game/Transient/Transfer Resources/TransferResourcesControl.xaml"/>
    </noesis:Xaml.Dependencies>
stonstad

stonstad

2026-08-13 15:50

reporter   ~0012508

I am definitely able to get further with the upgrade! I can see other users being impacted by stale data in \library\noesis -- the errors thrown are definitely confusing.

I started working through the dependencies. If I am understanding the scope of the change, it is a significant body of work.

I have to roll back the upgrade to complete other work. I'll try again when I have a large block of time to address the dependencies requirement.

OK to close issue.

jsantos

jsantos

2026-08-13 18:39

manager   ~0012512

You don't need the <noesis:Xaml.Dependencies> block at all if everything is configured properly. However, it could be a good workaround for now.

The problem is the following. Imagine we have this XAML:

<Page>
  <StackPanel>
    <Image Source="A.png" />
    <MyControl />
  </StackPanel>
</Page>

When we parse that XAML, we need to analyze all its dependencies. For example, in this case, the dependency onA.png is very clear and easy to resolve. But what about MyControl? I don't know where the .cs file associated with that type is, so finding it requires searching the database.

In 3.2, this process was automatic, but it was very slow because it searched for files everywhere, not only in /Assets, but also inside each package. It also has many restrictions and conventions rules.

That's why we added the new AssetDependency metadata. it is very efficient and very flexible.

That said, you're right: I could simplify this and bring back automatic dependency detection as a fallback, like before, but make it more efficient.

I'll try to have something ready for the next version.

jsantos

jsantos

2026-08-13 18:40

manager   ~0012513

Here is how I am implemented AssetDependency. Is this correct?

Yes, the same uri string is duplicated. You could unify it. So at the end, it is just finding all yours GUI.LoadComponent( and adding a metadata.

But wait a bit, let me see if we can auto-detect simple scenarios as said in my previous comment.

Issue History

Date Modified Username Field Change
2026-08-12 20:24 stonstad New Issue
2026-08-12 20:24 stonstad File Added: image.png
2026-08-12 20:24 stonstad File Added: BorderControlBorderless.cs
2026-08-12 20:27 stonstad Note Added: 0012487
2026-08-12 20:29 stonstad Note Added: 0012488
2026-08-12 20:29 stonstad File Added: image-2.png
2026-08-13 01:50 jsantos Relationship added related to 0005166
2026-08-13 01:50 jsantos Assigned To => jsantos
2026-08-13 01:50 jsantos Status new => assigned
2026-08-13 01:53 jsantos Note Added: 0012495
2026-08-13 01:53 jsantos File Added: NoesisXamlImporter.cs
2026-08-13 01:54 jsantos Status assigned => feedback
2026-08-13 02:29 jsantos Note Added: 0012498
2026-08-13 15:00 stonstad Note Added: 0012501
2026-08-13 15:00 stonstad Status feedback => assigned
2026-08-13 15:25 jsantos Note Added: 0012503
2026-08-13 15:25 jsantos Status assigned => feedback
2026-08-13 15:31 stonstad Note Added: 0012505
2026-08-13 15:31 stonstad Status feedback => assigned
2026-08-13 15:36 stonstad Note Added: 0012506
2026-08-13 15:41 stonstad Note Added: 0012507
2026-08-13 15:50 stonstad Note Added: 0012508
2026-08-13 15:51 stonstad Note Edited: 0012507
2026-08-13 18:28 jsantos Note Edited: 0012506
2026-08-13 18:28 jsantos Note Edited: 0012507
2026-08-13 18:39 jsantos Note Added: 0012512
2026-08-13 18:40 jsantos Note Added: 0012513
2026-08-13 18:41 jsantos Status assigned => feedback