Wie kann ich eine Page aus einem anderen WPF-Projekt aufrufen, wenn ein gegenseitiger Verweis existiert?
Hallo zusammen,
ich arbeite aktuell an einem WPF-Projekt und stehe vor folgendem Problem:
Ich möchte bzw. muss per Button-Klick eine Page aus einem anderen WPF-Bibliotheksprojekt aufrufen. Normalerweise könnte ich einfach einen Verweis auf das andere Projekt setzen, aber das funktioniert in meinem Fall nicht, da bereits ein Verweis in die entgegengesetzte Richtung existiert. Dadurch entsteht eine zyklische Abhängigkeit, die Visual Studio nicht erlaubt. Ich kann auch nicht sowas hier machen:
new H070_Artikel.pgArtikel(SelectedArtikel.ArtikelArt ?? "", SelectedArtikel.ArtikelID), true);
also die Page aus dem anderen Projekt aufrufen, da er sie nicht findet.
Gibt es eine Möglichkeit, die Page trotzdem zu instanziieren und anzuzeigen, ohne dass ich die Struktur meiner Projekte komplett umstellen muss?
Wie könnte man sowas machen? Aufruf über eine extra DLL zum Öffnen der Page oder etwas anderes?
Hat jemand eine Idee, wie man das lösen könnte?
Vielen Dank im Voraus!
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
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);
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);
}
}
}
Doch, dass kann man umgehen. Habe mir eine Funktion geschrieben, die jetzt funktioniert. Trotzdem danke