Samstag, 28. Juli 2007

My Extensions in VS2008

Nach einem Tag downloaden und Installation, habe ich nun endlich die Beta2 von Visual Studio 2008 "Orcas" auf meinem Rechner.





Aufgefallen ist mir der neue Projekttab "My Extensions"




Sollte es damit einfacher möglich sein neue Erweiterungen für den My Namespace zu erstellen?

Der Link "Learn more about customizung the My Namespace" war leider tot. Auch nachdem ich die 2Gig große VS2008MSDN runtergeladen und installiert hatte.

Gefunden habe ich dann doch noch, wonach ich gesucht hatte. Und zwar die Quelldatei für die WPF My Extensions, die bereits in Orcas auszuwählen sind. Diese befindet sich unter dem Ordner "My Project" in dem Ordner "MyExtensions". Dort wird man dann auch in Zukunft eigene Extensions für den My Namespace ablegen können.

Bleibt nach wie vor die Frage: Woher weiß VisualStudio, dass eine "My Extension" vorhanden ist? Ich habe weder eine Assembly noch ein Item-Template gefunden, dass die Vorlage für die "WPF My Extension" liefert. (Eine entsprechende Frage wie man denn diese My Extensions verwendet habe ich gleich nach der Entdeckung in das MSDN Forum gestellt. Mal gucken ob in nächster Zeit eine Anwort darauf zu finden ist.)

Demnächst erscheint ein Artikel von mir im dotnet.magazin über das Erstellen eines eigenen My Namespaces. Der My.Data Namespace bietet einfachen Zugriff auf eine im Vorhandene Datenbank. Der Artikel ist deswegen nur noch nicht fertig, weil das Deployment von My Extensions Probleme macht. Die Erweiterungen bestehen nämlich aus generiertem Code, der von den Projekteinstellungen abhängt, und kann deswegen nicht als Assembly verteilt werden. Es bleibt also nur noch ein Item-Template übrig für ein simples Deployment. Wenn man eine Vollintegration haben will, muss man wohl oder übel ein eigenes Add-In für Visual Studio schreiben. Das finde ich aber zu Aufwendig.

Auf der Basta 2007 Spring hatt ich mit Jay Schmelzer (Principal Program Manager Visual Basic) meine My.Data Erweiterung vorgestellt und mit ihm darüber gesprochen, wie man eigene My Erweiterungen in Visual Studio integrieren kann. Er versprach dies für Orcas mit einzuplanen.

Schade Jay, das war eigentlich nicht das, was ich mir vorgestellt hatte!



#If _MyType <> "Empty" Then
Namespace My
'''
''' Module used to define the properties that are available in the My Namespace for WPF
'''

'''
_
Module MyWpfExtension
Private m_Computer As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.Devices.Computer)
Private m_User As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.ApplicationServices.User)
Private m_Windows As New ThreadSafeObjectProvider(Of MyWindows)
Private m_Log As New ThreadSafeObjectProvider(Of Global.Microsoft.VisualBasic.Logging.Log)
'''
''' Returns the application object for the running application
'''

Friend ReadOnly Property Application() As Global.System.Windows.Application
Get
Return Global.System.Windows.Application.Current
End Get
End Property
'''
''' Returns information about the host computer.
'''

Friend ReadOnly Property Computer() As Global.Microsoft.VisualBasic.Devices.Computer
Get
Return m_Computer.GetInstance()
End Get
End Property
'''
''' Returns information for the current user. If you wish to run the application with the current
''' Windows user credentials, call My.User.InitializeWithWindowsUser().
'''

Friend ReadOnly Property User() As Global.Microsoft.VisualBasic.ApplicationServices.User
Get
Return m_User.GetInstance()
End Get
End Property
'''
''' Returns the application log. The listeners can be configured by the application's configuration file.
'''

Friend ReadOnly Property Log() As Global.Microsoft.VisualBasic.Logging.Log
Get
Return m_Log.GetInstance()
End Get
End Property
'''
''' Returns the collection of Windows defined in the project.
'''

Friend ReadOnly Property Windows() As MyWindows
_
Get
Return m_Windows.GetInstance()
End Get
End Property
_
_
Friend NotInheritable Class MyWindows
_
Private Shared Function Create__Instance__(Of T As {New, Global.System.Windows.Window})(ByVal Instance As T) As T
If Instance Is Nothing Then
If m_WindowBeingCreated IsNot Nothing Then
If m_WindowBeingCreated.ContainsKey(GetType(T)) = True Then
Throw New Global.System.InvalidOperationException("The window cannot be accessed via My.Windows from the Window constructor.")
End If
Else
m_WindowBeingCreated = New Global.System.Collections.Hashtable()
End If
m_WindowBeingCreated.Add(GetType(T), Nothing)
Return New T()
m_WindowBeingCreated.Remove(GetType(T))
Else
Return Instance
End If
End Function
_
Private Sub Dispose__Instance__(Of T As Global.System.Windows.Window)(ByRef instance As T)
instance = Nothing
End Sub
_
_
Public Sub New()
MyBase.New()
End Sub
Private Shared m_WindowBeingCreated As Global.System.Collections.Hashtable
Public Overrides Function Equals(ByVal o As Object) As Boolean
Return MyBase.Equals(o)
End Function
Public Overrides Function GetHashCode() As Integer
Return MyBase.GetHashCode
End Function
_
Friend Overloads Function [GetType]() As Global.System.Type
Return GetType(MyWindows)
End Function
Public Overrides Function ToString() As String
Return MyBase.ToString
End Function
End Class
End Module
End Namespace
#End If

Montag, 16. Juli 2007

Wie kann ich Controls in einer Form zentrieren?

Ich habe eine sehr einfache Methode herausgefunden um ein beliebiges Steuerelement in einer Form zu zentrieren; also genau mittig anzuzeigen. Wenn die Form verschoben wird, soll natürlich auch die Position des Controls wieder an die neue Mitte verschoben werden.

Meine Lösung erfordert keinen Code!

Man nehme ein TableLayoutPanel und stellt seine Dock Eigenschaft auf Dock.Fill. Nun füllt das Panel die ganze Form aus. Dann reduziert man das Panel auf genau 1 Zelle, d.h. RowCount und ColumnCount = 1. Auchtung eine Zelle kann immer nur 1 Control beinhalten. Wer daher mehere Elemente mittig anzeigen lassen will muss ein weiteres Panel verwenden in das er diese Elemente einfügt. Das Control wird nun auf Anchor = Anchor.None gesetzt. Siehe da das Control befindet sich nun immer in der Mitte!


Freitag, 13. Juli 2007

Achtung! .NET 3.5 Beta1 und .NET 3.5 June CTP inkompatibel!

Achtung bei dem Ausprobieren von Programmen, die unter Orcas erstellt worden sind. Orcas installiert standardmäßig das .NET Framework 3.5 Beta1 vom April 2007. Wer sich bereits die neue Juni CTP runtergeladen hat, wird Programme die LINQ verwenden nicht ausführen können. Es erscheint folgende Fehlermeldung:

Der Typ System.Linq.Func`2 in der Assembly System.Core, Version 2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561924e089 konnte nicht geladen werden.

LINQ macht excessiven Gebrauch vom generischen Delegate Func<>. Dieser wurde nun in einen anderen Namespace verfrachtet. Statt bisher im Namespace System.Linq ist Func<> im Namespace System zu finden. Er ist somit überall verfügbar, ohne dass man einen bestimmten Namespace einbinden muss. Denn im Namespace System befinden sich alle Grundlegenden Datentypen.

Hinzugekommen sind nun auch diverse Überladungen des Action-Delegate. Es ist nämlich bisher nicht möglich einen Delegate Func zu erstellen. Jedoch kann man einen normalen Delegate delegate void Action(T obj) erstellen.

Hier nochmal die vorgefertigten Delegates in der Übersicht:

TReturn Func
TReturn Func
TReturn Func
TReturn Func
TReturn Func

void Action
void Action
void Action
void Action

Montag, 9. Juli 2007

WPF: Wie erstelle ich einen autoskalierenden Text?

Es hat lange gebraucht bis ich mich an WPF (Windows Presentation Foundation) rangewagt habe. Ich erkenne aber immer mehr die Vorzüge von WPF. Z.B. erstelle ich gerade für eine Anwendung einen animierten Splash-Screen bzw. Intro. Das gibt natürlich wesentlich mehr her als ein statisches Bitmap auf dem das Anwendungslogo zu sehen ist. Ich brauche nicht zu erwähnen, dass dies mit GDI+ echt aufwändig wäre und lange nicht so gut aussehen würde wie mit WPF.

Der Splash-Screen soll natürlich voll Vektrobasiert sein, d.h. alle Grafiken, Text und Animationen, sollen sich der Größe des Fensters anpassen. Doch wie schaffe ich es, dass die Schriftgröße eines Textes in einer TextBox automatisch skaliert wird? Ich habe lange gesucht, und keine passende "einfache" Einstellung gefunden, wie z.B. TextBox.Strech = Fill; Hieran sieht man, dass WPF noch nicht ganz ausgereift ist. Es sind zwar viele Grundelemente vorhanden, die man zu anderen komplexen Elementen zusammenstellen kann, aber das muss man halt selber machen. Zudem braucht man eine Menge Wissen über WPF, was man am Anfang natürlich nicht hat. Wenn man also eine DataGridView in WPF braucht, wird man nicht direkt rangehen können, um sich eine eigene DataGridView zu bauchen; auch wenn die dafür erforderlichen Grundelemente vorhanden sind. 1. Braucht man viel Efahrung mit WPF und 2. kostet es enorm viel Zeit.

Aber zurück zu unserem skalierbaren Text.

Wir benötigen zunächst das Tool ExpressionBlend. Meine Lösung kann nicht programmiert werden, sondern benötigt ein Feature von ExpressionBlen

1. Wir nehmen einen TextBlock. (Achtung keine normale TextBox!)
2. Wir schreiben einen Text in unseren TextBlock. Es können auch andere Elemente wie Elipsen, Controls etc. zwischen dem Text stehen. Das ist mit einem TextBlock möglich. Dann die Schriftart und andere Einstellungen setzen.
3. Wir konvertiere nun den TextBlock in einen Path. Menü->Objekt->Pfad->in Pfad konvertieren. Alle Elemente des TextBlock auch der Text wird in einen Pfad konvertiert. Dieser Pfad besteht aus relativen Punkten die Vektororientiert sind und somit voll skalierbar.
4. Wir umgeben den Pfad mit einer ViewBox. Diese ViewBox kann automatisch ihre Childelemente skalieren.
5. Tada! Fertig ist unser automatisch skalierender Text

Nachteil: wenn man den Text ändern will muss man die Schritte erneut durchführen ;-(

Es wäre natürlich auch denkbar eine Skalierung in Abhängigkeit der Fenstergröße per Databinding zu ermöglichen. Hierzu müsste man aber entsprechende Berechnungen anstellen.