Design Time Feature-Implementierung in Silverlight-Toolkit
Einführung
Dies ist der zweite Beitrag von meinem Silverlight-Design-Time-Serie Merkmale. Die ersten post Design Time Features in Silverlight-Toolkit zeigte Design-Zeit-Funktionen von Silverlight-Toolkit -Steuerelemente. Dieser Beitrag erklärt, wie es umgesetzt wird. Hoffentlich Dieser Beitrag kann helfen, erklären unsere Quellcodes, die demonstrieren, wie Design-Zeit-Funktionen für Silverlight zu implementieren, und die Bereitstellung eines Rahmens und Source-Code-Leser können direkt in ihre eigenen Projekte zu nutzen.
Überblick
Download "Silverlight Toolkit - Binaries, Samples, Dokumentation, Unit-Tests und Quellen" von Dezember 2008 Release , offene SourceSilverlight.Controls.sln in Visual Studio, und wir werden sehen, dass es 12 Design-Projekte unter Design-Lösung Ordner, drei für jede Kontrolle Montage.
Nehmen Bedienelemente Projekt zum Beispiel, gibt es drei Design-Projekten:
- Controls.Design: Windows.Controls.Design.dll Builds, die Design-Zeit Funktionen zugreifen, indem Visual Studio und Expression Blend enthält.
- Controls.Expression.Design: Windows.Controls.Expression.Design.dll Builds, die Design-Zeit-Funktionen enthält Expression Blend für nur.
- Controls.VisualStudio.Design: Windows.Controls.VisualStudio.Design.dll Builds, die Design-Zeit-Funktionen enthält für Visual Studio nur.
Es gibt auch einen Ordner Design.Common das zwei Dateien enthält: Extensions.cs und MetadataBase.cs, die von allen Design-Projekten gemeinsam genutzt werden.
Es wird dringend empfohlen, dass Sie Justin Angel Blog-Eintrag lesen Silverlight Design Time Erweiterbarkeit : es bietet einen guten Hintergrund / Überblick über Silverlight Design-Zeit Erweiterbarkeit, auf denen unter Anwendung basiert.
Projekt
Alle 12 Design-Projekte folgen dem gleichen Muster Umsetzung, also werde ich einfach nutzen Control.Design Projekt, um sie alle zu erklären.
Legen Sie unter Silverlight.Controls.sln Source-Verzeichnis in Visual Studio zum ersten Mal, werden wir sehen Bildschirm wie folgt:
- Controls.Design hat ein Projekt die Abhängigkeit von Kontrollen Projekt. Diese Abhängigkeit stellt sicher, dass Bedienelemente Projekt zuerst gebaut wird, und Controls.Design hat einen Verweis auf Microsoft.Windows.Controls Montage von Steuerungen Projekt gebaut. Ein Design-Zeit der Montage immer verweist auf die Laufzeit der Montage bietet es Funktionen für die Design-Zeit.
- Controls.Design Referenzen Microsoft.Windows.Design & Microsoft.Windows.Design.Extensibility Baugruppen, die beide unter Verzeichnis% DevEnvDir% PublicAssemblies (c: Programm FilesMicrosoft Visual Studio 9.0Common7IDEPublicAssemblies auf meinem Laptop) sind. Ein Design-Zeit der Montage häufig Verweise diese beiden Baugruppen, die den Designer-Erweiterbarkeit Rahmenbedingungen zu schaffen. Ein Design-Zeit der Montage immer Referenzen System-Montage auch die System.ComponentModel-Namespace, wo viele der Metadaten-Attribute (wie CategoryAttribute, DescriptionAttribute) definiert sind, enthält.
- Controls.Design Referenzen System.Windows Montage. Wir können den Verweis in Controls.Design.csproj zu sehen:
<reference Include="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
<SpecificVersion> False </ SpecificVersion>
<HintPath> .. BinariesSystem.Windows.dll </ HintPath>
<Privat> False </ Private>
</ Reference>Bitte beachten Sie:
- Control.Design ist ein Windows-Klassenbibliothek-Projekt, nicht ein Silverlight-Projekt, und das erzeugte Microsoft.Windows.Controls.Design.dll ist ein NET-Assembly auf Desktop-innen Designer wie Visual Studio oder Expression Blend, kein Silverlight Montage laufen im Inneren laufen. ein Browser, auch wenn das Design Microsoft.Windows.Controls.Design.dll Zeit der Montage für die Silverlight-Laufzeit Montage Microsoft.Windows.Controls.dll ist.
- Die referenzierte System.Windows Montage ist eigentlich ein Silverlight-Montage (Version 2.0.5.0). System.Windows Assembly kann nicht von allen Design-Projekten für Silverlight-Steuerelemente benötigt. Wir brauchen sie hier vor allem wegen der GetMemberName <T> (Expression <Func <T, Objekt>> expr)-Methode in Extension.cs, dass wir mehr später in der Post zu diskutieren. Der Mix aus. NET und Silverlight Referenzen schafft alle möglichen interessanten Themen, wie wir bald sehen werden.
Unten ildasm Screenshot zeigt die gemischte Verweise auf NET und Silverlight Baugruppen durch Microsoft.Windows.Controls.Design.dll.:
- . Ver. 2:0:21024:1838 ist die Versionsnummer des Silverlight-Toolkits 2008 Dezember Release.
- . Ver. 3:5:0:0 ist die Versionsnummer für. NET Framework 3.5.
- . Ver. 2:0:0:0 ist die Versionsnummer für Silverlight 2.0.
- Wenn Sie zum ersten Last Controls.Design Projekt in Visual Studio, gibt es einen "der referenzierten Komponente 'System.Windows' konnte nicht gefunden werden" Warnung neben dem System.Windows Assemblyverweis in Projekt-Fenster und im Fenster Fehlerliste zu. Dies liegt daran, Controls.Design ist ein NET-Projekt statt Silverlight, und wir wissen nicht, wo Silverlight installiert ist (es ist unter C:. Programm FilesMicrosoft Silverlight2.0.31005.0 auf meinem Laptop, und unter C: Program Files (x86 ) Microsoft Silverlight2.0.31005.0 auf meinem Desktop). Die Lösung besteht darin, eine Pre-Build-Event, das Kommando .. CopySystemWindows.bat System.Windows.dll von Silverlight zu installieren Richtung kopiert .. Binaries haben. Also, wenn Sie das Projekt erstellt haben, wird die Warnung abzuschalten, wie wir gleich sehen werden.
- In Visual Studio-Screenshot oben gibt es ein weiteres Warnzeichen neben der Datei in Microsoft.Windows.Controls.XML Projekt Witwe, wenn Sie Controls.Design Projekt in Visual Studio laden zum ersten Mal. Diese Datei wird durch den Bau der abhängigen Steuerelemente Projekt generiert, weil in Controls Projekts Eigenschaften -> Registerkarte Build die Option "XML-Dokumentationsdatei" markiert ist, und hat einen Weg der "Binar ..
iesMicrosoft.Windows.Controls.XML ". Wenn Sie Controls.Design Projekt zu erstellen, wird die Warnung abzuschalten, wie wir gleich sehen werden. - Controls.Design Projekt hat ein Metadata.cs Datei, und Links zu Extension.cs und MetadataBase.cs in Design.Common Ordner. Alle 12 Design-Projekte haben diese drei Dateien. Wir werden sie später noch genauer zu besprechen.
Build Controls.Design Projekt oder die gesamte Lösung, können wir sehen:
- Alle Projekte gebaut fein mit 0 Fehler und 0 Warnungen.
- Die Warnzeichen neben System.Windows Referenz-und Microsoft.Windows.Controls.XML Datei verschwunden.
- Die Code-Analyse des Build-Prozesses dauert eine lange Zeit, und es erzeugt zwei Warnungen. Die erste, CA0060, ist ein weiteres Thema der gemischten. NET und Silverlight Referenzen und Code in Extension.cs. Sie können loszuwerden der Warnung durch das Kopieren System.dll und System.Core.dll von Silverlight-Installations-Verzeichnis zu Verzeichnis SourceBinaries und bauen wieder.
MetadataBase.cs
MetadataBase.cs, zusammen mit Metadata.cs, implementieren einen Rahmen für die Design-Zeit-Metadaten-Registrierung:
- DescriptionAttributes werden automatisch für die öffentliche Kontrolle Klassen (dh Unterklassen von FrameworkElement) und ihrer öffentlichen Eigenschaften von ihren "/ / / <summary>" XML-Dokumentation Kommentare im Source-Code generiert. (Gut zu kommentieren lohnt sich!
) - Um ein Steuerelement Klasse von einem Designer auszublenden, fügen Sie eine Zeile wie unten, um addAttributes Methode in das Projekt Metadata.cs Datei:
builder.AddCallback (typeof (TreeViewItem), b => b.AddCustomAttributes (neue ToolboxBrowsableAttribute (false))); - Um andere benutzerdefinierte Attribute für ein Steuerelement Klasse zu registrieren, fügen Sie eine XxxMetadata.cs Datei zum Projekt, wie die ViewboxMetadata.cs später besprochen.
Wenn Sie den Rahmen gefällt, können Sie MetadataBase.cs und Metadata.cs direkt in Ihr eigenes Projekt zu verwenden, vorbehaltlich der Microsoft Public License am Anfang all unserer Quelldateien enthalten.
MetadataBase.cs:
/ / (C) Copyright Microsoft Corporation. / / Diese Quelle unter der Microsoft Public License (Ms-PL) ist. / / Bitte beachten Sie http://go.microsoft.com/fwlink/?LinkID=131993 für weitere Einzelheiten. / / Alle sonstigen Rechte MetadataRegistrationBase { /// <summary> /// Build design time metadata attribute table. /// </summary> /// <returns>Custom attribute table.</returns> protected virtual AttributeTable BuildAttributeTable() { AttributeTableBuilder builder = new AttributeTableBuilder(); AddDescriptions(builder); AddAttributes(builder); AddTables(builder); return builder.CreateTable(); } /// <summary> /// Find all AttributeTableBuilder subclasses in the assembly /// and add their attributes to the assembly attribute table. /// </summary> /// <param name="builder">The assembly attribute table builder.</param> [SuppressMessage( "Microsoft.Design" , "CA1031:DoNotCatchGeneralExceptionTypes" , Justification = "Design time dll should not fail!" )] private static void AddTables(AttributeTableBuilder builder) { Debug.Assert(builder != null , "AddTables is called with null parameter!" ); Assembly asm = Assembly.GetExecutingAssembly(); foreach (Type t in asm.GetTypes()) { if (t.IsSubclassOf( typeof (AttributeTableBuilder))) { try { AttributeTableBuilder atb = (AttributeTableBuilder)Activator.CreateInstance(t); builder.AddTable(atb.CreateTable()); } catch (Exception e) { Debug.Assert( false , string .Format(CultureInfo.InvariantCulture, "Exception in AddTables method: {0}" , e)); } } } } /// <summary> /// Gets or sets the case sensitive resource name of the embedded XML file. /// </summary> protected string XmlResourceName { get; set; } /// <summary> /// Gets or sets the assembly FullName for types' assembly-qualified names. /// </summary> protected string AssemblyFullName { get; set; } /// <summary> /// Create description attribute from run time assembly xml file. /// </summary> /// <param name="builder">The assembly attribute table builder.</param> [SuppressMessage( "Microsoft.Design" , "CA1031:DoNotCatchGeneralExceptionTypes" , Justification = "Design time dll should not fail." )] private void AddDescriptions(AttributeTableBuilder builder) { Debug.Assert(builder != null , "AddDescriptions is called with null parameter!" ); if ( string .IsNullOrEmpty(XmlResourceName) || string .IsNullOrEmpty(AssemblyFullName)) { return ; } XDocument xdoc = XDocument.Load( new StreamReader( Assembly.GetExecutingAssembly().GetManifestResourceStream(XmlResourceName))); if (xdoc == null ) { return ; } foreach (XElement member in xdoc.Descendants( "member" )) { try { string name = ( string )member.Attribute( "name" ); bool isType = name.StartsWith( "T:" , StringComparison.OrdinalIgnoreCase); if (isType || name.StartsWith( "P:" , StringComparison.OrdinalIgnoreCase)) { int lastDot = name.Length; string typeName; if (isType) { typeName = name.Substring(2); } else { lastDot = name.LastIndexOf( '.' ); typeName = name.Substring(2, lastDot - 2); } typeName += AssemblyFullName; Type t = Type.GetType(typeName); if (t != null && t.IsPublic && t.IsClass && t.IsSubclassOf( typeof (FrameworkElement))) { string desc = member.Descendants( "summary" ).FirstOrDefault().Value; desc = desc.Trim(); desc = string .Join( " " , desc.Split( new char [] { ' ' , 't' , 'n' }, StringSplitOptions.RemoveEmptyEntries)); if (isType) { builder.AddCallback(t, b => b.AddCustomAttributes( new DescriptionAttribute(desc))); } else { string propName = name.Substring(lastDot + 1); PropertyInfo pi = t.GetProperty(propName); MethodInfo mi; if (pi != null && (mi = pi.GetSetMethod()) != null && mi.IsPublic) { builder.AddCallback(t, b => b.AddCustomAttributes(propName, new DescriptionAttribute(desc))); } } } } } catch (Exception e) { Debug.Assert( false , string .Format(CultureInfo.InvariantCulture, "Exception in AddDescriptions method: {0}" , e)); } } } /// <summary> /// Provide a place to add custom attributes without creating a AttributeTableBuilder subclass. /// </summary> /// <param name="builder">The assembly attribute table builder.</param> protected virtual void AddAttributes(AttributeTableBuilder builder) { } } } {/ / / <summary> / / / MetadataRegistration Klasse. / / / </ Summary> public class MetadataRegistrationBase {/ / / <summary> / / / Build Entwurfszeit Metadatenattribut Tisch. / / / </ Summary> / / / <returns> Benutzerdefinierte Attributtabelle </ returns> geschützte virtuelle AttributeTable BuildAttributeTable () {AttributeTableBuilder builder = new AttributeTableBuilder (); AddDescriptions (Baumeister); addAttributes (Baumeister); AddTables (Baumeister); Rückkehr builder.CreateTable ();}. / / / <summary> / / / Suche alle AttributeTableBuilder Unterklassen in der Montage / / / und fügen ihre Attribute an das Montageband Attributtabelle. / / / </ summary> / / / <param name="builder"> Das assembly-Attribut Tabelle Baumeister </ param>. [SuppressMessage ("Microsoft.Design", "CA1031: DoNotCatchGeneralExceptionTypes", Begründung = "! Design-Zeit dll nicht scheitern sollte")] private static void AddTables (AttributeTableBuilder Builder) {Debug.Assert (builder =! null, "AddTables wird mit Null-Parameter aufgerufen!"); Assembly asm = Assembly.GetExecutingAssembly (); foreach (Type t in asm.GetTypes ()) {if (t.IsSubclassOf (typeof (AttributeTableBuilder))) {try {AttributeTableBuilder ATB = (AttributeTableBuilder) Activator.CreateInstance (t); builder.AddTable (atb.CreateTable ());} catch (Exception e) {Debug.Assert (false, String-Format (CultureInfo.InvariantCulture, "Exception in AddTables Methode:. {0} ", e));}}}} / / / <summary> / / / Ruft die Groß-und Kleinschreibung Ressource Namen der eingebetteten XML-Datei / / / </ summary> string geschützten XmlResourceName {get;. eingestellt ;} / / / <summary> / / / Ruft die Assembly FullName für Typen 'Assembly qualifizierten Namen / / / </ summary> string geschützten AssemblyFullName {get;. gesetzt;} / / / <summary> / / / Erstellen Beschreibung Attribut aus der Laufzeit der Montage XML-Datei / / / </ summary> / / / <param name="builder"> Die Montage Attributtabelle Builder </ param> [SuppressMessage ("Microsoft.Design", "CA1031..: DoNotCatchGeneralExceptionTypes ", Begründung =" Design dll Zeit sollte nicht scheitern ")] private void AddDescriptions (AttributeTableBuilder Builder) {Debug.Assert (builder = null," AddDescriptions wird mit Null-Parameter aufgerufen ");.!. wenn (string IsNullOrEmpty ( XmlResourceName) | | String IsNullOrEmpty (AssemblyFullName)) {return;} XDocument xDoc = XDocument.Load (new StreamReader (Assembly.GetExecutingAssembly ().. GetManifestResourceStream (XmlResourceName))) if (xDoc == null) {return;} foreach (XElement Mitglied in xdoc.Descendants ("Mitglied")) {try {String name = (string) member.Attribute ("name"); bool isType = name.StartsWith ("T", StringComparison.OrdinalIgnoreCase); if ( isType | | name.StartsWith ("P:", StringComparison.OrdinalIgnoreCase)) {int lastDot = name.length; String typeName; if (isType) {typeName = name.Substring (2);} else {lastDot = name.LastIndexOf ('.'); typeName = name.Substring (2, lastDot - 2);} typeName + = AssemblyFullName; Type t = Type.GetType (typeName); if (t = null && && t.IsPublic t.IsClass && t.IsSubclassOf (typeof (FrameworkElement))) {String desc = member.Descendants ("summary") FirstOrDefault ()-Wertes;.. desc = desc.Trim ();. desc = string join ("", desc.Split ( new char [] {'', 't', 'n'}, StringSplitOptions.RemoveEmptyEntries)); if (isType) {builder.AddCallback (t, b => b.AddCustomAttributes (neue DescriptionAttribute (DESC)));} else {String propName = name.Substring (lastDot + 1); PropertyInfo pi = t.GetProperty (propName); MethodInfo mi;! wenn (pi = null && (mi = pi.GetSetMethod ()) = null && mi.IsPublic ) {builder.AddCallback (t, b => b.AddCustomAttributes (propName, neue DescriptionAttribute (DESC)));}}}}} catch (Exception e) {Debug.Assert (false, String-Format (CultureInfo.InvariantCulture,. "Exception in AddDescriptions Methode: {0}"., e));}}} / / / <summary> / / / Geben Sie einen Ort, um benutzerdefinierte Attribute ohne eine AttributeTableBuilder Unterklasse hinzufügen / / / </ summary> / / / <param name="builder"> Die Montage Attributtabelle Builder. </ param> protected virtual void addAttributes (AttributeTableBuilder Builder) {}}}
MetadataBase.cs implementiert die MetadataRegistrationBase Klasse. Sprechen wir über einige seiner wichtigsten Methoden:
AddDescriptions
Alle Laufzeit Montage Projekte haben "XML-Dokumentation Datei"-Option aktiviert, und mit einem Pfad wie ".. BinariesMicrosoft.Windows.Controls.XML", in Projekt -> Eigenschaften -> Registerkarte Build Output-Bereich. Dort finden Sie auch die Einstellung in CSPROJ-Datei wie unten.:
> ..BinariesMicrosoft.Windows.Controls.Design.XML </ DocumentationFile > <DocumentationFile> .. BinariesMicrosoft.Windows.Controls.Design.XML </ DocumentationFile>
Es folgt ein Auszug aus Microsoft.Windows.Controls.XML zu zeigen, was die generierte Dokumentation XML-Datei wie folgt aussieht:
version ="1.0" ? > <? Xml version = "1.0"?> > <Doc> > <Montage> > Microsoft.Windows.Controls </ name > <Name> Microsoft.Windows.Controls </ name> > </ Assembly> > <Mitglieder> name ="T:Microsoft.Windows.Controls.Viewbox" > <Mitglied name = "T: Microsoft.Windows.Controls.Viewbox"> > <Übersicht> Definiert einen Inhalt, der Dekorateur strecken und skalieren können, um ein einziges Kind Füllen Sie den verfügbaren Speicherplatz. > </ Summary> > Preview </ QualityBand > <QualityBand> Vorschau </ QualityBand> > </ Mitglied> name ="F:Microsoft.Windows.Controls.Viewbox.ChildElementName" > <Mitglied name = "F: Microsoft.Windows.Controls.Viewbox.ChildElementName"> > <Übersicht> Name des Kindes Element Viewbox der Standard-Template. > </ Summary> > </ Mitglied> name ="M:Microsoft.Windows.Controls.Viewbox.IsValidStretchValue(System.Object)" > <Mitglied name = "M: Microsoft.Windows.Controls.Viewbox.IsValidStretchValue (System.Object)"> > <Übersicht> Prüfen, ob das Objekt im Wert übergeben eine gültige Stretch Enum-Wert ist. > </ Summary> name ="o" > The object typed value to be checked. </ param > <Param name = "o"> Das Objekt typisierten Wert überprüft werden. </ Param> > True if o is a valid Stretch enum value, false o/w. </ returns > <Returns> true, wenn o eine gültige Stretch Enum-Wert, falsche o / w. </ Returns> > </ Mitglied> name ="P:Microsoft.Windows.Controls.Viewbox.Child" > <Mitglied name = "P: Microsoft.Windows.Controls.Viewbox.Child"> > <Übersicht> Ruft die einzelnes untergeordnetes Element einer Viewbox. > </ Summary> > </ Mitglied> > </ Members> > </ Doc>
Das name-Attribut eines jeden <member> Element folgt dem Muster:
- "T: Microsoft.Windows.Controls.Viewbox": "T:" zeigt an, dies ist eine Art, von der Art der volle Name gefolgt;
- "F: Microsoft.Windows.Controls.Viewbox.ChildElementName": "F:" zeigt an, dies ist ein Gebiet, durch das Feld ist voll qualifizierten Namen gefolgt;
- "M: Microsoft.Windows.Controls.Viewbox.IsValidStretchValue (System.Object)": "M:" zeigt an, dies ist eine Methode, durch die Methode voll qualifizierten Namen und Parameter gefolgt;
- "P: Microsoft.Windows.Controls.Viewbox.Child": "P:" deutet dies ist eine Eigenschaft, von der Unterkunft vollen Namen gefolgt;
Control.Design Projekt Links auf die Microsoft.Windows.Controls.XML-Datei als eingebettete Ressource:
-
- Controls.Design.csproj:
Include ="..BinariesMicrosoft.Windows.Controls.XML" /> <EmbeddedResource Include = ".. BinariesMicrosoft.Windows.Controls.XML" />
AddDescriptions Methode analysiert den eingebetteten XML-Datei und erzeugt DescriptionAttribute für öffentliche Kontrolle Klassen und ihre öffentliche Eigenschaften:
MetadataBase.cs: 134: builder.AddCallback (t, b => b.AddCustomAttributes (neue DescriptionAttribute (DESC))); MetadataBase.cs: 143: builder.AddCallback (t, b => b.AddCustomAttributes (propName, neue DescriptionAttribute (DESC)));
AddAttributes
AddAttributes ist in der Regel in Metadata.cs Datei überschrieben werden, um eine ToolboxBrowsableAttribute (false) benutzerdefiniertes Attribut für die Steuerung Klassen, die nicht erscheinen in ein Designer-Toolbox sollte hinzufügen:
- wenn die Control-Klasse von allen Designern ausgeblendet werden sollen, fügen Sie die ToolboxBrowsableAttribute (false) benutzerdefinierte Attribut auf die Xxx.Design Projekt;
- wenn es von Visual Studio ausgeblendet werden sollen nur, fügen Sie das benutzerdefinierte Attribut zu Xxx.VisualStudio.Design Projekt;
- wenn es von Expression Blend versteckt werden sollte nur, fügen Sie das benutzerdefinierte Attribut zu Xxx.Expression.Design Projekt;
Unten ist addAttributes Umsetzung in Metadata.cs von Controls.VisualStudio.Design Projekt:
/ / / <summary> / / / Geben Sie einen Ort, um benutzerdefinierte Attribute ohne eine AttributeTableBuilder Unterklasse hinzufügen. / / / </ Summary> / / / <param Name="builder"> Die Montage Attributtabelle Builder. </ Param> protected override void addAttributes (AttributeTableBuilder Builder) { ToolboxBrowsableAttribute( false ))); builder.AddCallback (typeof (TreeViewItem), b => b.AddCustomAttributes (neue ToolboxBrowsableAttribute (false))); }
AddTables
Zum anderen Attributen als ToolboxBrowsableAttribute (false) für einen Typ hinzuzufügen, fügen Sie eine Datei wie XxxMetadata.cs ViewboxMetadata.cs unten auf den entsprechenden Design-Projekt:
ViewboxMetadata.cs:
/ / (C) Copyright Microsoft Corporation. / / Diese Quelle unter der Microsoft Public License (Ms-PL) ist. / / Bitte beachten Sie http://go.microsoft.com/fwlink/?LinkID=131993 für weitere Einzelheiten. / / Alle sonstigen Rechte ViewboxMetadata : AttributeTableBuilder { /// <summary> /// To register design time metadata for Viewbox. /// </summary> public ViewboxMetadata() : base () { AddCallback( typeof (Viewbox), b => { b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.BorderThickness), new BrowsableAttribute( false )); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.BorderBrush), new BrowsableAttribute( false )); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Background), new BrowsableAttribute( false )); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Foreground), new BrowsableAttribute( false )); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Child), new CategoryAttribute(Properties.Resources.CommonProperties)); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Stretch), new CategoryAttribute(Properties.Resources.CommonProperties)); b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.StretchDirection), new CategoryAttribute(Properties.Resources.CommonProperties)); }); } } } {/ / / <summary> / / / Um sich zu registrieren Entwurfszeit Metadaten für Viewbox / / / </ summary> interne Klasse ViewboxMetadata:.. AttributeTableBuilder {/ / / <summary> / / / Zur Design-Zeit-Metadaten für Viewbox registrieren / / / </ summary> public ViewboxMetadata (): base () {addCallback (typeof (Viewbox), b => {b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.BorderThickness), neue BrowsableAttribute (false)) ; b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.BorderBrush), neue BrowsableAttribute (false)); b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.Background), neue BrowsableAttribute (false )); b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.Foreground), neue BrowsableAttribute (false)); b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.Child), neue CategoryAttribute (Properties.Resources.CommonProperties)); b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> (x => x.Stretch), neue CategoryAttribute (Properties.Resources.CommonProperties)); b.AddCustomAttributes (Extensions.GetMemberName <Viewbox> ( x => x.StretchDirection), neue CategoryAttribute (Properties.Resources.CommonProperties));});}}}
- Es wird empfohlen, die Namenskonvention hier folgen. Nehmen Viewbox zum Beispiel, ist der Dateiname ViewboxMetadata.cs, und der Name der Klasse ist ViewboxMetadata.
- Die Metadaten-Klasse muss von AttributeTableBuilder erben.
- Sie können benutzerdefinierte Attribute im Konstruktor des Metadaten-Klasse:
- Sie können entweder den Anruf zurück Modell (wie in ViewboxMetada.cs oben) oder die direkte Modell. Die Callback-Modell ist angeblich effizienter.
- direkte Modell Beispiel:
AddCustomAttributes (
typeof (Viewbox), / / Typ
"BorderThickness", / / Name der Eigenschaft
neues Attribut [] {new BrowsableAttribute (false)}); / / benutzerdefinierte Attribut-Array
- direkte Modell Beispiel:
- Um den Namen der Eigenschaft Parameters auf AddCustomAttribute Aufruf bereitzustellen, können Sie entweder die Methode Extensions.GetMemberName () wie in ViewboxMetadata.cs (mehr darüber später in der Post zu diskutieren), um den Namen der Eigenschaft in einer Art sicherer Weg zu bekommen, oder stellen der Name der Eigenschaft direkt als String wie "BorderThickness" im obigen Modell direkte Umsetzung.
- Sie können entweder den Anruf zurück Modell (wie in ViewboxMetada.cs oben) oder die direkte Modell. Die Callback-Modell ist angeblich effizienter.
Die AddTables (AttributeTableBuilder Builder)-Methode:
- listet alle Unterklassen von AttributeTableBuilder in der ausgeführten Assembly
- erstellt eine Instanz von jedem gefunden AttributeTableBuilder Unterklasse:
AttributeTableBuilder ATB = (AttributeTableBuilder) Activator.CreateInstance (t);
- fügen Sie die Attributtabelle der Klasse gefunden, um Builder:
builder.AddTable (atb.CreateTable ());
Metadata.cs
Metadata.cs implementiert MetadataRegistration Klasse, die von MetadataRegistrationBase Klasse in MetadataBase.cs umgesetzt erbt. Sie setzt auch IRegisterMetadata Schnittstelle.
Metadata.cs:
/ / (C) Copyright Microsoft Corporation. / / Diese Quelle steht unter der Microsoft Public License (Ms-PL). / / Bitte beachten Sie http://go.microsoft.com/fwlink/?LinkID=131993 für Details. / / Alle anderen Rechte vorbehalten. using System.Reflection; Verwendung Microsoft.Windows.Controls.Design.Common; Verwendung Microsoft.Windows.Design.Metadata; Namespace Microsoft.Windows.Controls.Design { / / / <summary> / / / MetadataRegistration Klasse. / / / </ Summary> public class MetadataRegistration: MetadataRegistrationBase, IRegisterMetadata { / / / <summary> / / / Design Zeit Metadaten-Registrierung Klasse. / / / </ Summary> öffentlichen MetadataRegistration () : Base () { AssemblyName asmName = typeof (Viewbox) Assembly.GetName ().; XmlResourceName asmName.Name = + ". Design." + + AsmName.Name ". XML" / / "Microsoft.Windows.Controls.Design.Microsoft.Windows.Controls.XML" AssemblyFullName = "," + asmName.FullName; } / / / <summary> / / / Aus System.Windows.Controls.Toolbox.Design.MetadataRegistration entlehnt: / / / Eine statische Flagge, um sicherzustellen, Metadaten einzige registriert ist. / / / </ Summary> private static bool _initialized; / / / <summary> / / / Von Werkzeugen aufgerufen, um Design-Zeit-Metadaten zu registrieren. / / / </ Summary> public void register () { if (! _initialized) { MetadataStore.AddAttributeTable (BuildAttributeTable ()); _initialized = true; } } / / / <summary> / / / Geben Sie einen Ort, um benutzerdefinierte Attribute ohne eine AttributeTableBuilder Unterklasse hinzufügen. / / / </ Summary> / / / <param Name="builder"> Die Montage Attributtabelle Builder. </ Param> protected override void addAttributes (AttributeTableBuilder Builder) { } } }
Sprechen wir über einige seiner wichtigsten Methoden:
MetadataRegistration
Dieser Konstruktor initialisiert zwei wichtigen Bereichen:
- XmlResourceName: Ressource Namen der eingebetteten Dokumentation XML-Datei, indem MetadataRegistrationBase.AddDescriptions-Methode verwendet.
- AssemblyFullName: vollständige Name der Laufzeit der Montage dieser Entwurf ist für die Zeit der Montage.
Sie müssen die Art Viewbox mit Ihrer eigenen Klasse ersetzen, wenn Sie Metadata.cs in Ihr eigenes Design-Projekt verwenden.
Registrieren
Dies ist die einzige Methode der IRegisterMetadata Schnittstelle. Es fügt das benutzerdefinierte Attribut-Tabelle, aus AddDescripions, addAttributes & AddTables oben beschriebenen Methoden, um Designer-Metadatenspeicher gebaut:
MetadataStore.AddAttributeTable (BuildAttributeTable ());
Extensions.cs
Diese Datei enthält die Implementierung der Erweiterung Methode GetMemberName <T> (Expression <Func <T, Objekt>> expr), die verwendet werden, um den Namen eines Mitglieds einer Art mit der Kompilierung Scheck bekommen und IntelliSense wird:
Die Idee wurde ursprünglich durch vorgeschlagen Jafar Husain (siehe seinem Blog-Post Symbole in C # 3.0 ) und dann verbesserte sich um Justin Angel . Es ist ein großer Trick, um Tippfehler zu vermeiden, aber der Nachteil ist, dass es in Silverlight Referenzen und Baugruppen zieht in eine ansonsten rein. NET-Assembly. Hier ist die komplette Quelle:
Extensions.cs:
System; using System.Linq.Expressions; namespace Microsoft.Windows.Controls.Design.Common { /// <summary> /// This set of internal extension methods provide general solutions and /// utilities in a small enough number to not warrant a dedicated extension /// methods class. /// </summary> internal static class Extensions { /// <summary> /// Helper method to get member name with compile time verification to avoid typo. /// </summary> /// <typeparam name="T">The containing class of the member whose name is retrieved.</typeparam> /// <param name="expr">The lambda expression usually in the form of o => o.member.</param> /// <returns>The name of the property.</returns> public static string GetMemberName<T>(Expression<Func<T, object >> expr) { Expression body = ((LambdaExpression)expr).Body; MemberExpression memberExpression = body as MemberExpression; if (memberExpression == null ) { memberExpression = (MemberExpression)((UnaryExpression)body).Operand; } return memberExpression.Member.Name; } } } / / (C) Copyright Microsoft Corporation. / / Diese Quelle unter der Microsoft Public License (Ms-PL) ist. / / Bitte beachten Sie http://go.microsoft.com/fwlink/?LinkID=131993 für weitere Einzelheiten. / / Alle anderen Rechte vorbehalten using System;. mit System.Linq.Expressions; Namespace Microsoft.Windows.Controls.Design.Common {/ / / <summary> / / / Dieser Satz von internen Nebenstelle Methoden bieten allgemeine und Lösungen / / / Utilities in einer kleinen Zahl genug, um keine Gewähr für eine spezielle Erweiterung / / / Methoden der Klasse. / / / </ summary> interne statische Klasse Extensions {/ / / <summary> / / / Helper-Methode zur Member-Namen mit der Kompilierung Überprüfung bekommen, um zu vermeiden, Tippfehler. / / / </ summary> / / / <typeparam name="T"> Die enthaltenden Klasse des Mitglieds, dessen Namen abgerufen werden. </ typeparam> / / / <param name="expr"> Der Lambda-Ausdruck in der Regel in Form von O => o.member. </ param> / / / <returns> Der Name der Eigenschaft. </ returns> public static string GetMemberName <T> (Expression <Func <T, object>> expr) {Ausdruck Körper = ((LambdaExpression) expr) Karosserie;. MemberExpression MemberExpression = Körper als MemberExpression;. if (MemberExpression == null) {MemberExpression = (MemberExpression) ((UnaryExpression) Körper) Operanden;} return memberExpression.Member.Name; }}}
Abschluss
Dieser Beitrag beschreibt die Umsetzung der Design-Zeit-Funktionen im Dezember 2008 Release von Silverlight-Toolkit , und führte einen einfachen Rahmen für die Umsetzung Design-Zeit-Funktion für Silverlight-Steuerelemente. Sie können zu modellieren, die Umsetzung und die Wiederverwendung der Rahmen, in Ihren eigenen Projekten. Der Rahmen ist noch sehr primitiv, unterstützt Metadaten-Registrierung nur, weil das ist alles für jetzt Mischung unterstützt. Ich werde in die Verbesserung der Rahmen-und Design-Zeit-Funktionen für Silverlight-Toolkits schauen, wie das Hinzufügen von benutzerdefinierten Inline / Erweitert / Dialog-Editoren, Design-Zeit-Daten, Design-Zeit nur Verhaltensweisen etc.
Wie ich in früheren Post erklärte Design-Time Features in Silverlight-Toolkit , Design-Zeit Erfahrungen für die Kontrollen sind sehr wichtig. Es verbessert nicht nur die Erfahrung und die Produktivität der Entwickler, die diese Steuerelemente verwenden mit geruhen Zeit-Funktionen, sondern verbessert auch die Erfahrung von Endnutzern, da immer mehr Anwendungen den Benutzern mehr Flexibilität bei der Anpassung Benutzeroberfläche, wie das Ändern von Layout per Drag & Drop Kontrollen oder das Ändern eines Steuerelements Einstellungen, genau wie in einem Designer, ausser dass er während der Laufzeit ist.










@ FIDER
Zum Debuggen, versuchen Sie dies:
* Sicherstellen, dass die Namensgebung und den Standort Ihres Kontrolle und Baugruppen sind korrekt, dh wenn Ihr Steuerbaugruppe ist in c foo.dll: \ tmp, dann ist das Design der Montage ist in c foo.design.dll: \ tmp oder C: \ tmp \ Design.
* Öffnen Sie Ihre Design-Projekt, einen Haltepunkt an Ihrem Register-Funktion, und stellen Sie den Debug-Ziel von Ihrem Design-Projekt-Eigenschaft auf Visual Studio starten
* F5 drücken, wird eine neue Instanz ins Leben gerufen VS; erstellen Sie ein neues SL-Projekt, dann gehen Sie durch die Elemente auswählen Dialog, wählen Sie Silverlight-Tab, suchen Sie den Ordner c: \ tmp und wählen Sie Ihre Steuerung dll foo.dll.
* Ihr Haltepunkt sollte getroffen.
Eine weitere häufige Ursache von Problemen: stellen Sie sicher, es ist die Silverlight-Typ (TreeViewItemAdv in Ihrem Beispiel) Sie sind für die Angabe von Metadaten.
Hope this helps!
Danke,
-Ning
Hallo Ning,
Ich habe als Baugruppen erstellt und in einem lokalen Ordner und GAC. Ich versuche, die Assemblys aus dem lokalen Ordner wählen, in der Elemente-Dialog zu öffnen - Silverlight Registerkarte Komponenten. Seine wählt alle Klassen in den Versammlungen und sind auf Toolbox hinzugefügt.
Also lassen Sie mich wissen, wie kann ich diese Projekte zu debuggen
Regards,
FIDER
Hallo Ning,
Guter Artikel. Ich habe Design und visualstudio.design Projekt für meine Silverlight benutzerdefiniertes Steuerelement erstellt. Mein Ziel ist es nur eine Klasse aus der Toolbox zu verstecken. Ich habe eine Datei metadata.cs wo ich in ToolboxBrowsableAttribute eingestellt haben, dass zu den Klassen, die hidded werden sollte false gesetzt. Meine metadata.cs Datei sieht wie folgt aus.
public class Metadaten: IRegisterMetadata
{
public void register ()
{
AttributeTableBuilder builder = new AttributeTableBuilder ();
builder.AddCallback (typeof (TreeViewItemAdv), b => b.AddCustomAttributes (neue ToolboxBrowsableAttribute (false)));
MetadataStore.AddAttributeTable (builder.CreateTable ());
}
}
Aber ich konnte immer noch die Klasse angezeigt werden.
Jede Hilfe wird spürbar sein.
Regards,
FIDER.
Hallo FIDER,
Ihr Code sieht recht. Mein Vorschlag ist, Schritt durch, um sicherzustellen, Ihren Code geladen und ausgeführt wird korrekt:
* Legen Sie einen Haltepunkt an Ihrer Funktion Register und sehen, ob es getroffen wurde. VS2010 & Blend3 Verwendung neueren MWD und eine andere Anlaufstelle (IProvideAttributeTable.AttributeTable statt IRegisterMetadata.Register), und wenn der Name oder Ort Ihrer Design-DLL ist nicht richtig, bezogen auf die entsprechende Laufzeit-DLL, wird Ihr Design dll nicht entweder geladen. Dies ist eine häufige Ursache für Probleme.
* Wenn Ihr Haltepunkt erreicht wird, Schritt durch den Code, dann überprüfen Sie die Wirkung, über AttributeTable.ValidateTable, AttributeTable.GetCustomAttributes oder TypeDescriptor.GetAttributes etc.
Hope this helps.
Danke,
-Ning