Wednesday, December 19, 2007

Vom Namen zur Instanz mit DynamicMethod

Ein Kunde legt beim Logging unterschiedliche LogMessages (unterschiedliche Klassen) in einer Datenbank ab. Dabei werden der Typ der Message (der LogMessage Klassenname), die Message selber sowie weitere Felder in der Tabelle gespeichert.
Nun sollten diese Einträge gelesen und die ursprünglichen Objekte wieder hergestellt werden.

Für die erste Lösung wurde der offensichtliche Weg mit Reflection gewählt:

string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
BaseType b = (BaseType)Activator.CreateInstance(t);

Da dies nicht sehr performant ist, wurde der Code wie folgt verbessert:

string s = "Namespace.Name.FromDB";
Assembly a = Assembly.GetAssembly(typeof(ThisClass));
Type t = a.GetType(s);
ConstructorInfo i = t.GetConstructor(Type.EmptyTypes);
BaseType b = (BaseType)i.Invoke(null);

Wobei der Klassenname und die zugehörige ConstructorInfo in einem statischen Dictionaire abgelegt werden, damit diese nicht bei jedem Aufruf neu evaluiert werden müssen. So haben wir vom zweiten bis x-ten Aufruf den gewünschten Performancegewinn. Der Code dazu sieht folgendermassen aus.

ConstructorInfo c = null;
if (_cinfo.TryGetValue(s, out c))
{
c = t.GetConstructor(Type.EmptyTypes);
lock(_cinfo)
{
if (_cinfo.ContainsKey(s))
{
_cinfo.Add(s, c);
}
}
}

Noch eleganter und viel performanter geht's mit Dynamic Methods. Der von der dynamischen Methode zurückgegebene Delegate wird wie zuvor die ConstructorInfo in einem Dictionaire abgelegt und für die Folgeaufrufe wieder verwendet:

public delegate BaseType CtorDelegate();

DynamicMethod dm = new DynamicMethod
("MyCtor", typeof(BaseType), Type.EmptyTypes, typeof(BaseType).Module);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Newobj, t.GetConstructor(Type.EmptyTypes));
ilgen.Emit(OpCodes.Ret);
CtorDelegate d = (CtorDelegate)dm.CreateDelegate(typeof(CtorDelegate));

Der Aufruf ist dann genau so kurz (d wird zuvor aus dem Dictionaire gelesen) und unheimlich schnell:

t message = (t)d();

Nun muss nur noch alles in einer Factory Klasse schön verpackt werden und fertig. Mehr zu diesem Thema inklusive Performance Messungen findet man wie immer bei Google ;-)

Monday, November 05, 2007

Sprachfeatures C# 3.0 und wie sie die Welt verändern

Mit dem .NET Framework 3.5 kommen erneut etliche Features zur Sprache C# 3.0 hinzu, die dem Entwickler das Leben bedeutend erleichtern (ausser man ist auf der Wartungsseite).
Automatic Properties, Extension Methods, Lambda Expressions, Anonymous Types, usw. bieten dem Entwickler vollkommen neue Möglichkeiten, was zu sehr schickem Code führen kann. Bei exzessivem Anwenden nur des Features oder der Eleganz wegen kann das aber zu schwer wartbarem und unübersichtlichen Code- und Sprachauswüchsen führen.

Trotzdem, ganz hübsche ist folgendes (auch wenn nicht sehr sinnvoll ;-):
7.TimesPrint("MeinText");

Wesentlich eleganter als:
for (int i = 0; i < 7; i++)
{
Console.WriteLine("MeinText");
}

Doch damit es funktioniert braucht's noch eine Extension Method:
Public static void TimesPrint(this int no, string s)
{
for (int i = 0; i < no; i++)
{
Console.WriteLine(s);
}
}

Etwas allgemeiner formuliert könnte der Aufruf so aussehen und so auch wirklich Sinn machen (die Extension Method muss natürlich entsprechend angepasst werden):
7.Times(i => Console.WriteLine("MeinText"));


Ob die Wartbarkeit durch die schwerer erkennbare Funktionalität komplexer oder durch die elegante Syntax doch eher erleichtert wird, wird spätestens die Praxis zeigen.

So oder so, die neuen Features machen viel Spass!

PS: Eine gute Übersicht zu den neuen Features gibt's hier.

Thursday, October 25, 2007

Output Parameter in dynamischem T-SQL

In einer Stored Procedure werden in einem ersten Schritt zwei Variablen gesetzt, die für die weitere Verarbeitung benötigt werden. Dies könnte so aussehen:

SELECT @var1 = Var1, @var2 = Var2
FROM MyTable
WHERE ID = @ID

Nun muss das Statement aber dynamisch zusammengestellt werden und der Select-String danach ausgeführt werden. Dies könnte so aussehen:

SET @query = '
SELECT @var1 = Var1, @var2 = Var2
FROM MyTable
WHERE ID = ' + CAST(@ID as CHAR(15))
EXEC (@query)

In meinem Fall wird das Statement auf einem Hostsystem ausgeführt und der Aufruf muss über Openquery gemacht werden. Dies könnte so aussehen:

SET @query = '
SELECT @var1 = Var1, @var2 = Var2
FROM OPENQUERY(MySystem, ''SELECT Var1, Var2 FROM MyTable WHERE ID = '
+ dbo.MyTypeConverter(@id) + ''')'
EXEC (@query)

Ganz klar, dass die beiden Variablen so nicht mehr abgefüllt werden, sondern zu einem Fehler führen. Wie können diese gesetzt werden?
Gemäss einer ersten Idee wurde eine Variable vom Typ TABLE verwendet und das Openquery-Resultat mit INSERT INTO angefügt. Danach das erste Select-Statement verwendet um die Variablen zu setzten. Funktioniert! Ist aber nicht sehr elegant.
Hübscher ist's so:

SET @params = ' @var1 int OUTPUT, @var2 nvarchar(max) OUTPUT'
SET @query = '
SELECT @var1Out = Var1, @var2Out = Var2
FROM OPENQUERY(MySystem, SELECT Var1, Var2 FROM MyTable '
+ dbo.MyTypeConverter(@id) + ''')'
EXEC sp_executesql @query, @params, @var1Out = @var1 OUTPUT, @var2Out = @var2 OUTPUT

Dieser Ansatz mit der System Procedure sp_executesql bietet zudem noch weitere Vorteile die in der SQL Server Hilfe nachgelesen werden können.

Friday, October 05, 2007

Dynamic Method Performance

In einem interessanten Artikel im MSDN Magazine wird auf unterschiedliche Performance Probleme, im Speziellen im Zusammenhang mit Reflection, eingegangen. Mich interessierte vor allem die gelobte Performance der dynamischen Methoden, die seit der .NET Framework Version 2.0 zur Verfügung stehen. Eine einfache Testanwendung hat gezeigt, dass der Artikel recht behält.
In der Testanwendung wird einem Objekt ein String eine Million mal zugewiesen. Dies zuerst direkt verdrahtet, danach mit dynamischer Methode und zuletzt via Reflection. Das Ergebnis ist eindeutig. Die direkt verdrahtete Methode ist natürlich die Schnellste, die dynamische Methode braucht dafür doppelt so lange (was ich immer noch sehr gut finde) und mittels Reflection dauert es 100mal länger. What a gain!
Übrigens, die Assembly habe ich mit Lutz's Reflector untersucht, um sicher zu stellen, dass die Schlaufe auch wirklich bei allen drei Varianten ausgeführt wird.

Und hier noch der Code:

public class Program
{
private delegate void DemoDelegate(Foo arg);

static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();

Foo f = new Foo();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
f.Text = "Hello World!";
}
stopwatch.Stop();
Console.WriteLine("Direct: {0}", stopwatch.Elapsed);

stopwatch.Reset();

DemoDelegate del = createMethod();
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
del(f);
}
stopwatch.Stop();

Console.WriteLine("Dynamic method: {0}", stopwatch.Elapsed);

stopwatch.Reset();

PropertyInfo property = f.GetType().GetProperty("Text");
stopwatch.Start();
for (int i = 0; i < 1000000; i++)
{
property.SetValue(f, "Hello World!", null);
}
stopwatch.Stop();
Console.WriteLine("Reflection: {0}", stopwatch.Elapsed);


Console.ReadKey();
}

private static DemoDelegate createMethod()
{
Type[] parameterTypes = new Type[] { typeof(Foo) };
DynamicMethod method = new DynamicMethod("Demo", null, parameterTypes, typeof(Program));

ILGenerator iLGenerator = method.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Ldstr, "Hello World!");
MethodInfo setMethod = typeof(Foo).GetProperty("Text").GetSetMethod();
iLGenerator.EmitCall(OpCodes.Call, setMethod, null);
iLGenerator.Emit(OpCodes.Ret);

return (DemoDelegate)method.CreateDelegate(typeof(DemoDelegate));
}
}

public class Foo
{
private string _text;

public string Text
{
get { return _text; }
set { _text = value; }
}
}

Friday, September 14, 2007

Remote Debbuging

Wenn man eine Applikation remote debuggen soll, wird man feststellen, dass verschiedene Punkte beachtet werden müssen, damit das Ziel erreicht wird. Einige hoffentlich nützliche Hinweise möchte ich hiermit weitergeben:

1. Remotedebugging heisst, mit dem lokalen Visual Studio eine laufende Applikation auf einer anderen Maschine, typischerweise einem Server, zu debuggen.
2. Der Applikationscode (dll und pdb Files) muss auf beiden Systemen identisch sein.
3. Auf der Remotemaschine muss der Remote Debugger, im Speziellen das File msvsmon.exe vorhanden sein. Auf der lokalen Maschine muss Visual Studio vorhanden sein.
4. Der lokale Account (unter dem Visual Studio läuft) muss auf dem Remotesystem Administratorrechte haben, damit sich Visual Studio attachen kann.
5. Auf dem Remotesystem muss der Remotedebugger (msvsmon.exe) unter dem lokalen Account (unter demselben wie Visual Studio läuft), gestartet werden (im Contextmenü Run as ... ausführen).
6. In Visual Studio muss unter Tools, Options, Debugging, General die Option ‚Enable just my Code’ inaktiv sein, damit die Symbols geladen werden und Breakepoints gesetzt werden können.
7. In Visual Studio kann nun unter Debug, Attach to Process der Remoteprozess angebunden werden. Dazu Transport: Default und Qualifier: SERVERNAME wählen. Nun den gewünschten Prozess, z. Bsp. W3wp.exe auswählen. Ist der gewünschte Prozess nicht aufgelistet, muss sichergestellt werden, dass die Applikation wirklich läuft. Dazu z. Bsp. Die Webseite aufrufen.
8. Nun wie gewohnt debuggen.

Viel Spass.

http und Textfile

Für einen Kunden sollte ich von einer Webseite (http://.../mytext.php) ein File downloaden und den Text auf einem Server Share speichern. Und so einfach ist das mit .NET 2.0.

// Get stream from any webserver over HTTP response object
WebRequest request = (WebRequest)WebRequest.Create(new Uri("http://.../mytext.php"));
WebResponse response = request.GetResponse();
// create reader to read the stream and save it in a string
StreamReader reader = new StreamReader(response.GetResponseStream());
// write string to a file
File.WriteAllText(@"c:/test.txt", reader.ReadToEnd());
// close objects
reader.Close();
response.Close();

Wednesday, February 14, 2007

Microsoft Expression - Tools for Designer or Developer?

Vier neue Produkte aus dem Hause Microsoft warten auf uns Entwickler... oder doch nicht?

* Microsoft Expression Media (endgültige Version erscheint voraussichtlich Mitte 2007)
Expression Media ist ein Produkt für das Digital Asset Management. Basierend auf der Software iView MediaPro, die im Juni 2006 von Microsoft übernommen wurde, dient es der Katalogisierung und Verarbeitung von Mediendateien wie Grafiken und Videos.

Ich denke nicht, dass dieses Produkt für Entwickler von Interesse ist.


* Microsoft Expression Design (Beta 1 verfügbar, endgültige Version erscheint voraussichtlich Mitte 2007)
Expression Design ist das Bildbearbeitungsprogramm, also der PhotoShop von Microsoft. Das Produkt deckt höchste Anforderungen ab, könnte aber auch einige Entwickler überfordern. Vielleich hält man sich da doch besser ans Paint.NET 3.0 mit dem auch ein Entwickler das gewünschte Resultat erreicht und zudem erst noch der Sourcecode vorliegt (siehe letzte Posts).

Ich denke nicht, dass dieses Produkt für Entwickler von Interesse ist.


* Microsoft Expression Web (bereits verfügbar)
Das aus Microsoft FrontPage hervorgegangene Expression Web ist eine Web-Entwicklungsumgebung die leider nicht alles bietet was man sich wünscht. Ich war total entäuscht von diesem Produkt. Zum Beispiel werden keine Themes und Skins von ASP.NET 2.0 unterstützt aber auch Basics wie ein tabellenloses Layout (mit DIV Tags und CSS) wird nicht unterstützt. Das heisst, der Designer kann's nicht anzeigen und es gibt auch keine Tools, Intellisense, ... um damit zu arbeiten.

Ich denke nicht, dass dieses Produkt für Entwickler von Interesse ist.


* Microsoft Expression Blend (Beta 2 verfügbar, endgültige Version erscheint voraussichtlich Mitte 2007)
Expression Blend ist ein Designwerkzeug für das Erstellen von interaktiven Benutzeroberflächen. Es fungiert als WYSIWYG-Editor für XAML, eine in XML formulierte Sprache zur Beschreibung und Erstellung von Oberflächen der Windows Presentation Foundation (WPF und WPF/E). Vektorgrafiken, Rastergrafiken, 3D-Objekte aus bekannten 3D-Programmen, Videos, Sound und Text lassen sich miteinander vereinen und mit Hilfe einer Zeitleiste animieren.

Ich denke, dass dieses Produkt bis zu einem bestimmten Punkt auch für Entwickler von Interesse ist. Um alle Möglichkeiten auszuschöpfen, sollte es allerdings in die Hände eines Grafikers gelegt werden.

Wednesday, February 07, 2007

UpdatePanel und Validation Controls

Natürlich wollen wir alle das UpdatePanel einsetzen. Speziell wenn wir ein oder mehere GridViews auf der Seite haben, die ein Paging, Sorting und Edit bieten.
Gerade im Edit Fall kommen aber sicher die Validator Controls ins Spiel, die aber leider im UpdatePanel nicht funktionieren.

Das Problem wird gelöst, in dem AJAX-Compatible Validators verwendet werde, die im CTP auch bereits vorhanden waren. Im RTM sind sie aber nicht dabei, sondern ausgelagert in den ASP.NET AJAX January Futures CTP, den man hier downloaden kann.

Wer weder verzichten, noch den CTP einsetzen will, der kann auf den Validater Controls einfach die Property enableclientscript="false" setzen und das Ganze funktioniert. Ein paar Einschränkungen gibt's natürlich. Z.Bsp. showmessagebox="true" auf dem ValidationSummary funktioniert nicht.

Sunday, February 04, 2007

GridView and Formating

Ich will ein Datum im GridView formatieren und es gibt auch eine entsprechende Property. Einfach? Gar nicht! Den die Property will einfach nicht greifen. Nach langem finde ich den Weg der funktioniert, obwohl mir da noch etwas das Verständnis dafür fehlt.
Die Formatierung greift nur, wenn die Property HTMLEncode auf false gesetzt wird.

Das funktionierende Beispiel:







Friday, February 02, 2007

February CTP of WPF/E

Der Dezember Release ist bereits abgelaufen, obwohl dieser bis Ende Februar hätte laufen müssen. Macht nichts, den der Februar Release ist bereits da.

Einige schöne, neue Features sind hinzugekommen. Aber auch einige Änderungen an bestehenden Sachen, so das viele Beispiele nicht mehr laufen.

Mehr Infos gibt's hier.

Wednesday, January 31, 2007

Paint.NET

Auch als Software Entwickler ist ein Grafikprogramm immer wieder von nutzen. Nur soll es nicht so teuer und kompliziert sein wie ein PhotoShop und doch etwas mehr Möglichkeiten bieten als ein Paint.
Definitiv das richtige Tool für den .NET Entwickler ist Paint.NET. Dies ist nicht etwa Paint in .NET programmiert sondern wirklich ein kleiner aber feiner PhotoShop.

Hier geht's zum Produkt. Übrigens, es gibt's sogar den Source Code zum downloaden ;-)

Monday, January 29, 2007

Redirecting to another page

Um von einer zur anderen Seite navigieren zu können, kennen wir heute die unterschiedlichsten Methoden. Mit ASP.NET werden hauptsächlich vier Arten eingesetzt.
- Hyperlinks
- Browser Redirect
- Server Transfer
- Cross-Page Posting

Die Vor- und Nachteile werden hier von Microsoft beschrieben.


Für alle die immer noch eine gewisse Unsicherheit spüren, hier noch ein paar Ergänzungen.

Sofern von der einen zur anderen Seite keine Daten übertragen werden müssen, sollte der Hyperlink verwendet werden. Ausnahme, wenn man die URL 'verstecken' will.

Auf das Redirect wird wenn immer möglich verzichtet, da dies einen zusätzlichen Roundtrip bedeutet.

Wenn Daten ausgetauscht werden müssen, kommen die beiden Möglichkeiten 'Server Transfer' und 'Cross-Page Posting' ins Spiel.
'Cross-Page Posting' ist seit ASP.NET 2.0 möglich und die etwas schönere Variante. Dabei ändert sich die Browserfunktionalität nicht, jedoch muss das Ziel fix verdrahtet werden.
Beim 'Server Transfer' ist ein GoBack im Browser nicht möglich, die dynamische Adressierung jedoch kein Problem.
In der Praxis merkt man aber sehr schnell, dass beide Methoden ihre Berechtiging haben und man am Besten fährt, wenn eine Kombination der beiden Möglichkeiten verwendet wird.

Datenübergabe:
Beim 'Server Transfer' werden die Daten über public Properties übergeben, die auf der aufrufenden Seite implementiert werden. Auf der Zielseite können sie über das Object Context.Handler abgefragt werden.
((PagesClassName)Context.Handler).PropertyName

Beim 'Cross-Page Posting' können die Felder mit FindControl() ausgelesen werden.
((TextBox)((PagesClassName)PreviousPage).FindControl("MyTextBox")))).Text

Diese Methode muss verwendet werden da die Controls protected sind. Aus Performance-Gründen (und OO Sicht) sollte auf diese Methode verzichtet werden. Wenn analog dem 'Server Transfer' mit public Properties gearbeitet wird, lassen sich zudem Schwierigkeiten vermeiden, die beim Einsatz von Masterpages und UserControls entstehen und mit einem zwischenschritt über den ContentPlaceholder gelöst werden müssten.
prePage = (PagesClassName)PreviousPage;
ContentPlaceHolder c = (ContentPlaceHolder)prePage .Master.FindControl("ContentPlaceHolder1");
(((TextBox)(c.FindControl("MyTextBox"))).Text



Wednesday, January 24, 2007

WPF/E for the real world

Ich war sofort begeistert von WPF/E als ich die ersten Beispiel sah. Wenn man aber erst mal einen Tag damit rumgespielt und selber die ersten Demos realister hat kommt plötzlich die Frage: War's das schon?

Im Moment ja, den wichtige Elemente für eine 'richtige' Anwendung fehlen noch. So gibt's nicht einmal eine Textbox, was die Interaktivität schwierig macht.
Das ist natürlich alles bereits angekündigt, wir müssen uns aber noch etwas in Geduld üben.

Wer schon mal einen kleinen Vorgeschmack möchte, soll sich das Beispiel von Tony anschauen. Nice!

Stay tuned, more is coming up.

Tuesday, January 23, 2007

ASP.NET AJAX 1.0 Released

Leider reichte es dazumal mit dem Release nicht auf den Projektstart vom Dezember 2006. Das wir ihn jetzt bereits bekommen haben ist aber sehr erfreulich. Dann wollen wir uns mal ans Update machen.

Den ASP.NET AJAX 1.0 Release gibt's hier. Die Online Doku ist nun auch ganz ordentlich und die Upgrade Whitepaper funktionieren.

Auch vom ASP.NET AJAX Control Toolkit gibt's am selben Ort einen neuen Release. Wie man im Blog von Scott sieht, sind ein paar schöne Controls wie der Calendar Extender dazu gekommen.

Monday, January 22, 2007

Templated Control with Designer Support

Für mein aktuelles Projekt benötigten wir eine schön formatierte Box die einen Titel hat. Statt diese auf jeder Seite x-mal mit Table Tags zu bauen und zu formatieren, wollten wir ein eigenes Control verwenden.

1. Versuch: Erstellen eines einfachen Controls abgeleitet von der Klasse Controls befriedigte unsere Bedürfnisse nur mässig, da zwischen dem Start- und Endtag nur Text verwendet werden konnte. Wir wollten jedoch ASP.NET Controls in der Box platzieren.

[DefaultProperty("Title"), ToolboxData("<{0}:SimpleBox runat=server>")]
public class SimpleBox : Control
{
private string _title;

[Bindable(true), Category("Appearance"), DefaultValue("")]
public string Title
{
get { return _title; }
set { _title = value; }
}

protected override void Render(HtmlTextWriter output)
{
string outputString = string.Empty;

// generate output (html)
outputString = "<table cellspacing="0" cellpadding="0" border="1">"
+ " <tr>"
+ " <td>" + Title + "</td>"
+ " </tr>"
+ " <tr>"
+ " <td>";

// read between the begin and end tag
if ((HasControls()) && (Controls[0] is LiteralControl))
{
outputString += ((LiteralControl)Controls[0]).Text;
}

string outputStringEnd = "</td>"
+ " </tr>"
+ "</table>";

// speak your mind
output.Write(outputString);
}
}


2. Versuch: Erstellen eines Composite Controls
Das war schon besser. Die Controls zwischen Start- und Endtag wurden erkannt und gerendert. Einziges Problem waren die Events der Childcontrols. Diese wurden nicht gefeuert. Könnte man ausprogrammieren, aber...

[DefaultProperty("Title"), ToolboxData("<{0}:ComposedBox runat=server>")]
public class ComposedBox : Control, INamingContainer
{
private string _title;

//[Bindable(true), Category("Appearance"), DefaultValue("")]
public string Title
{
get
{
return _title;
}
set
{
_title = value;
}
}

protected override void CreateChildControls()
{
string outputStringStart = "<table cellspacing="0" cellpadding="0" border="1">"
+ " <tr>"
+ " <td>" + Title + "</td>"
+ " </tr>"
+ " <tr>"
+ " <td>";

string outputStringEnd = "</td>"
+ " </tr>"
+ "</table>
";

this.Controls.AddAt(0, (new LiteralControl(outputStringStart)));
this.Controls.AddAt(this.Controls.Count - 1, (new LiteralControl(outputStringEnd)));
}
}


3. Versuch: Das Templated Control
Hier funktioniert alles auf anhieb, ohne grossen Programmieraufwand. In kurzer Zeit war unser Control erstellt und die formatierte Box stand zur Verfügung. In einer überarbeiteten Variante integrierten wir dann auch noch den fehlenden Designer Support. Great!


public class BoxTemplate : CompositeControl, INamingContainer
{
private String _message = null;
public BoxTemplate() { }
public BoxTemplate(String message)
{
_message = message;
}
public String Message
{
get { return _message; }
set { _message = value; }
}
}

[DefaultProperty("Title"),
ParseChildren(true), PersistChildren(false),
Designer("CSBS.TVCITTool.Client.Web.Controls.TemplatedBoxControlDesigner, CSBS.TVCITTool.Client.Web.Controls"),
ToolboxData("<{0}:TemplatedBox runat=server>")]
public class TemplatedBox : CompositeControl, INamingContainer
{
private string _title;
private ITemplate _messageTemplate = null;
private String _message = null;

public String Message
{
get { return _message; }
set { _message = value; }
}

[Bindable(true), Category("Appearance"), DefaultValue("")]
public string Title
{
get { return _title; }
set { _title = value; }
}

[PersistenceMode(PersistenceMode.InnerProperty), DefaultValue(null), TemplateContainer(typeof(BoxTemplate)),
TemplateInstance(TemplateInstance.Single), Browsable(false)]
public ITemplate MessageTemplate
{
get { return _messageTemplate; }
set { _messageTemplate = value; }
}

public override void DataBind()
{
EnsureChildControls();
base.DataBind();
}

protected override void CreateChildControls()
{
// If a template has been specified, use it to create children.
// Otherwise, create a single literalcontrol with message value

string outputStringStart = "
<table class="boxframe" cellspacing="'\">"
+ " <tbody><tr>"
+ " <td class="boxheader">" + Title + "</td>"
+ " </tr>"
+ " <tr>"
+ " <td>";

string outputStringEnd = "</td>"
+ " </tr>"
+ "</tbody></table>
";

if (MessageTemplate != null)
{
Controls.Clear();
BoxTemplate i = new BoxTemplate(this.Message);
MessageTemplate.InstantiateIn(i);
Controls.Add(i);
}
else
{
this.Controls.Add(new LiteralControl(this.Message));
}

this.Controls.AddAt(0, (new LiteralControl(outputStringStart)));
this.Controls.AddAt(this.Controls.Count, (new LiteralControl(outputStringEnd)));
}
}

public class TemplatedBoxControlDesigner : CompositeControlDesigner
{
private TemplatedBox _designControl;
private TemplateGroupCollection _templateGroupCollection;

public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
_designControl = (TemplatedBox)component;
SetViewFlags(ViewFlags.DesignTimeHtmlRequiresLoadComplete, true);
SetViewFlags(ViewFlags.TemplateEditing, true);
}

public override TemplateGroupCollection TemplateGroups
{
get
{
if (_templateGroupCollection == null)
{
_templateGroupCollection = base.TemplateGroups;
TemplateGroup group;
TemplateDefinition templateDefinition;
group = new TemplateGroup("ContextRowTemplates");
templateDefinition = new TemplateDefinition(this, "MessageTemplate", _designControl, "MessageTemplate", false); //der vierte Parameter ist der Name der Property des Control
group.AddTemplateDefinition(templateDefinition);
_templateGroupCollection.Add(group);
}
return _templateGroupCollection;
}
}
}

Tuesday, January 09, 2007

Passed 70-554

I did it again... und bin nun endlich MCPD.

Hilfen und Links findet ihr in den weiteren Blogs mit dem Label Learning.

Friday, January 05, 2007

WPF/E Code Samples

Es ist noch nicht lange her das wir WPF/E erhalten haben und schon gibt's dutzende Beispiele.
Microsoft hat gleich selber ein Sample Pack erstellt das man hier downloaden kann. Weitere Informationen gibt's auf MSDN WPF/E Dev Center.
Weitere interessante Beispiele inklusive Source Code findet man im Blog von Mike Harsh hier oder hier von Richard Leggett.
Mein technisches Lieblingsbeispiel kommt von Simon, leider ohne Source Code und befindet sich hier.