Fragensteller
Silverlight Page dynamisch anlegen

Frage
-
Hallo Community,
ich weiß nicht, ob so eine Anforderung schon einmal bestand, aber für meinen Fall wäre es sehr wichtig.
Ich habe eine Anwendung, in der ich zwischen Silverlight Pages hin- und hernavigiere. Diese Pages müssten zur Laufzeit eingelesen werden und nicht schon statisch vor Ort liegen. Idel wäre es, wenn ich einfach eine Page auf den Server lege bzw. deren XML und diese dann einlese und auf Page caste. Ist sowas irgendwie möglich?
Wenn nicht, gibt es sonst Alternativen? Unter Umständen könnte ich mir auch die Pages im Code zusammenbauen, aber das wäre ziemlich umständlich...
Donnerstag, 18. August 2011 06:26
Alle Antworten
-
Servus Gozar,
das ganze erinnert mich sehr an den XamlCrunsher vom lieben Herrn Petzold (Autor meines sehr geschätzten WPF-Buches [und wahrscheinlich Xaml-Nerd]). Der XamlCruncher war/ist ein Programm, mit dem man einfach Xaml schreiben kann, welches dann eingelesen und angezeigt wird. Im Endeffekt also genau das, was du suchst.
Glücklicherweise mag der Herr Petzold natürlich auch Silverlight (gibt ja auch dort Xaml) und daher hat er sich die Mühe gemacht und hat den XamlCruncher auf Silverlight "portiert". Finden kannst du den ganzen Spaß hier: XamlCruncher (inklusive SourceCode und Erläuterung).
Und um das Geheimnis aufzulösen: Es dreht sich im Endeffekt alles um die statische Klasse XamlReader, die, wie der Name schon sagt, Xaml einlesen kann und das ganze dann sogar noch verwertet. Diese Klasse bietet die statische Methode Load(String XamlINPUT) und damit solltest du dann auch glücklich werden können :)
Mfg CK
Donnerstag, 18. August 2011 22:24 -
Sehr sehr geil! Vielen Dank für die Hilfe! Das macht es wesentlich einfacher! :)
Eine Frage habe ich aber diesbezüglich noch: Wenn ich jetzt das XML als Page eingelesen habe. Wie kann ich dorthin navigieren innerhalb meiner Anwendung. Bis jetzt mach ich es über die NavigateUri eines HyperlinkButton. Aber wie mache ich das jetzt wenn ich eine Page im Code habe?
Freitag, 19. August 2011 06:33 -
Guten Morgen,
da gibt es verschiedene Möglichkeiten. Du sagst, dass du das XAML als Page einließt. Sind es denn wirklich Pages, oder doch nur UserControls? Ich meine damit, dass es im Silverlight SDK richtig die Klasse Page gibt, von der "richtige Seiten" erben können. In einem normalen Projekt (mit normalem Template) erben die großen Dinger, in denen du dein Content rein steckst, allerdings von UserControl.
Um weiter helfen zu können, wäre es jetzt wichtig, zu wissen, ob deine "Seiten" von System.Windows.Controls.Page erben, oder ob sie dies von System.Windows.Controls.UserControl tun.
Freitag, 19. August 2011 08:44 -
System.Windows.Control.Page :) Es sind reine Pages. Bis jetzt. Im Moment sehen sie so aus:
<?xml version="1.0" encoding="utf-8" ?> <navigation:Page 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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" mc:Ignorable="d" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" Title="NEU"> <Grid x:Name="LayoutRoot"> <sdk:Label x:Name="lblHead" Content="NEU" /> </Grid> </navigation:Page>
Freitag, 19. August 2011 08:49 -
Hmm da hast du dir ja was recht "schweres" rausgesucht. Ich habe lange überlegt, welche Vorgehensweise denn wohl a) geht und b) noch ein möglichst gutes Design mit sich bringt und ich denke, dass die Antwort darauf das INavigationContentLoader-Interface ist.
Allgemein ist das Problem ja, dass du einem Frame (in der sich ja sicherlich deine Seiten befinden?) zwar sagen kannst, dass du gerne Navigieren möchtest (Navigate-Methode), aber dafür eine Uri brauchst. Also ist das Problem CLR-Objekt -> darauf zeigende Uri.
Grundsätzlich ist es ja so, dass das Frame ja durch irgendeine Logik auch die übergebene Uri auflößt und entsprechend ein resultierendes Objekt zurück gibt. Und diese Logik bringt ein Objekt einer Klasse, welche das schon genannte INavigationContentLoader implementiert und der Eigenschaft ContentLoader des Frame-Objektes zugewiesen wird.
Ich weiß ja nicht, wo du die Source für das plain XAML deiner Pages liegen hast. Aber falls du es zum beispiel irgendwo auf einem Webserver liegen hast, dann könntest du (das Interface wird Asynchron implementiert) innerhalb der entsprechenden Methoden das Xaml runterladen, Parsen lassen und dann als Objekt verpackt in einem LoadResult-Objekt zurück geben.
Beachte bitte, dass diese Vorgehensweise erst ab SL4 funktioniert. Vorher wollte noch niemand ein so dynamisches Frame ;)
Ich habe mich ein wenig schwer getan entsprechende Links (außer die der MSDN) zu finden. Hier allerdings kannst du einen NavigationContentLoader finden, der aus Uris direkt Typen macht (man übergibt also für die Navigation im Frame nur noch die Typnamen - keine Uris mehr). Da könntest du nachschauen, wie man sinnvollerweise das Interface implementiert.
Ich hoffe, dass dir das erst einmal weiter geholfen hat.
Cya later - happy coding!
CK~Freitag, 19. August 2011 10:14 -
Das klingt auf jeden Fall schon mal sehr gut! Leider funktioniert es bei mir noch nicht. Das Problem bei mir ist, dass ich die XAML Seiten gerne ins Webprojekt auslagern würde, sodass sie wirklich zur Laufzeit geladen werden und auf dem Server liegen und damit auch editierbar sind. Hast du da zufällig auch eine Lösung? :)Freitag, 19. August 2011 12:17
-
Das klingt auf jeden Fall schon mal sehr gut! Leider funktioniert es bei mir noch nicht. Das Problem bei mir ist, dass ich die XAML Seiten gerne ins Webprojekt auslagern würde, sodass sie wirklich zur Laufzeit geladen werden und auf dem Server liegen und damit auch editierbar sind. Hast du da zufällig auch eine Lösung? :)
Hallo Gozar,wenn du nur die XAML - Dateien "auslagerst" hast du ja das Problem das keinerlei CodeBehind mitgeliefert wird.
Damit würdest du die möglichen "Anpassungen" an den Seiten aber einigermaßen einschränken.
Eine Idee wäre nur so eine Art von LayoutConfig - Files in's WebProjekt zu übernehmen und diese dan auf eine im SL - Projekt enthaltene Page anzuwenden. So kannst du auch sicher sein das nicht möglicher "unerwünschter"/"schadhafter" Code deine Anwendung stört.
Daniel
http://www.silverlight-community.de - deutsche Community mit Tutorials, Blogs und UsergroupsFreitag, 19. August 2011 15:09 -
wenn du nur die XAML - Dateien "auslagerst" hast du ja das Problem das keinerlei CodeBehind mitgeliefert wird.
Damit würdest du die möglichen "Anpassungen" an den Seiten aber einigermaßen einschränken.
Eine Idee wäre nur so eine Art von LayoutConfig - Files in's WebProjekt zu übernehmen und diese dan auf eine im SL - Projekt enthaltene Page anzuwenden. So kannst du auch sicher sein das nicht möglicher "unerwünschter"/"schadhafter" Code deine Anwendung stört.
Hallo,
Codebehind soll auch nicht angepasst werden können. Die XAML Seiten beinhalten lediglich Text, Bilder und vielleicht Verlinkungen... Mehr nicht.
Wie meinst du das mit der LayoutConfig?
Montag, 22. August 2011 06:20 -
Wie meinst du das mit der LayoutConfig?
Du gibts dem User nur die Möglichkeit bestimmte Layout - Werte zu ändern. Beispielsweise die Position und Größe der Controls oder Hintergrundfarben/ Bilder etc.Dafür erstellst du eine Konfigurationsdatei (XML, INI, ...) Nun brauchst du nur ein Objekt welches deine LayoutConfig - Informationen aufnimmt (diese Datei einliest und auswertet). Wenn du dann deine eigentliche UI (im XAML) an die Propertys deines LayoutConfig-Objektes bindest, kann der User nur die Dinge anpassen die du erlaubt hast. Außerdem bleibst du schön im Standard und brauchst du keine "Tricks".
Daniel
http://www.silverlight-community.de - deutsche Community mit Tutorials, Blogs und UsergroupsMontag, 22. August 2011 15:11 -
Das Problem liegt daran, dass die XAML Seiten auf dem Server liegen sollen. Also nicht im Silverlight Projekt sondern im WebProjekt. Daher kommt eine Exception bei GetType:
public class ContentLoader : INavigationContentLoader { public IAsyncResult BeginLoad(Uri targetUri, Uri currentUri, AsyncCallback userCallback, object asyncState) { if (targetUri != null) { ContentLoaderAsyncResult result = new ContentLoaderAsyncResult(asyncState); Type t = Type.GetType(GetTypeNameFromUri(targetUri), false, true); object instance = Activator.CreateInstance(t); result.Result = instance; userCallback(result); return result; } return null; } public bool CanLoad(Uri targetUri, Uri currentUri) { if (targetUri != null) { string typeName = GetTypeNameFromUri(targetUri); <strong>Type t = Type.GetType(typeName, false, true);</strong> if (t == null) return false; ConstructorInfo defaultConstructor = t.GetConstructor(new Type[0]); if (defaultConstructor == null) return false; return true; } return false; } public void CancelLoad(IAsyncResult asyncResult) { return; } public LoadResult EndLoad(IAsyncResult asyncResult) { return new LoadResult(((ContentLoaderAsyncResult)asyncResult).Result); } private string GetTypeNameFromUri(Uri uri) { if (uri != null) { if (!uri.IsAbsoluteUri) uri = new Uri(new Uri(App.AppDomain, UriKind.Absolute), uri.OriginalString); return Uri.UnescapeDataString(uri.AbsolutePath.Substring(1)); } return null; } } internal class ContentLoaderAsyncResult : IAsyncResult { public object Result { get; set; } public ContentLoaderAsyncResult(object asyncState) { this.AsyncState = asyncState; this.AsyncWaitHandle = new ManualResetEvent(true); } #region IAsyncResult Members public object AsyncState { get; private set; } public System.Threading.WaitHandle AsyncWaitHandle { get; private set; } public bool CompletedSynchronously { get { return true; } } public bool IsCompleted { get { return true; } } #endregion }
Die Stelle ist fett markiert in der CanLoad. Also t ist an der Stelle dann null und es geht nicht weiter... Muss ich da irgendwie den XAMLReader einbauen?Dienstag, 30. August 2011 19:21