2009-12-13 15 views
8

Ich habe diese Markup-ErweiterungAngeben DataTemplate.DataType mit einer benutzerdefinierten Typ Erweiterung

public class NullableExtension : TypeExtension 
{ 
    public NullableExtension() { 
    } 

    public NullableExtension(string type) 
     : base(type) { 
    } 

    public NullableExtension(Type type) 
     : base(type) { 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) { 
     Type basis = (Type)base.ProvideValue(serviceProvider); 
     return typeof(Nullable<>).MakeGenericType(basis); 
    } 
} 

, die eine Nullable-Version eines anderen Typs zu schaffen, ausgelegt ist. Es funktioniert wie erwartet, wenn es in "normalem" XAML verwendet wird. Zum Beispiel, wie in

<SomeControl DataContext="{My:Nullable System:Int32}"/> 

(unter der Annahme, dass Ihr das XML-Namespace für den C# Namespace Halten der Erweiterung und in ähnlicher Weise für System definiert ist). Der Datenkontext für das Steuerelement wird wie erwartet auf System.Type für Nullable<int> gesetzt.

Allerdings, wenn ich diese Erweiterung verwenden, um zu versuchen, und stellen Sie die DataType Eigenschaft eines DataTemplate wie

<DataTemplate DataType="{My:Nullable System:Int32}"> 
    <TextBlock ... /> 
</DataTemplate> 

mir gesagt wurde, durch den Compiler, dass

Ein Schlüssel für ein Wörterbuch kann nicht vom Typ 'System.Windows.Controls.Primitives.TextBlock' sein. Nur String, TypeExtension und StaticExtension unterstützt werden. "

und

" Nein Konstruktor für Typ 'NullableExtension' hat 1 Parameter.

Weiß jemand, warum nur diese drei Methoden (und nicht einmal Unterklassen von TypeExtension, wie meins ist) erlaubt sind? Was ist das Besondere an der Verarbeitung des XAML an diesem Punkt? Und gibt es eine andere Möglichkeit, dies zu erreichen (Datenvorlagenauswahl basierend auf Typen, die Nullwerte enthalten können), ohne auf DataTemplateSelector zurückzugreifen?

Antwort

11

Ich bin wirklich auf Ihre Frage eingegangen, und hier ist, was ich gefunden habe.

F: Warum nur jene drei (String, TypeExtension und StaticExtension) sind erlaubt?

A: Durch Entwurf. Wenn Sie eine benutzerdefinierte Markup-Erweiterung schreiben könnten, die als Schlüssel in einem Wörterbuch verwendet werden soll, welche Nebenwirkungen würde dies verursachen? Bedenken Sie, dass Sie Binding als Wert von DataType haben ... Ich bin mir ziemlich sicher, dass Sie Dutzende von Problemen hinzufügen können, die sich auf die dynamische Natur von Dictionary-Schlüsseln beziehen.

Q: Was an diesem Punkt über die Verarbeitung des XAML besonders?

Ein. An diesem Punkt haben Sie BAML-Erstellung. Das Problem kommt von der internen Klasse BamlRecordWriter, aber die Nachricht beschreibt nicht das tatsächliche Problem. Wenn Sie eine benutzerdefinierte Markup-Erweiterung als DataType angeben, wird des untergeordneten Elements der DataTemplate verwendet und überprüft, ob sie aus String, TypeExtension oder StaticExtension zuweisbar ist (siehe BamlRecordWriter.WriteElementStart()). Tatsächlich.Nicht Ihre Erweiterung (die TypeExtension zugewiesen werden kann), sondern das erste untergeordnete Element (das nicht zuweisbar ist). Jetzt haben Sie diese seltsame "kann nicht vom Typ" Sache sein. Obwohl es wie ein Fehler von BamlRecordWriter aussieht, haben sie es meiner Meinung nach absichtlich verlassen. Bis Sie nicht benutzerdefinierte Markup-Erweiterung als DataType-Wert verwenden können, wer kümmert sich um Fehlermeldung?

Q: Gibt es eine andere Möglichkeit, diese (Datenvorlage Auswahl an Typen basieren, die NULL-Werte zulässt sein kann) zu erreichen, ohne auf einen Datatemplateselector zurückgreifen?

A: Ja, irgendwie. Zunächst einmal können Sie Standard haben TypeExtension für Sie all die schmutzige Arbeit tun:

<DataTemplate DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}"> 
</DataTemplate> 

Aber in den meisten Fällen (wenn auch nicht die ganze Zeit) werden Sie die Templating Ergebnisse nicht sehen. Warum? Jetzt kommt es zu Boxregeln für NULL-fähige Typen. Wenn Sie einen nicht nullbaren Nullwerttyp boxen, wird der Werttyp selbst eingeschlossen, nicht der System.Nullable, der den Werttyp umschließt. Der Standard-Vorlagenselektor sucht daher nach DataTemplate mit dem Datentyp T nicht von Nullable<T>.

Ich kann nicht genau verstehen, Problem, das Sie versuchen, mit NULL-Erweiterung zu lösen, aber Sie möchten möglicherweise NULL in Ihrem eigenen Ref-Typ zu wickeln, schreiben Sie eine DataTemplate für den Wrapper und verwenden Sie DataTemplate.Triggers, um das Erscheinungsbild zu wählen. Nun, das sieht aus wie neu erfunden Datenvorlage Selektor:) ...

NB: Ich bin kein MS-Typ, und meine Ergebnisse basieren auf Reflektor und meine eigene Erfahrung (die nicht so groß ist, wie ich es gerne hätte zu sein alt text http://www.kolobok.us/smiles/standart/blush2.gif). Hoffe ich könnte auf jeden Fall helfen :).

Prost

+0

Danke. Ich weiß, dass es gefährlich sein könnte, aber ich würde meinen, dass es möglich wäre, den Typ des Wertes zu validieren, der DataType zugewiesen wurde, nämlich ein System.Type. Ich versuche nicht, DataType einen beliebigen Wert zuzuweisen, sondern nur einen Type. Interessant über den BAML-Parser. Wie für das Boxen Problem, das ist ein guter Punkt, obwohl in dem Fall, den ich habe, werde ich explizit eine Nullable eher als einen verpackten Wert und haben andere Gerüste an Ort und Stelle, um das "das Richtige tun" für meine Bedürfnisse. Sieht so aus, als müsste ich mit einem Workaround fortfahren. Danke für die Hilfe. –

+0

Ugh, ich vergesse immer, dass Kommentare hier keine extra Zeilenumbrüche enthalten. Sorry das oben Formatieren ist so eklig. –

+1

Die Dinge haben sich offensichtlich seit dem geschrieben, und während es aussieht wie 'DataType = "{x: Type TypeName = System: Nullable \' 1 [[System.Int32]]} "sollte im August 2013 unter funktionieren. NET 4.0, tut es nicht. Der einzige Weg, um dies zu umgehen, ist eine Art MarkupExtension. – Dan

1

Die Syntax

DataType="{x:Type TypeName=System:Nullable`1[[System.Int32]]}"> 

nicht für benutzerdefinierte Typen zu funktionieren scheint :(

Eigentlich eine andere Art und Weise ist eine Basis nicht-generischen Typ zu erstellen. Erste Set Datenvorlage für diesen Typ und binden Sie ContentPresenter.Content an die Eigenschaft, die Objekt T hält. Erstellen Sie dann andere Datenvorlagen für T

1

dies funktionieren sollte ...

<DataTemplate DataType="{x:Type System:Nullable`1[System.Int32]}"> 
</DataTemplate> 
+0

Um eine bessere Antwort zu schreiben, sollten Sie einen Kontext mit der Lösung bereitstellen (warum funktioniert es? Welche Quellen haben Sie verwendet?) – Dhara

2

ich eine böse Abhilfe für dieses gefunden. Aus irgendeinem Grund hat @Anvaka Recht: Die DataType-Eigenschaft lässt keine benutzerdefinierte MarkupExtension zu. Aber es wird Ihnen ermöglichen, eine StaticResource der benutzerdefinierten MarkupExtension zu verwenden.

Nehmen Sie Ihre MarkupExtension, fügen Sie einen öffentlichen Standardkonstruktor hinzu. Erstellen Sie dann eine Instanz Ihrer Erweiterung in den Ressourcen und legen Sie die Eigenschaften direkt fest. Boom, es braucht es. Das Folgende ist ähnlich wie das, was Sie tun müssten

<My:Nullable x:Key="Foo" Type="{x:Type System:Int32}"/> 
<DataTemplate DataType="{StaticResource Foo}"> 
    <TextBlock ... /> 
</DataTemplate> 
Verwandte Themen