Wie kann ich eine Page aus einem anderen WPF-Projekt aufrufen, wenn ein gegenseitiger Verweis existiert?

1 Antwort

Ich weiß zwar nicht ganz auf was du mit dem Code Beispiel hinaus willst aber, nein einen zyklische abhängigkeitsfehler kann man nicht umgehen.

Wobei ich dabei die Frage hätte warum dein Lib Projekt deine Hauptanwendung referenziert. Auch wenn es nur ein Lib Projekt zur Isolierung von bestimmten Komponenten ist sollte keine direkte Abhängigkeit zur Anwendung selber bestehen.

Falls das so erforderlich ist kannst du immernoch die Page und weitere Komponenten auf die das zutrifft in ein Extraprojekt verschieben.

LG Suiram1

Woher ich das weiß:Hobby – Ich programmiere seit 3 Jahren mit C#

EchoTech 
Beitragsersteller
 19.02.2025, 00:39

Doch, dass kann man umgehen. Habe mir eine Funktion geschrieben, die jetzt funktioniert. Trotzdem danke

Suiram1  19.02.2025, 06:13
@EchoTech

Das würde mich jetzt mal interessieren wie man eine zyklische Abhängigkeit mit einer einfachen Methode umgehen kann. Könntest du den Code von dieser eventuell teilen?

EchoTech 
Beitragsersteller
 21.02.2025, 09:11
@Suiram1

ich habe eine Klasse gemacht die die page in dem Projekt mit der passenden DLL aufruft. Meine WPF-Anwendung ist komplett custom, hat seinen eigenen UI-Mgr usw. deswegen wirst du wahrscheinlich den code nicht zu 100 % verstehen. 

aber hier die Methode:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows;
using uUIMgr;
using uAllgemein;

namespace uArtikel
{
  public static class PageLoader
  {
    private static readonly Dictionary<string, Assembly> _cachedAssemblies = new();
    private static readonly Dictionary<string, Type> _cachedTypes = new();
    private static readonly Dictionary<string, object> _cachedInstances = new();

    static PageLoader()
    {
      PreloadAssemblies();
    }

    private static void PreloadAssemblies()
    {
      string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
      foreach (string file in Directory.GetFiles(baseDirectory, "*.dll"))
      {
        try
        {
          string assemblyName = Path.GetFileNameWithoutExtension(file);
          if (!_cachedAssemblies.ContainsKey(assemblyName))
          {
            _cachedAssemblies[assemblyName] = Assembly.LoadFrom(file);
          }
        }
        catch (Exception ex)
        {
          Main.Log.LogError(ex, "uAllgemein", "PageLoader", nameof(PreloadAssemblies));
        }
      }
    }

    public static void OpenPage(string namespaceName, string pageName, params object[] parameters)
    {
      try
      {
        string fullTypeName = $"{namespaceName}.{pageName}";

        if (!_cachedInstances.TryGetValue(fullTypeName, out object instance))
        {
          instance = CreateInstance(namespaceName, pageName, parameters);
          if (instance is not OtecPage otecPage)
          {
            ShowErrorMessage($"Die Klasse '{fullTypeName}' ist keine gültige OtecPage.");
            return;
          }
          _cachedInstances[fullTypeName] = otecPage;
        }

        UI.Show((OtecPage)instance, true);
      }
      catch (Exception ex)
      {
        Main.Log.LogError(ex, "uAllgemein", "PageLoader", nameof(OpenPage));
      }
    }

    public static object CallFunction(string namespaceName, string className, string methodName, params object[] parameters)
    {
      try
      {
        object classInstance = CreateInstance(namespaceName, className);
        if (classInstance == null)
        {
          ShowErrorMessage($"Die Klasse '{namespaceName}.{className}' konnte nicht instanziiert werden.");
          return null;
        }

        MethodInfo method = classInstance.GetType().GetMethod(methodName);
        if (method == null)
        {
          ShowErrorMessage($"Die Methode '{methodName}' wurde in '{namespaceName}.{className}' nicht gefunden.");
          return null;
        }

        return method.Invoke(classInstance, parameters);
      }
      catch (Exception ex)
      {
        Main.Log.LogError(ex, "uAllgemein", "PageLoader", nameof(CallFunction));
        return null;
      }
    }

    private static object CreateInstance(string namespaceName, string className, params object[] parameters)
    {
      string fullTypeName = $"{namespaceName}.{className}";

      if (!_cachedAssemblies.TryGetValue(namespaceName, out Assembly assembly))
      {
        string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{namespaceName}.dll");
        if (!File.Exists(assemblyPath))
        {
          ShowErrorMessage($"Die Datei '{assemblyPath}' wurde nicht gefunden.");
          return null;
        }

        assembly = Assembly.LoadFrom(assemblyPath);
        _cachedAssemblies[namespaceName] = assembly;
      }

      if (!_cachedTypes.TryGetValue(fullTypeName, out Type type))
      {
        type = assembly.GetType(fullTypeName);
        if (type == null)
        {
          ShowErrorMessage($"Typ '{fullTypeName}' nicht gefunden.");
          return null;
        }
        _cachedTypes[fullTypeName] = type;
      }

      return Activator.CreateInstance(type, parameters);
    }

    private static void ShowErrorMessage(string message)
    {
      MessageBoxService.ShowError(message);
    }
  }

  internal static class MessageBoxService
  {
    public static void ShowError(string message)
    {
      clsMessageboxService msgBox = new clsMessageboxService();
      msgBox.ShowMessageBox(
        Main.Multilanguage.getText(message),
        Main.Multilanguage.getText("Fehler"),
        clsMessageboxService.MSGType.OK,
        clsMessageboxService.MSGIcon.Critical);
    }
  }
}

so rufe ich die Page dann auf:

PageLoader.OpenPage("H070_Artikel", "pgArtikel", SelectedArtikel?.ArtikelArt ?? "", PersonID ?? 0, SelectedArtikel?.ArtikelID ?? 0);

EchoTech 
Beitragsersteller
 21.02.2025, 09:31
@Suiram1

habe sie nochmal überarbeitet. Ist jetzt sauberer:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows;
using uUIMgr;
using uAllgemein;


namespace uArtikel
{
    public static class PageLoader
    {
        private static readonly Dictionary<string, ClassCacheEntry> CachedClasses = new();


        private struct ClassCacheEntry
        {
            public Assembly Assembly { get; }
            public string DLLNamespace { get; }
            public object KlassenInstanz { get; }
            public Type KlassenType { get; }


            public ClassCacheEntry(Assembly assembly)
            {
                Assembly = assembly;
                DLLNamespace = string.Empty;
                KlassenInstanz = null;
                KlassenType = null;
            }


            public ClassCacheEntry(Assembly assembly, string dllNamespace, object instance, Type type)
            {
                Assembly = assembly;
                DLLNamespace = dllNamespace;
                KlassenInstanz = instance;
                KlassenType = type;
            }
        }


        static PageLoader()
        {
            PreloadAssemblies();
        }


        private static void PreloadAssemblies()
        {
            string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            foreach (var file in Directory.GetFiles(baseDirectory, "*.dll"))
            {
                try
                {
                    string assemblyName = Path.GetFileNameWithoutExtension(file);
                    if (!CachedClasses.ContainsKey(assemblyName))
                    {
                        Assembly assembly = Assembly.LoadFrom(file);
                        CachedClasses[assemblyName] = new ClassCacheEntry(assembly);
                    }
                }
                catch 
                {
                   //
                }
            }
        }


        public static void OpenPage(string namespaceName, string pageName, params object[] parameters)
        {
            try
            {
                string fullTypeName = $"{namespaceName}.{pageName}";


                if (!CachedClasses.TryGetValue(fullTypeName, out ClassCacheEntry cacheEntry) || cacheEntry.KlassenInstanz == null)
                {
                    cacheEntry = CreateInstance(namespaceName, pageName, parameters);
                    if (cacheEntry.KlassenInstanz is not OtecPage otecPage)
                    {
                        return;
                    }
                    CachedClasses[fullTypeName] = cacheEntry;
                }


                UI.Show((OtecPage)cacheEntry.KlassenInstanz, true);
            }
            catch
            {
                //
            }
        }


        public static object CallFunction(string namespaceName, string className, string methodName, params object[] parameters)
        {
            try
            {
                string fullTypeName = $"{namespaceName}.{className}";


                if (!CachedClasses.TryGetValue(fullTypeName, out ClassCacheEntry cacheEntry) || cacheEntry.KlassenInstanz == null)
                {
                    cacheEntry = CreateInstance(namespaceName, className);
                    CachedClasses[fullTypeName] = cacheEntry;
                }


                MethodInfo method = cacheEntry.KlassenType?.GetMethod(methodName);
                if (method == null)
                {
                    return null;
                }


                return method.Invoke(cacheEntry.KlassenInstanz, parameters);
            }
            catch
            {
                return null;
            }
        }


        private static ClassCacheEntry CreateInstance(string namespaceName, string className, params object[] parameters)
        {
            string fullTypeName = $"{namespaceName}.{className}";


            if (!CachedClasses.TryGetValue(namespaceName, out ClassCacheEntry cacheEntry) || cacheEntry.Assembly == null)
            {
                string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{namespaceName}.dll");
                if (!File.Exists(assemblyPath))
                {
                    return default;
                }


                Assembly assembly = Assembly.LoadFrom(assemblyPath);
                cacheEntry = new ClassCacheEntry(assembly);
                CachedClasses[namespaceName] = cacheEntry;
            }


            Type type = cacheEntry.Assembly?.GetType(fullTypeName);
            if (type == null)
            {
                return default;
            }


            object instance = Activator.CreateInstance(type, parameters);
            return new ClassCacheEntry(cacheEntry.Assembly, namespaceName, instance, type);
        }


    }
}


Suiram1  21.02.2025, 15:43
@EchoTech

Ist natürlich ein interessanter Weg das mit der reflection API zu lösen.

Ich persöhnlich würde jedoch eher zusehen das, dass schon zur kompilierzeit geht.