OnUtils.dll v1.2

    • Release
    • Open Source

    Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

      OnUtils.dll v1.2

      Name der Klassenbibliothek:
      OnUtils.dll v1.2

      Beschreibung:
      Diese Dll enthält allerlei Helferlein für Dinge, die man immer wieder mal braucht.
      Beispiele:
      Einige WinApi-Wrapper.
      HotKey-Wrapper.
      Extensions für die System.Reflection.Emit.ILGenerator-Klasse, die nur gültige Werte zulassen. Die Emit-Methode dieser Klasse ist nämlich stark überladen und wenn man sich nicht 100%-ig sicher ist, hat man schnell ungültige Parameter angegeben.
      WPF-TextBlock mit Property für formatierten Text.
      StringBuilder mit Einrückung.

      Klassendiagramm:
      Friend und Private Member sind nicht aufgelistet.
      Wenn nicht anders angegeben ist Public anzunehmen.
      Module beinhalten ausschließlich Extensions.
      Bei Extensions ist der Name des ersten Parameters nicht relevant, deshalb habe ich ihn weggelsasen (Wäre sowieso bei jeder "Target").
      Klassendiagramm: Klassendiagramm.txt

      Details und Beispiele

      Class DelayedEvent

      Wird verwendet, um das Ausführen von Code zu verzögern. Beispiel: pointerpointer.com. Die Mausbewegung wird erfasst, aber erst, sobald sie für eine kurze Zeit nicht mehr bewegt wird, wird ein passendes Bild gesucht.
      Verwendung:

      VB.NET-Quellcode

      1. Dim WithEvents Delayer As New DelayedEvent(2000, Me) '2 Sekunden Verzögerung
      2. Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
      3. Delayer.Trigger()
      4. MyBase.OnMouseMove(e)
      5. End Sub
      6. Private Sub DelayElapsed() Handles Delayer.Elapsed
      7. MessageBox.Show("Hallo Welt")
      8. End Sub
      Es kann ein Control angegeben werden ("DelegateControl"), welches verwendet wird, um mittels BeginInvoke die Ausführung des Codes in den Thread des Controls zu delegieren.

      Class DisplayItem(Of T)

      Kapselt einen Wert des Typs T. Überschreibt die ToString-Funktion, und gibt den Inhalt der DisplayText-Property zurück.
      Wird verwendet, um z.B. in eine ListBox Objekte einzufügen, jedoch einen eigenen String anzugeben, der angezeigt wird.
      Verwendung:

      VB.NET-Quellcode

      1. Enum FooMode
      2. StandardMode
      3. FooBarMode
      4. End Enum
      5. '...
      6. Dim Modes As DisplayItem(Of FooMode)() = _
      7. {
      8. New DisplayItem(Of FooMode)("Standard", FooMode.StandardMode), _
      9. New DisplayItem(Of FooMode)("Foobar-Modus", FooMode.FooBarMode)
      10. }
      11. '...
      12. ComboBox_FooModes.DataSource = Modes
      13. '...
      14. Select Case Modes(ComboBox_FooModes.SelectedIndex).Value
      15. Case FooMode.StandardMode
      16. Case FooMode.FooBarMode
      17. End Select


      Class Singleton(Of T)

      Eine Basisklasse für Singletons.
      Als Typenparameter T muss die Klasse angegeben werden, die von Singleton erbt, damit das Pattern korrekt implementiert wird.
      Die in T angegebene Klasse muss einen parameterlosen, öffentlichen Konstruktor besitzen.
      Ausnahmsweise eine sinnvolle Verwendung für die Vererbung von statischen Membern.
      Verwendung:

      VB.NET-Quellcode

      1. Public Class Foo
      2. Inherits Singleton(Of Foo)
      3. Public Property Bar As String
      4. End Class
      5. '...
      6. Sub Test()
      7. 'Instance-Property über Foo zugreifbar, obwohl sie in Singleton(Of T) deklariert ist.
      8. Foo.Instance.Bar = "Hallo Welt"
      9. MessageBox.Show(Foo.Instance.Bar)
      10. End Sub


      Class WeakReference(Of T)

      Eine streng typisierte Variante von System.WeakReference, weil System.WeakReference nur Object unterstützt.
      Verwendung:

      VB.NET-Quellcode

      1. Dim TestReference As New WeakReference(Of String)("Hallo Welt")
      2. GC.Collect()
      3. If TestReference.IsAlive Then
      4. MessageBox.Show(TestReference.Target)
      5. End If


      Class Helpers

      Beinhaltet Hilfsfunktionen, die keine extensions sind.
      Function GetAbsolutePath

      Gibt einen absoluten Pfad zurück, der durch einen relativen Pfad beschrieben wird. ".." wechselt in den übergeordneten Ordner.
      Z.B: "C:\Temp\Foo\..\..\" wird zu "C:\"
      Möglicherweise unnötig. Siehe System.IO.Path.GetFullPath().
      Verwendung:

      VB.NET-Quellcode

      1. Dim SolutionFilePath As String = Helpers.GetAbsolutePath(Path.Combine(Application.StartupPath, "..", "..", "..", "Project.sln"))


      Function GetObjectAddress

      Gibt die Adresse des angegebenen Objektes zurück. Kann für Anschauungszwecke hilfreich sein. Z.B. um jemanden zu erklären, wann ein Objekt ByValue und wann ByRef übergeben wurde *hust!*
      Für Details siehe auch StackOverflow
      Verwendung:

      VB.NET-Quellcode

      1. Sub Test()
      2. Dim Instance As New Foo
      3. Console.WriteLine(Helpers.GetObjectAddress(Instance).ToString)
      4. Change(Instance)
      5. Console.WriteLine(Helpers.GetObjectAddress(Instance).ToString)
      6. End Sub
      7. Sub Change(<ByRef|ByVal> Target As Foo)
      8. Target = New Foo()
      9. End Sub



      Class NopeException

      Wird verwendet, um unmögliche oder ungültige Zustände zu behandeln.
      Hilft auch, um die Meldung, dass eine Funktion keinen Wert zurückgibt, zu beseitigen.
      Verwendung:

      VB.NET-Quellcode

      1. Enum Option
      2. Option1 = 1
      3. Option2 = 2
      4. End Enum
      5. '...
      6. Function Foo(SelectedOption As Option) As Integer
      7. Select Case SelectedOption
      8. Case Option1
      9. Return 1
      10. Case Option2
      11. Return 2
      12. Case Else
      13. 'Wenn diese Exception ausgelöst wird, sollte der Programmierer darauf aufmerksam gemacht werden, weil er irgendwo einen Bug im Programm hat.
      14. 'Der StackTrace verrät wo. Für weitere Details gibt es Message und InnerException.
      15. Throw New NopeException
      16. End Select
      17. End Sub


      Class Resizing

      Beinhaltet Funktionen zum Skalieren von Rechtecken.
      Es gibt Überladungen für Integer/Rectangle, Single/RectangleF und Double/RectangleD
      Verwendung:

      VB.NET-Quellcode

      1. Dim Bpm As New Bitmap("...")
      2. Protected Overrides Sub OnPaint(e As PaintEventArgs)
      3. e.Graphics.DrawImage(Bpm, Resizing.Resize(Bpm.Width, Bpm.Height, 0, 0, Me.ClientSize.Width, Me.ClientSize.Height, Resizing.ResizeMode.UniformToFill))
      4. MyBase.OnPaint(e)
      5. End Sub

      Enum ResizeMode

      Eine Angabe über die Art, wie die Resize-Funktion skaliert.
      • Uniform Das ursprüngliche Rechteck wird so skaliert, dass es vollständig in das Zielrechteck passt. Breite und Höhe des ursprünglichen Rechteckes sind nie größer als Breite und Höhe des Zielrechtecks.
      • UniformToFill Das ursprüngliche Rechteck wird so skaliert, dass es das Zielrechteck vollständig füllt. Breite und Höhe des ursprünglichen Rechteckes sind nie kleiner als Breite und Höhe des Zielrechtecks.


      Structure RectangleD

      Wird von der Resize-Funktion mit Double-Argumenten zurückgegeben.
      Enthält ähnliche Member wie System.Drawing.RectangleF.


      Namespace Emit

      Enum AccessModifiers

      Eine Angabe über die Sichtbarkeit von Membern. Wird zum Beispiel bei der Emit.Macros.EmitEvent-Funktion verwendet.
      • Private Nur in der Klasse sichtbar, in dem der Member deklariert ist.
      • Family Nur in der Klasse sichtbar, in dem der Member deklariert ist, und in Klassen, die von dieser Klasse erben.
      • Assembly Nur in Assembly sichtbar, in dem der Member deklariert ist.
      • FamilyAndAssembly Nur in der Klasse sichtbar, in dem der Member deklariert ist, und in Klassen, die von dieser Klasse erben und sich in der Assembly befinden, in der der Member deklariert ist.
      • FamilyOrAssembly Nur in der Klasse sichtbar, in dem der Member deklariert ist, und in Klassen, die von dieser Klasse erben oder sich in der Assembly befinden, in der der Member deklariert ist.
      • Public Überall sichtbar.


      Class EmitAssembly

      Definiert eine neue, dynamische Assembly in der aktuellen Anwendungsdomäne. Der Name der Assembly wird anhand des im Konstruktor angegebenen Dateipfades ermittelt.
      In der Assembly wird automatisch ein Modul definiert (nicht das selbe wie Module in VB).
      Dynamische Typen, die mit der DefineType-Methode definiert wurden, werden , falls offen, automatisch geschlossen, sobald die Assembly gespeichert wird.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Asm As New EmitAssembly("C:\FooBar.dll")
      2. Dim FooType = Asm.DefineType("Foo.Bar")
      3. Dim DoStuffMethod = FooType.DefineMethod("DoStuff", Reflection.MethodAttributes.Public Or Reflection.MethodAttributes.Static, Reflection.CallingConventions.Standard, GetType(Void), Type.EmptyTypes)
      4. With DoStuffMethod.GetILGenerator
      5. .Ldstr("Hallo Welt")
      6. .Call(PredefinedMembers.System.Windows.Forms.MessageBox.Show_String)
      7. .Pop()
      8. .Ret()
      9. End With
      10. Asm.Save()


      Class PredefinedMembers

      Beinhaltet Member, die häufig beim Generieren von Code verwendet werden.
      Verwendung:

      VB.NET-Quellcode

      1. With Method.GetILGenerator
      2. .Ldarg_0()
      3. .Ldfld(EventField)
      4. .Ldarg_0()
      5. .Ldsfld(PredefinedMembers.System.EventArgs.Empty)
      6. .Callvirt(PredefinedMembers.System.EventHandler.Invoke_Object_EventArgs)
      7. .Ret()
      8. End With
      Im Vergleich dazu:

      VB.NET-Quellcode

      1. With Method.GetILGenerator
      2. .Ldarg_0()
      3. .Ldfld(EventField)
      4. .Ldarg_0()
      5. .Ldsfld(GetType(EventArgs).GetField("Empty", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static))
      6. .Callvirt(GetType(EventHandler).GetMethod("Invoke", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance, Nothing, {GetType(Object), GetType(EventArgs)}, Nothing))
      7. .Ret()
      8. End With


      Namespace IL

      Module IL

      Enthält Extensions für die System.Reflection.Emit.ILGenerator-Klasse. Die Methoden erlauben, im Gegensatz zur ILGenerator.Emit-Methode, nur die richtigen Argumente. Beispielsweise wäre folgender Code ungültig:

      VB.NET-Quellcode

      1. ...GetILGenerator.Emit(OpCodes.Ldc_I4_S, 16)
      16 wird als Integer interpretiert, Ldc_i4_S wird jedoch mit einem Byte verwendet. So können sich Fehler einschleichen.
      Mit den Extensions erkennt man sofort, was verwendet werden muss.
      Verwendung:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. .Ldstr("Hallo")
      3. .Ldstr(" ")
      4. .Ldstr("Welt")
      5. .Call(PredefinedMembers.System.String.Concat_String_String_String)
      6. .Ret()
      7. End With



      Namespace Macros

      Class EmitEvent

      (Siehe Emit.Macros.EmitEvent())

      Module Macros

      Enthält Extensions für häufig verwendete Szenarien die beim Generieren von Code auftreten.
      Sub GetType(ILGenerator, T As Type)

      Legt ein Type-Objekt auf den Auswertungsstapel, das den Typ darstellt, der vom Parameter T angegeben wird.
      Entspricht dem OpCode ldtoken mit dem angegebenen Type, gefolgt von einem Aufruf an Type.GetTypeFromHandle.
      Verwendung:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. .GetType(FooType)
      3. .GetType(BarType)
      4. .Call(GetType(Type).GetMethod("IsAssignableFrom"))
      5. .Brtrue(...)
      6. End With
      Im Vergleich dazu:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. .Ldtoken(FooType)
      3. .Call(GetType(Type).GetMethod("GetTypeFromHandle", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static, Nothing, {GetType(RuntimeTypeHandle)}, Nothing))
      4. .Ldtoken(BarType)
      5. .Call(GetType(Type).GetMethod("GetTypeFromHandle", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static, Nothing, {GetType(RuntimeTypeHandle)}, Nothing))
      6. .Call(GetType(Type).GetMethod("IsAssignableFrom"))
      7. .Brtrue(...)
      8. End With


      Sub DirectCast(ILGenerator, TargetType As Type)

      Castet das Objekt auf dem Auswertungsstapel zu einem anderen Typ durch und beachtet, ob es sich um einen Referenz- oder Wertetyp handelt.
      Entspricht DirectCast() in Visual Basic und dem as-Operator in C#.
      Bei Object wird ein Aufruf an RuntimeHelpers.GetObjectValue(Object) verwendet.
      Bei Wertetypen wird der OpCode unbox_any verwendet.
      Bei Referenztypen wird der OpCode castclass verwendet.
      Verwendung:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. .DirectCast(GetType(Object))
      3. .DirectCast(GetType(Point))
      4. .DirectCast(GetType(IList(Of String)))
      5. End With
      Im Vergleich dazu:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. .Call(GetType(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("GetObjectValue", BindingFlags.Public Or BindingFlags.Static, Nothing, {GetType(Object)}, Nothing))
      3. .Unbox_Any(GetType(Point))
      4. .Castclass(GetType(IList(Of String)))
      5. End With


      Sub CompareEquality(ILGenerator)

      Wie

      VB.NET-Quellcode

      1. CompareEquality(ILGenerator, TypeOfValues As Type)
      mit Nothing als TypeOfValues.

      Sub CompareEquality(ILGenerator, TypeOfValues As Type)

      Vergleicht die obersten beiden Werte auf dem Auswertungsstapel und beachtet die Art des Typs.
      Legt True auf den Auswertungsstapel, wenn die Werte als gleich zu betrachten sind, andernfalls False.
      Ist TypeOfValues Nothing, wird ein Aufruf an Object.Equals(Object, Object) verwendet.
      Bei Wertetypen wird versucht, einen passenden Operator op_Equality (z.B. System.Drawing.Color.op_Equality) zu finden. Wird dieser nicht gefunden, wird Object.Equals(Object, Object) verwendet. Es ist zu beachten, dass dabei Boxing verwendet wird. Dazu wird eine lokale Variable des angegebenen Typs deklariert. Diese Variable wird wiederverwendet, wenn CompareEquality mit dem gleichen Typ erneut aufgerufen wird.
      Bei Interfaces, Klassen (nicht String) und Object, wird der OpCode Ceq verwendet.
      Bei String wird String.Compare(String, String) verwendet und das Ergebnis mit auf Gleichheit mit 0 geprüft.
      Verwendung:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. '2 zu vergleichende Werte auf den Stack legen
      3. .ldarg_1()
      4. .ldarg_2()
      5. 'CompareEquality aufrufen. Beispiele:
      6. .CompareEquality()
      7. .CompareEquality(GetType(Integer))
      8. .CompareEquality(GetType(IObservable(Of )))
      9. .CompareEquality(GetType(Object))
      10. .CompareEquality(GetType(String))
      11. .CompareEquality(FooType)
      12. End With


      Sub GetObjectAddress(ILGenerator)

      Legt die Adresse des Objektes auf dem Auswertungsstapel in Form eines IntPtr auf den Auswertungsstapel ab.
      Dabei werden im ILGenerator zwei LocalBuilders deklariert. Wenn das eine ungewollte Beeinflussung ist, fügen Sie stattdessen einen Aufruf an Helpers.GetObjectAddress() ein.
      Das muss nicht die physikalische Adresse im Arbeitsspeicher sein. Es handelt sich um eine Anschauungsmöglichkeit, um z.B. den Unteschied zwischen Call By Value und Call By Reference zu erklären.
      Verwendung:

      VB.NET-Quellcode

      1. With MethodBuilder.GetILGenerator
      2. Dim PtrLocal = .DeclareLocal(GetType(IntPtr))
      3. .Ldarg_0()
      4. .GetObjectAddress()
      5. .Stloc(PtrLocal)
      6. .Ldloca(PtrLocal)
      7. .Constrained(GetType(IntPtr))
      8. .Call(PredefinedMembers.System.Object.ToString)
      9. .Call(PredefinedMembers.System.Windows.Forms.MessageBox.Show_String)
      10. .Pop()
      11. .Ret()
      12. End With


      Function EmitEvent(Target As TypeBuilder, AccessModifiers As AccessModifiers, IsStatic As Boolean, EventName As String, DelegateType As Type) As EmitEvent

      Erstellt alle nötigen Member für ein Event und gibt diese gekapselt in einem EmitEvent-Objekt zurück.
      Verwendung:

      VB.NET-Quellcode

      1. Dim StuffDoneEvent = TypeBuilder.EmitEvent(AccessModifiers.Public, False, "StuffDone", GetType(EventHandler))
      2. '...
      3. With MethodBuilder.GetILGenerator
      4. Dim NullLabel = .DefineLabel
      5. .Ldarg_0()
      6. .Ldfld(StuffDoneEvent.EventField)
      7. .Brfalse(NullLabel)
      8. .Ldarg_0()
      9. .Ldfld(StuffDoneEvent.EventField)
      10. .Ldarg_0()
      11. .Ldsfld(PredefinedMembers.System.EventArgs.Empty)
      12. .Callvirt(PredefinedMembers.System.EventHandler.Invoke_Object_EventArgs)
      13. .MarkLabel(NullLabel)
      14. .Ret()
      15. End With
      Die erstellten Member bei diesem Beispiel sind:
      • Private StuffDoneEvent As EventHandler
      • Public Sub add_StuffDoneEvent(arg As EventHandler)
      • Public Sub remove_StuffDoneEvent(arg As EventHandler)
      • Public Event StuffDone As EventHandler





      Namespace Extensions

      Module Extensions

      Binhaltet Extensions
      Function AsT(Of T)(Object) As T

      Castet den angegebenen Wert zu T. Funktioniert wie DirectCast, nur als Extension-Methode.
      Unter Visual Basic funktioniert diese Extension nicht für Object... Weil Baum.
      Verwendung:

      VB.NET-Quellcode

      1. Dim A As BaseClass = New SubClass
      2. Dim B As SubClass = A.AsT(Of SubClass)()


      Function AsEnumerable(Of T)(IEnumerable(Of T)) As IEnumerable(Of T)

      Gibt das Objekt, eingeschränkt auf IEnumerable(Of T), zurück. Wird verwendet, wenn Klassen Member mit Namen von IEnumerable(Of T)-Extensions haben.
      Beispiel: Count As Integer ist eine Property von List(Of T), die IList(Of T).Count implementiert. Count(Predicate As Func(Of T, Boolean)) As Integer ist eine Extension von IEnumerable(Of T).

      VB.NET-Quellcode

      1. Dim Items As New List(Of String)
      2. Items.Add(...)
      3. 'Funktioniert nicht, weil Count die Property bezeichnet:
      4. Dim CountLessThan4Chars = Items.Count(Function(Item) Item.Length < 4)
      5. 'Möglich, aber umständlich:
      6. Dim CountLessThan4Chars = DirectCast(Items, IEnumerable(Of String)).Count(Function(Item) Item.Length < 4)
      7. 'Mit dieser Extension:
      8. Dim CountLessThan4Chars = ItemsAsEnumerable.Count(Function(Item) Item.Length < 4)


      Function HByte(UInt16) As Byte

      Gibt die höherwertigen 8 Bits des 16-Bit-Wertes zurück.

      Function LByte(UInt16) As Byte

      Gibt die niederwertigen 8 Bits des 16-Bit-Wertes zurück.

      Function Write(Stream, Value As Byte)

      Schreibt ein einzelnes Byte in den Stream.
      Äquivalent zu:

      VB.NET-Quellcode

      1. Stream.Write({Value}, 0, 1)


      Function Write(Stream, Bytes As Byte())

      Schreibt alle Bytes in den Stream.
      Äquivalent zu:

      VB.NET-Quellcode

      1. Stream.Write(Bytes, 0, Bytes.Length)


      Function Read(Stream) As Byte

      Liest ein einzelnes Byte aus dem Stream.
      Löst eine Streams.EndOfStreamException aus, wenn kein Byte gelesen werden konnte.

      Function Read(Stream, ByteCount As Integer) As Byte()

      Liest die angegebene Anzahl an Bytes aus dem Stream.
      Löst eine Streams.EndOfStreamException aus, wenn weniger Bytes gelesen wurden, als in ByteCount angegeben ist.

      Function Insert(Of T)(T(), Other As T(), TargetIndex As Integer)

      Kopiert alle Elemente aus Other in dieses Array, beginnend bei Index TargetIndex.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Temp = New Byte(7) {}
      2. Temp.Insert({0, 1, 2, 3}, 0)
      3. Temp.Insert({4, 5, 6, 7}, 4)
      4. 'Inhalt von Temp: {0, 1, 2, 3, 4, 5, 6, 7}


      Function SubSequence(Of T)(T(), StartIndex As Integer, Length As Integer) As T()

      Erstellt ein neues Array mit Length Elementen, kopiert die Elemente dieses Arrays, beginnend bei StartIndex, in das neue Array und gibt es zurück.
      Verwendung:

      VB.NET-Quellcode

      1. Dim A = {0, 1, 2, 3, 4, 5, 6, 7}
      2. Dim B = A.SubSequence(2, 3)
      3. 'Inhalt von B: {2, 3, 4}


      Function SubSequence(Of T)(IEnumerable(Of T), StartIndex As Integer, Length As Integer) As IEnumerable(Of T)

      Gibt ein IEnumerable(Of T) zurück, das die Elemente bis StartIndex überspringt und anschließend Length Elemente zurückgibt.
      Verwendung:

      VB.NET-Quellcode

      1. Dim A As New List(Of Integer)
      2. A.AddRange({0, 1, 2, 3, 4, 5, 6, 7})
      3. Dim B = A.SubSequence(2, 3)
      4. 'Inhalt von B: {2, 3, 4} (IEnumerable, nicht Array)


      Function ParseToEnum(Of T As Structure)(String) As T

      Konvertiert den String zum angegebenen Enum-Typ. Angenehmer als System.Enum.Parse und vorallem streng typisiert.
      Verwendung:

      VB.NET-Quellcode

      1. Enum FooMode
      2. StandardMode
      3. FooBarMode
      4. End Enum
      5. '...
      6. 'SettingsElement : XElement oder XmlElement
      7. 'SettingsElement.Element("Mode").Value : String
      8. Dim Mode = SettingsElement.Element("Mode").Value.ParseToEnum(Of FooMode)()


      Function TryParseToEnum(Of T As Structure)(String, OutputValue As T) As Boolean

      Versucht, den String zum angegebenen Enum-Typ zu konvertieren. Ist die Konvertierung erfolgreich, wird der ByRef-Parameter OutputValue mit dem Ergebnis überschrieben und True zurückgegeben, andernfalls wird False zurückgegeben.
      Verwendung:

      VB.NET-Quellcode

      1. Enum FooMode
      2. StandardMode
      3. FooBarMode
      4. End Enum
      5. '...
      6. 'SettingsElement : XElement oder XmlElement
      7. 'SettingsElement.Element("Mode").Value : String
      8. Dim Mode As FooMode = Nothing
      9. If Not SettingsElement.Element("Mode").Value.TryParseToEnum(Of FooMode)(Result) Then
      10. Throw New FooException()
      11. End If


      Function IndexAfter(String, ContainedString As String) As Integer

      Gibt den Index nach dem ersten Vorkommen von ContainedString zurück. Dadurch entfällt das Addieren der Länge des gesuchten Strings.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "abcdefgh"
      2. Dim Index = A.IndexAfter("cd")
      3. 'Inhalt von Index: 4


      Function SubstringAfter(String, ContainedString As String) As String

      Gibt den verbleibenden String nach dem ersten Vorkommen von ContainedString zurück.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "abcdefgh"
      2. Dim SubString = Source.SubstringAfter("cd")
      3. 'Inhalt von SubString: "efgh"


      Function SubstringAfter(String, ContainedString As String, Length As Integer) As String

      Gibt den String nach dem ersten Vorkommen von ContainedString, mit der Länge Length, zurück.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "abcdefgh"
      2. Dim SubString = Source.SubstringAfter("cd", 3")
      3. 'Inhalt von SubString: "efg"


      Function SubstringBetween(String, StartIndex As Integer, EndIndex As Integer) As String

      Gibt den Teil des Strings zurück, der bei StartIndex beginnt und bei EndIndex endet.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "abcdefgh"
      2. Dim SubString = Source.SubstringBetween(1, 5)
      3. 'Inhalt von SubString: "bcdef"


      Function ConcatAll(Of T)(IEnumerable(Of IEnumerable(Of T))) As IEnumerable(Of T)

      Gibt eine Auflistung zurück, die alle Auflistungen dieser Auflistung beinhaltet.
      z.B. aus der 2-rangig verschachtelten Auflistung {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} wird die 1-rangige Auflistung {1, 2, 3, 4, 5, 6, 7, 8, 9}.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Batches As New Queue(Of String())
      2. '...
      3. Dim RemainingElements = Batches.ConcatAll


      Function Prepend(Of T)(IEnumerable(Of T), Item As T) As IEnumerable(Of T)

      Gibt eine Auflistung zurück, die mit Item beginnt und anschließend die Elemente dieser Auflistung beinhaltet.
      z.B. aus {1, 2, 3}.Prepend(4) wird {4, 1, 2, 3}.

      Function Append(Of T)(IEnumerable(Of T), Item As T) As IEnumerable(Of T)

      Gibt eine Auflistung zurück, die Elemente dieser Auflistung und anschließend Item beinhaltet.
      z.B. aus {1, 2, 3}.Append(4) wird {1, 2, 3, 4}.

      Function AllIndicesOf(Of T)(IEnumerable(Of T), SubSequence As IEnumerable(Of T)) As IEnumerable(Of Integer)

      Wie

      VB.NET-Quellcode

      1. AllIndicesOf(Of T)(T(), SubSequence As T()) As IEnumerable(Of Integer)
      nur für allgemeine Auflistungen. Ruft für diese Auflistung und für SubSequence die .ToArray-Funktion auf und ist deshalb etwas langsamer.

      Function AllIndicesOf(Of T)(IEnumerable(Of T), SubSequence As IEnumerable(Of T), StartIndex As Integer) As IEnumerable(Of Integer)

      Wie

      VB.NET-Quellcode

      1. AllIndicesOf(Of T)(T(), SubSequence As T(), StartIndex As Integer) As IEnumerable(Of Integer)
      nur für allgemeine Auflistungen. Ruft für dieese Auflistung und für SubSequence die .ToArray-Funktion auf und ist deshalb etwas langsamer.

      Function AllIndicesOf(Of T)(T(), SubSequence As T()) As IEnumerable(Of Integer)

      Gibt eine Auflistung aller Startindices von SubSequence in diesem Array zurück. Überlappungen werden ebenfalls gefunden. Also {9, 9, 9, 9}.AllIndicesOf({9, 9}) findet nicht nur {0, 2}, sondern {0, 1, 2}.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "Hallo Welt, das ist ein Test"
      2. Dim Indices = Source.AllIndicesOf(" ")
      3. 'Inhalt von Indices: {5, 11, 15, 19, 23}


      Function AllIndicesOf(Of T)(T(), SubSequence As T(), StartIndex As Integer) As IEnumerable(Of Integer)

      Gibt eine Auflistung aller Startindices von SubSequence in diesem Array zurück, beginnend bei StartIndex. Überlappungen werden ebenfalls gefunden. Also {9, 9, 9, 9}.AllIndicesOf({9, 9}) findet nicht nur {0, 2}, sondern {0, 1, 2}.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Source = "Hallo Welt, das ist ein Test"
      2. Dim Indices = Source.AllIndicesOf(" ", 7)
      3. 'Inhalt von Indices: {11, 15, 19, 23} (5 ist nicht dabei, weil erst ab 7 gesucht wird)


      Function Join(Of T)(IEnumerable(Of IEnumerable(Of T)), Concatenator As IEnumerable(Of T)) As IEnumerable(Of T)

      Verknüpft alle Auflistungen in dieser Auflistung mit Concatenator dazwischen.
      Funktioniert wie String.Join(String, String()), aber mit allgemeinen Auflistungen.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Items As New List(Of Integer())
      2. Items.Add({1, 2, 3})
      3. Items.Add({4, 5})
      4. Items.Add({6, 7, 8})
      5. Dim Concatenated = Items.Join({10, 11})
      6. 'Inhalt von Concatenated: {1, 2, 3, 10, 11, 4, 5, 10, 11, 6, 7, 8}


      Function ToVisibility(Boolean) As System.Windows.Visibility

      Gibt Visibility.Visible bei True und Visibility.Collapsed bei False zurück.
      Für ViewModels bei WPF, die anhand einer Bedingung die Sichtbarkeit von Elementen umschalten.



      Namespace Forms

      Class HotKey

      Grundidee von ErfinderDesRades. Leicht abgeändert.
      Löst ein Event aus, sobald die Tastenkombination, die durch die KeyCode-Property angegeben wird, gedrückt wird. Keys.None gibt an, dass auf keine Tastenkombination reagiert werden soll. Das HotKeyPressed-Event wird nur ausgelöst, wenn die EnableRaisingEvent-Property auf True gesetzt ist.
      Verwendung:

      VB.NET-Quellcode

      1. Dim WithEvents ToggleVisibilityHotKey As New Hotkey(Keys.Control Or Keys.Shift Or Keys.Y)
      2. Private Sub ToggleVisibility() Handles ToggleVisibilityHotKey.HotKeyPressed
      3. Me.Visible = Not Me.Visible
      4. End Sub



      Namespace Results

      Results enthalten einen Zustand, der angibt, ob ein Vorgang erfolgreich durchgeführt worden ist.
      Abhängig von der verwendeten Klasse bzw. vom verwendeten Interface können zusätzlich Informationen, wie z.B. der aufgetretene Fehler oder das Ergebnis des Vorgangs, mitgegeben werden.
      Verwendung:

      VB.NET-Quellcode

      1. Public Function TryGetValue() As ValuedResult(Of Integer)
      2. If HasRightsForRegistry Then
      3. '...
      4. Dim ResultValue As Integer
      5. If Not Integer.TryParse(..., ResultValue) Then
      6. Return New ValuedResult(Of Integer)()
      7. End If
      8. Return New ValuedResult(Of Integer)(ResultValue)
      9. Else
      10. Return New ValuedResult(Of Integer)()
      11. End If
      12. End Sub

      Interface IResult

      Definiert ein Ergebnis, das nur enthält, ob der Vorgang erfolgreich abgeschlossen wurde.

      Interface IErroredResult(Of TError)

      Definiert ein Ergebnis, das Informationen über einen aufgetretenen Fehler beinhalten kann.
      Erweitert IResult.

      Interface IValuedResult(Of TValue)

      Definiert ein Ergebnis, das das Ergebnis des Vorganges beinhalten kann.
      Erweitert IResult.

      Class Result

      Ein Ergebnis, das nur enthält, ob der Vorgang erfolgreich abgeschlossen wurde. Wird verwendet, wenn keine zusätzlichen Daten vorhanden/benötigt werden.

      Class ErroredResult(Of TError)

      Erbt von Result und implementiert IErroredResult(Of T).

      Class ValuedResult(Of TValue)

      Erbt von Result und implementiert IValuedResult(Of T).

      Class ValuedErroredResult(Of TValue, TError)

      Erbt von Result und implementiert IErroredResult(Of T) und IValuedResult(Of T).
      Der Konstruktor "Sub New(Undefined As Object, Success As Boolean)" wird verwendet, wenn sich TValue und TError soweit ähneln, dass der Compiler nicht zwischen "Sub New(NewValue As TValue)" und "Sub New(NewError As TError)" unterscheiden kann. Ist Success true, wird der übergebene Wert als TResult betrachtet, andernfalls als TError.


      Namespace Streams

      Class EndOfStreamException

      Wird ausgelöst, wenn ein Stream unerwartet zuende ist.

      Class IndentStringBuilder

      Kapselt einen StringBuilder und bietet die Möglichkeit, Zeilen einzurücken.
      Verwendung:

      VB.NET-Quellcode

      1. Class TreeNode
      2. Dim Name As String
      3. Dim SubNodes As IEnumerable(Of TreeNode)
      4. Overrides Sub ToString()
      5. Dim SB As New IndentStringBuilder
      6. Me.ToString(SB)
      7. Return SB.ToString
      8. End Sub
      9. Private Sub ToString(SB As IndentStringBuilder)
      10. SB.AppendLine(Name)
      11. SB.AppendLine("{")
      12. SB.Indent()
      13. For Each i In SubNodes
      14. i.ToString(SB)
      15. Next
      16. SB.UnIndent()
      17. SB.AppendLine("}")
      18. End Sub
      19. End Class
      Ein Beispiel für eine Ausgabe könnte sein:

      Quellcode

      1. Namespace A
      2. {
      3. Class B
      4. {
      5. }
      6. Class C
      7. {
      8. }
      9. Namespace D
      10. {
      11. Class E
      12. {
      13. }
      14. Namespace F
      15. {
      16. Class G
      17. {
      18. }
      19. }
      20. Class H
      21. {
      22. }
      23. }
      24. }


      Class StringStream

      Beinhaltet Methoden zum Lesen von Strings in Blöcken.
      Kann z.B. verwendet werden, um auf die Schnelle Json zu parsen.
      Verwendung:

      VB.NET-Quellcode

      1. Dim Progresses As New List(Of Progress)
      2. Const QuoteChar As Char = """"c
      3. Dim S As New StringStream(...)
      4. Do
      5. S.ReadToNext(QuoteChar)
      6. S.ReadToNext(QuoteChar)
      7. S.ReadToNext(":"c)
      8. Dim Id = Convert.ToInt32(S.ReadToNext(","c))
      9. S.ReadToNext(QuoteChar)
      10. S.ReadToNext(QuoteChar)
      11. S.ReadToNext(QuoteChar)
      12. Dim Name = S.ReadToNext(QuoteChar)
      13. S.ReadToNext(QuoteChar)
      14. S.ReadToNext(QuoteChar)
      15. S.ReadToNext(":"c)
      16. Dim Status = Convert.ToInt32(S.ReadToNext(","c))
      17. S.ReadToNext(QuoteChar)
      18. S.ReadToNext(QuoteChar)
      19. S.ReadToNext(":"c)
      20. Dim IsFinal = Convert.ToBoolean(S.ReadToNext(","c))
      21. S.ReadToNext(QuoteChar)
      22. S.ReadToNext(QuoteChar)
      23. S.ReadToNext(":"c)
      24. Dim CategoryId = Convert.ToInt32(S.ReadToNext(","c))
      25. S.ReadToNext("}"c)
      26. Progresses.Add(New Progress(Id, Name, Status, IsFinal, CategoryId))
      27. Loop Until S.EndOfStream


      Class BufferDataReceivedEventArgs(Of T)

      Enthält empfangene Daten, die von DataBuffer verarbeitet werden bzw. wurden.

      Interface IDataBufferSource(Of T)

      Definiert eine Datenquelle für DataBuffer.
      Das DataReceived-Event wird ausgelöst, wenn die Quelle neue Daten erhalten hat.
      Durch die Flush-Methode muss das DataReceived-Event für eventuell zurückgebliebene Daten erneut aufgerufen werden.
      Die CompareData-Funktion muss true zurückgeben, wenn die beiden übergebenen Objekte als gleich zu betrachten sind.
      SerialPortDataSource implementiert IDataBufferSource(Of Byte).

      Class DataBuffer(Of T)

      Puffert Daten, bis ein vollständiger Block empfangen wurde und löst anschließend das BlockReceived-Event aus.
      Ein Block wird als vollständig betrachtet, sobald die Folge von Daten, die in DelimiterData angegeben ist, empfangen wurde.
      Wird die Flush-Methode aufgerufen, wird zuerst die Flush-Methode der Datenquelle aufgerufen, was zur folge haben kann, dass weitere Blöcke vollständig empfangen werden, und anschließend, wenn vorhanden, das BlockReceived-Event für die aktuell gepufferten Daten ausgelöst, auch wenn der Block noch unvollständig ist. Beim Nächsten empfangenen Delimiter wird das BlockReceived-Event nur für die Daten ausgelöst, die nachher empfangen wurden, und nicht für den kompletten letzten Block.
      Verwendung:

      VB.NET-Quellcode

      1. Dim WithEvents Buffer As New DataBuffer(Of Byte)(New SerialPortDataSource(New SerialPort("COM1")), {1, 2, 3, 4})
      2. Private Sub BlockReceived(sender As Object, e As BufferDataReceivedEventArgs(Of Byte)) Handles Buffer.BlockReceived
      3. MessageBox.Show(String.Join(", ", e.Data))
      4. End Sub


      Class SerialPortDataSource

      Stellt ein SerialPort als Datenquelle für DataBuffer bereit.
      Verwendungsbeispiel siehe DataBuffer.


      Namespace WinApi

      Class DwmColor

      Stellt eine Farbe mit Rot- Grün und Blaukanal und zusätzlichem Alpha-Kanal dar.
      Diese Klasse ist unabhängig von Windows Forms und WPF, kann aber mit den entsprechenden Funktionen (ToFormsColor und ToWpfColor) in eine dort verwendbare Farbe konvertiert werden.

      Class DwmColorization

      Enthält die API-Funktion DwmGetColorizationParameters (dwmapi.dll). Die GetDwmColorization-Funktion gibt nur die Hauptfarbe zurück.

      Enum MouseAction

      Eine Maus-Aktion für WinApi.MouseAndKeyboard.

      Class MouseAndKeyboard

      Enthält die API-Funktion mouse_event (user32.dll). Damit kann die Maus gesteuert werden.

      Class HotKeys

      Enthält API-Funktionen RegisterHotKey und UnregisterHotKey (user32.dll) zum Registrieren von HotKeys. Wird von Forms.HotKey verwendet.

      Class SetWindowPos

      Enthält die API-Funktion SetWindowPos (user32.dll). Damit können Fenster fremder Prozesse (Fensterhandle muss bekannt sein) neu positioniert und in den Vorder- bzw. Hintergrund gerückt werden.
      Details siehe MSDN.
      Enum Flags

      Enthält zusätzliche Angaben über Aktionen der SetWindowPos-Funktion. Beispielsweise kann die Größenangabe ignoriert werden, wodurch das Fenster nur verschoben wird.

      Enum ZPosition

      Eine Angabe darüber, in welche Z-Ebene das Fenster verschoben wird.



      Namespace Wpf

      Enthält Klassen, die vorwiegend mit WPF verwendet werden. Damit dieser Namespace in einem XAML-Dokument verfügbar ist, muss folgendes XML-Namespace importiert werden:

      XML-Quellcode

      1. xmlns:o="clr-namespace:OnUtils.Wpf;assembly=OnUtils"
      (o hier als Beispiel)

      Class DelegateCommand

      Implementiert ICommand und führt einen Delegaten aus. CanExecute gibt immer True zurück.
      Kann vererbt werden, um genauere Anpassungen durchzuführen (Siehe Wpf.EnableableDelegateCommand).
      Verwendung:

      XML-Quellcode

      1. <Button Command="{Binding Path=DoStuffCommand}">Do stuff</Button>

      VB.NET-Quellcode

      1. Public ReadOnly Property DoStuffCommand As ICommand
      2. Get
      3. Static Temp As New DelegateCommand(AddressOf DoStuff)
      4. Return Temp
      5. End Get
      6. End Property
      7. Private Sub DoStuff()
      8. End Sub


      Class EnableableDelegateCommand

      Erbt von Wpf.DelegateCommand und überschreibt die CanExecute-Funktion. Durch das Setzen der IsEnabled-Property kann das Ergebnis bestimmt werden.
      Praktisch ist, dass viele WPF-Steuerelemente prüfen, ob das zugeordnete Command ausgeführt werden kann. Ein Button wird automatisch ausgegraut, wenn CanExecute False zurückgibt.
      Verwendung:

      XML-Quellcode

      1. <TextBox Text="{Binding Path=DownloadUrl}"/>
      2. <Button Command="{Binding Path=DownloadFileCommand}">Datei herunterladen</Button>

      VB.NET-Quellcode

      1. Dim _DownloadUrl As String
      2. Public Property DownloadUrl As String
      3. Get
      4. Return _DownloadUrl
      5. End Get
      6. Set(ByVal value As String)
      7. _DownloadUrl = value
      8. 'Download kann nur gestartet werden, wenn die URL gültig ist.
      9. _DownloadFileCommand.IsEnabled = UrlIsValid(_DownloadUrl)
      10. End Set
      11. End Property
      12. Dim _DownloadFileCommand As New DelegateCommand(AddressOf DownloadFile, False)
      13. Public ReadOnly Property DownloadFileCommand As ICommand
      14. Get
      15. Return _DownloadFileCommand
      16. End Get
      17. End Property
      18. Private Sub DownloadFile()
      19. End Sub


      Class FormatTextBlock

      Kann formatierten Text anzeigen. Dazu wird die FormatText-Property an eine Auflistung von System.Windows.Documents.Inline-Objekten gebunden.
      Verwendung:

      XML-Quellcode

      1. <o:FormatTextBlock FormatText="{Binding Path=Inlines}"/>

      VB.NET-Quellcode

      1. Public ReadOnly Property Inlines As IEnumerable(Of Inline)
      2. Get
      3. Return TextInlines.Create( _
      4. "Das ist ", _
      5. New Run("kursiver") With {.FontStyle = FontStyles.Italic}, _
      6. " Text", _
      7. New LineBreak, _
      8. "Es können auch ", _
      9. New InlineUIContainer(New Image With {.Width = 16, .Height = 16, .Source = New BitmapImage(New Uri("http://www.vb-paradise.de/wcf/images/smilies/biggrin.png"))}), _
      10. " eingefügt werden.")
      11. End Get
      12. End Property
      Dieses Beispiel ergibt:


      Class TextInlines

      Wird mit Wpf.FormatTextBlock verwendet, um Inlines für Objekte zu erstellen.
      Out of the box werden Strings, Inlines und Auflistungen von Inlines unterstützt. Mithilfe der InlineSelector-Property können auch andere Objekte unterstützt werden.
      Das Beispiel von Wpf.FormatTextBlock kann beispielsweise so verändert werden:

      VB.NET-Quellcode

      1. TextInlines.InlineSelector = _
      2. Function(Item As Object)
      3. If TypeOf Item Is ImageSource Then
      4. Return {New InlineUIContainer(New Image With {.Width = 16, .Height = 16, .Source = DirectCast(Item, ImageSource)})}
      5. End If
      6. Throw New NopeException
      7. End Function
      8. '...
      9. Return TextInlines.Create( '...
      10. New BitmapImage(New Uri("http://www.vb-paradise.de/wcf/images/smilies/biggrin.png")), _ '...


      Class ViewModelBase

      Eine Basisklasse für ViewModels. Implementiert INotifyPropertyChanged und stellt Member bereit, die das Arbeiten damit erleichtern.
      Verwendung:

      VB.NET-Quellcode

      1. Class MainWindowViewModel
      2. Inherits ViewModelBase
      3. Dim _Percentage As Double
      4. Public Property Percentage As Double
      5. Get
      6. Return _Percentage
      7. End Get
      8. Set(value As Double)
      9. 'Ändert _Percentage, wenn sich der Wert von value unterscheidet und löst das PropertyChanged-Event mit den Namen "Percentage" und "PercentageFormatString" aus.
      10. ChangeIfDifferent(_Percentage, value, "Percentage", "PercentageFormatString")
      11. End Set
      12. End Property
      13. Public ReadOnly Property PercentageFormatString As String
      14. Get
      15. Return Math.Round(_Percentage * 100, 0).ToString & " %"
      16. End Get
      17. End Property
      18. End Class


      Class ViewModelBase(Of T)

      Erbt von Wpf.ViewModelBase und enthält zusätzlich eine Property "Target" vom Typ T.
      Gedacht für ViewModels, die zusätzlich zu den ursprünglichen Objekten noch formatierte Strings zur Anzeige oder Commands enthalten, an die gebunden wird.
      Beispiel:

      VB.NET-Quellcode

      1. Class MainWindowViewModel
      2. Inherits ViewModelBase
      3. Dim _Servers As IEnumerable(Of Server)
      4. Public Property Servers As IEnumerable(Of Server)
      5. Get
      6. Return _Servers
      7. End Get
      8. Set(value As IEnumerable(Of Server))
      9. If ChangeIfDifferent(_Servers, value, "Servers") Then
      10. _ServerViewModels = _Servers.Select(Function(Item) New ServerViewModel(Item))
      11. OnPropertyChanged("ServerViewModels")
      12. End If
      13. End Set
      14. End Property
      15. Dim _ServerViewModels As IEnumerable(Of ServerViewModel)
      16. Public ReadOnly Property ServerViewModels As IEnumerable(Of ServerViewModel)
      17. Get
      18. Return _ServerViewModels
      19. End Get
      20. End Property
      21. End Class
      22. Class ServerViewModel
      23. Inherits ViewModelBase(Of Server)
      24. Public ReadOnly Property ShutDownCommand As ICommand
      25. Get
      26. Static Temp As New DelegateCommand(Sub() Target.ShutDown())
      27. Return Temp
      28. End Get
      29. End Property
      30. Public ReadOnly Property DisplayText As String
      31. Get
      32. Return String.Format("...", Target.Name, Target.UpTime, ...)
      33. End Get
      34. End Property
      35. Public Sub New(NewTarget As Server)
      36. MyBase.New(NewTarget)
      37. End Sub
      38. End Class


      Class ToolTipButton

      Ein Button, der beim Klick, falls vorhanden, den zugeordneten ToolTip öffnet.
      Es ist zwar möglich, im XAML Elemente in die ToolTip-Property einzufügen, ohne explizit ein ToolTip-Objekt zu erstellen, aber die ToolTip-Propertymussauf ein ToolTip-Objekt festgelegt werden. Andernfalls wird eine Wpf.ToolTipException ausgelöst.
      Verwendung:

      XML-Quellcode

      1. <o:ToolTipButton
      2. Grid.Row="0"
      3. Grid.Column="2"
      4. Grid.RowSpan="2"
      5. Margin="4"
      6. Padding="10,0"
      7. Visibility="{Binding Path=EncryptionHelpButtonVisibility}"
      8. >
      9. <o:ToolTipButton.ToolTip>
      10. <ToolTip>
      11. <TextBlock>Sie sollten mindestens eine von beiden Optionen auswählen.</TextBlock>
      12. </ToolTip>
      13. </o:ToolTipButton.ToolTip>
      14. ?
      15. </o:ToolTipButton>






      Verwendete Programmiersprache:
      Visual Basic .NET (IDE: Visual Basic 2010 Express)

      Systemanforderungen:
      .Net Framework 4.0

      Download:
      OnUtils v1.2.zip (44 KB gepackt, 176 KB entpackt)
      Source.zip (57 KB gepackt, 240 KB entpackt)

      Lizenz/Weitergabe:
      Darf beliebig weitergegeben werden, solange dafür kein Geld verlangt wird.
      Einkompilieren erlaubt (Erwähnung wäre nett).
      Dekompilieren erlaubt, Quellcode ist aber dabei und ich beantworte Fragen zum Code aber auch gerne selbst.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils

      Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Niko Ortner“ ()

      Class EnableableDelegateCommand im Namespace Wpf hinzugefügt.
      Auf fast alle Methoden das DebuggerStepThrough-Attribut angewandt, damit man nicht jedes Mal in die Dll hineindebuggt.

      Und mir ist aufgefallen, dass ich unter "Details und Beispiele" nicht alle Bereiche formatiert habe. Muss das noch ändern.
      Edit: Ok.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Niko Ortner“ ()

      Class DisplayItem hinzugefügt.
      Class Singleton(Of T) hinzugefügt.
      Class WeakReference(Of T) hinzugefügt.
      In Module Extensions Methoden hinzugefügt.
      StringStream.PeekToNext- und StringStream.ReadToNext-Funktionen hinzugefügt.
      In Module Emit.Macros.Macros Bug in CompareEquality-Methode behoben.
      Parameterlosen Konstruktor für EndOfStreamException hinzugefügt.
      Removed Herobrine.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils
      Ein asynchroner DelegateCommand wäre noch wünschenswert.

      EDIT: Ach ja und du könntest das ganze auch noch auf GitHub und ggf. NuGet hochladen. Mir ist noch eingefallen, dass du noch ein paar einfache IValueConverter hinzufügen könntest (​FalseAtNullValue, HiddenAtNullValue BooleanToString (ggf. mit Eigenschaften, die dann zurückgegeben werden), FalseAtEmptyString, FalseAtZero, BytesToString (Rundet Bytes auf), ​ListOfStringToString)
      Mfg
      Vincent

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VincentTB“ ()

      @VincentTB

      asynchroner DelegateCommand
      Gute Idee. Ist Erledigt.

      HiddenAtNullValue
      Gibt's anscheinend schon als BooleanToVisibilityConverter
      stackoverflow.com/questions/50…t-in-wpf-ivalueconverters

      Was soll ich in der IValueConverter.ConvertBack-Funktion machen, wenn es nicht möglich ist, zurückzukonvertieren?
      z.B. FalseAtEmptyString:
      Leerer String -> False
      False -> Leerer String
      Nicht-leerer String -> True
      True -> ?

      ​FalseAtNullValue
      Ist nicht so einfach, wie es sich anhört. stackoverflow.com/questions/65…e-is-equal-to-its-default

      BytesToString
      Meinst Du damit, dass z.B. 1024 nicht als "1024", sondern als "1 KiB" angezeigt wird?

      ListOfStringToString
      Einfach alle Strings mit Leerzeichen oder Beistriche verknüpfen?


      Edit: Bezüglich GitHub.
      Das könnte ich später mal probieren, aber mir ist das nicht so ganz geheuer. Ich habe meinen Quellcode lieber immer hier auf dem Computer. Und ich schreibe mir immer auf, was ich ändere bzw. hinzufüge, weil ich beim nächsten Update das Klassendiagramm und die Beispiele aktualisieren muss. Und ich weiß nicht, wie das auf GitHub funktioniert.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Niko Ortner“ ()

      Naja, bei GitHub siehst du zusätzlich zum Quellcode, was bei jedem Push geändert wurde. Ein Push ist eine Gruppe von Änderungen, welcher du auch einen Namen geben kannst. Wenn du es richtig machst, geht das alles über VS mithilfe des Team Explorers und du kannst dann halt jede Änderung bei Bedarf Rückgängig machen & auch immer die genauen Veränderungen nachschauen (sind im Grunde genommen Backups). Ich finds richtig praktisch, da nie wieder was verloren gehen kann. Und der Quellcode bleibt auch auf deinem PC, er wird nur mit dem Server synchronisiert.
      zum DelegateCommand: guggemol DelegateConverter
      Zum ICommandPattern gehört nämlich aus gutem Grund, dass ICommand.CanExecute zumindest optional wirklich eine TestMethode ausführt.
      Wenn deine Implementation generell immer True zurückgibt, beschneidet sie das Interface, und führt diesen Teil ad Absurdum.
      Also imo unabdingbar, dass man einem DelegateCommand einen Delegaten auch fürs CanExecute mitgeben können muss - zumal im 2013er DelegateCommand das auch so implementiert ist.
      Nochma zum "guten Grund": Ein Wpf-Button disabled sich selbstatig, solange (nicht länger) er an ein Command gebunden ist, das zeitweilig nicht ausführbar ist. Gugge verlinkten Sample-Download.



      Zum StringStream gugge vlt. auch mal System.IO.StringReader, der leistet einen Großteil dessen, was du da auch implementierst.



      Zur RectangleD - Structure sieh dir mal System.Windows.Rect an (hihi!)

      Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()

      @Niko Ortner
      HiddenAtNullValue -> return value == null ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;

      FalseAtNullValue -> return value != null;

      BytesToString -> Ja genau, bei sowas wäre es auch schön, wenn im ValueConverter eine statische Eigenschaft dazu wäre.

      Spoiler anzeigen

      C#-Quellcode

      1. static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
      2. static string SizeSuffix(Int64 value)
      3. {
      4. if (value < 0) { return "-" + SizeSuffix(-value); }
      5. if (value == 0) { return "0.0 bytes"; }
      6. int mag = (int)Math.Log(value, 1024);
      7. decimal adjustedSize = (decimal)value / (1L << (mag * 10));
      8. return string.Format("{0:n1} {1}", adjustedSize, SizeSuffixes[mag]);
      9. }
      10. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      11. {
      12. return SizeSuffix((Int64)value);
      13. }

      ListOfStringToString -> return string.Join(", ", lst.ToArray()); (Vorher gucken, ob die Liste nicht nichts ist)

      FalseAtEmptyString ->
      Spoiler anzeigen

      C#-Quellcode

      1. if (value == null) { return false; }
      2. return !string.IsNullOrEmpty(value.ToString());


      Wenn du nicht zurück konvertieren kannst, gib Binding.DoNothing zurück.

      Bei GitHub könnte man auch direkt Codeverbesserungsvorschläge machen, welche du dann akzeptieren kannst und dann sofort wirksam werden.
      Mfg
      Vincent

      @nafets3646
      Das hört sich gut an, aber funktioniert das auch mit der Express-Version von VisualStudio? Da lassen sich nämlich keine AddOns installieren.

      @ErfinderDesRades
      Dafür gibt es Wpf.EnableableDelegateCommand. Wpf.DelegateCommand ist für die Situationen gedacht, bei denen das Command auf jeden Fall ausgeführt werden kann.

      Zum StringReader:

      Da muss ich Dir fast ein bisschen widersprechen. Gleich ist eigentlich nur der eine Konstruktor, Peek(), Read() und ReadToEnd().
      Read mit Buffer-Arrays wollte ich gezielt vermeiden.
      Eventuell könnte ich noch die Async-Methoden einbauen.

      Wegen System.Windows.Rect.
      Wenn man die Structure in einem Windows-Forms-Projekt verwenden will, muss man zusätzlich einen Verweis auf die... PresentationCore.dll war das glaub ich... hinzufügen. Um das zu vermeiden hab ich eine eigene Structure genommen.
      Klar, umgekehrt ist es im Moment sowieso der Fall, dass man in einem Wpf-Projekt einen Verweis auf System.Drawing.dll machen muss, wenn man Resize mit Integer oder Single verwenden will, aber ich denke, dass diese Funktionen bei WPF eher selten verwendung finden werden (da gibt's genug andere Möglichkeiten, ein Layout zu basteln). Und wenn man es in WPF trotzdem verwenden möchte, kann man immer noch die Überladung mit Double verwenden, denn in WPF ist fast alles, was das Layout betrifft, mit Double gelöst.
      Siehe dazu auch WinApi.DwmColor.ToFormsColor() und WinApi.DwmColor.ToWpfColor(). Man kann jeweils das verwenden, was man im aktuellen Projekt braucht, ohne dass man einen Verweis auf die jeweils anderen Assemblies benötigt.

      @VincentTB
      HiddenAtNullValue: Ach so, einen mit Null. Ok, werde ich einbauen.

      FalseAtNullValue: Das geht aber nur mit Verweistypen. DirectCast(0, Object) Is Nothing bzw. (Object)0 == null ergibt immer False. Das gilt für alle Wertetypen, und ich glaube sogar für Nullables.

      BytesToString: So ähnlich habe ich das auch. Dein Beispiel hat mich aber noch auf ein paar Ideen gebracht, die ich noch einbauen werde.

      ListOfStringToString, FalseAtEmptyString: Ok, wird gemacht.

      Binding.DoNothing: Danke für den Hinweis. Das werde ich verwenden.


      GitHub werde ich mir noch überlegen. Ich muss da erst mal ein bisschen experimentieren.


      Edit: @ErfinderDesRades
      Ein Wpf-Button disabled sich selbstatig
      Jup, das wusste ich. In solchen Situationen verwende ich dann ein EnableableDelegateCommand.
      Aber trotzdem danke für den Hinweis.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils
      @Niko Ortner
      Das weiß ich leider nicht (wenn du einen "Team Explorer"-Tab hast, dann funktioniert es auf jeden Fall, ansonsten müsstest du halt mit GitHub For Windows arbeiten (nicht mehr Arbeit, einfach ein weiteres Programm). Der Aufwand ist jedenfalls minimal und man kriegt ein hochwertiges Backup-System, eine einfache Möglichkeit, zusammen zu arbeiten und ein praktisches Error-Report-System.

      Niko Ortner schrieb:

      Das geht aber nur mit Verweistypen.

      Ich weiß. Soll eine einfache Logik sein, dass wenn etwas Nothing ist es false zurückgibt, andernfalls true.
      Value bezieht sich auf den Wert der Eigenschaft. Ist vielleicht ein bisschen blöd benannt, richtig wäre dann ​FalseAtNull bzw. für VB ​FalseAtNothing.
      Mfg
      Vincent

      Niko Ortner schrieb:


      Ein Wpf-Button disabled sich selbstatig
      Jup, das wusste ich. In solchen Situationen verwende ich dann ein EnableableDelegateCommand.
      Aber dein EnableableDelegateCommand tickt anners als standardmäßig vorgesehen.
      Bei deinem musst du explizit das Command disablen, beim "richtigen" ICommandPattern weiß das Command selbst, obs enabled ist.
      Und auch weils ab 2013 auch im Framework-DelegateCommand so umgesetzt ist, würde ich empfehlen, dein DelegateCommand diesem Standard anzupassen, damit später beim Wechsel auf 2013 und höher möglichst wenig Anpassung zu leisten ist.
      @ErfinderDesRades
      Ich verstehe, was Du meinst. Ich habe auch darüber nachgedacht, bin aber zum Schluss gekommen, dass man genausogut eine Property reinbauen kann.
      Ich gehe mal meinen Gedankengang durch, den ich da hatte.
      Das ICommand-Interface sieht so aus:

      VB.NET-Quellcode

      1. Interface ICommand
      2. Event CanExecuteChanged(sender As Object, e As EventArgs)
      3. Function CanExecute(parameter As Object) As Boolean
      4. Sub Execute(parameter As Object)
      5. End Interface
      Execute ist klar. Das führt einfach den Delegaten aus, der beim Konstruktor angegeben wurde.
      CanExecute kann dann ebenfalls einen Delegaten ausführen, der beim Konstruktor angegeben wurde.
      Soweit sogut.
      Aber um das Event CanExecuteChanged auslösen zu können, muss ich eine Methode öffentlich verfügbar machen, die dann von außen aufgerufen wird. Das heißt, wenn ich Daten ändere, von denen das Command abhängt, muss ich erst wieder eine Methode aufrufen, die das Event auslöst, was den Button (o.ä.) veranlasst, CanExecute aufzurufen, was den Delegaten aufruft, was die Daten prüft.
      Da hab ich mir gedacht, dass es eigentlich auch kein Problem wäre, wenn man selbst eben jene Funktion zum Prüfen der Daten aufruft und das Ergebnis an eine Property zuweist. Man muss ja sowieso was machen, weil der Button sowieso nicht von selbst weiß, dass sich was geändert hat.
      Den einzigen Unterschied, den man hat, ist, dass der "parameter"-Parameter der CanExecute-Funktion ignoriert wird.
      Wenn man aber ein Command benötigt, bei dem das Ergebnis von CanExecute durch einen Delegaten bestimmt werden soll bzw. wenn man den Parameter trotzdem benötigt, kann man auch von DelegateCommand erben:

      VB.NET-Quellcode

      1. Public Class FooCommand
      2. Inherits DelegateCommand
      3. Dim CanExecuteCallback As Func(Of Object, Boolean)
      4. Public Sub New(NewTarget As Action, NewCanExecuteCallback As Func(Of Object, Boolean))
      5. MyBase.New(NewTarget)
      6. CanExecuteCallback = NewCanExecuteCallback
      7. End Sub
      8. Public Sub New(NewTarget As Action(Of Object), NewCanExecuteCallback As Func(Of Object, Boolean))
      9. MyBase.New(NewTarget)
      10. CanExecuteCallback = NewCanExecuteCallback
      11. End Sub
      12. Public Shadows Sub OnCanExecuteChanged()
      13. MyBase.OnCanExecuteChanged()
      14. End Sub
      15. Public Overrides Function CanExecute(Parameter As Object) As Boolean
      16. Return CanExecuteCallback(Parameter)
      17. End Function
      18. End Class
      Als Beispiel:
      FooCommand

      VB.NET-Quellcode

      1. Class MainWindowViewModel
      2. Inherits ViewModelBase
      3. Public ReadOnly Property AddItemCommand As ICommand
      4. Get
      5. Static Temp As New DelegateCommand(Sub() _Items.Add(DateTime.Now.ToLongTimeString))
      6. Return Temp
      7. End Get
      8. End Property
      9. Public ReadOnly Property RemoveItemCommand As FooCommand
      10. Get
      11. Static Temp As New FooCommand(Sub() _Items.RemoveAt(0), Function(Parameter) _Items.Count > 0) 'Der Parameter wird hier eigentlich auch nicht benötigt.
      12. Return Temp
      13. End Get
      14. End Property
      15. Public ReadOnly Property DoStuffCommand As FooCommand
      16. Get
      17. Static Temp As New FooCommand(Sub() DoStuff(), Function(Parameter) _Items.Count > 0)
      18. Return Temp
      19. End Get
      20. End Property
      21. Dim _Items As New ObservableCollection(Of String)
      22. Public ReadOnly Property Items As ObservableCollection(Of String)
      23. Get
      24. Return _Items
      25. End Get
      26. End Property
      27. Public Sub New()
      28. AddHandler _Items.CollectionChanged, Sub()
      29. DoStuffCommand.OnCanExecuteChanged()
      30. RemoveItemCommand.OnCanExecuteChanged()
      31. End Sub
      32. End Sub
      33. Public Sub DoStuff()
      34. MessageBox.Show(String.Join(", ", _Items))
      35. End Sub
      36. End Class
      Bzw. so, wenn man den Parameter nicht benötigt.
      EnableableDelegateCommand

      VB.NET-Quellcode

      1. Class MainWindowViewModel
      2. Inherits ViewModelBase
      3. Public ReadOnly Property AddItemCommand As ICommand
      4. Get
      5. Static Temp As New DelegateCommand(Sub() _Items.Add(DateTime.Now.ToLongTimeString))
      6. Return Temp
      7. End Get
      8. End Property
      9. Public ReadOnly Property RemoveItemCommand As EnableableDelegateCommand
      10. Get
      11. Static Temp As New EnableableDelegateCommand(Sub() _Items.RemoveAt(0), _Items.Count > 0) 'Entweder so
      12. Return Temp
      13. End Get
      14. End Property
      15. Public ReadOnly Property DoStuffCommand As EnableableDelegateCommand
      16. Get
      17. Static Temp As New EnableableDelegateCommand(Sub() DoStuff(), False) 'Oder so (es sind standardmäßig keine Items vorhanden)
      18. Return Temp
      19. End Get
      20. End Property
      21. Dim _Items As New ObservableCollection(Of String)
      22. Public ReadOnly Property Items As ObservableCollection(Of String)
      23. Get
      24. Return _Items
      25. End Get
      26. End Property
      27. Public Sub New()
      28. AddHandler _Items.CollectionChanged, Sub()
      29. DoStuffCommand.IsEnabled = (_Items.Count > 0)
      30. RemoveItemCommand.IsEnabled = (_Items.Count > 0)
      31. End Sub
      32. End Sub
      33. Public Sub DoStuff()
      34. MessageBox.Show(String.Join(", ", _Items))
      35. End Sub
      36. End Class
      Kommt, finde ich, inetwa aufs gleiche, solange man den Parameter nicht braucht.


      @VincentTB
      Ok, ich hab mir halt gedacht, dass das unerwartet wäre.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils
      jo, ist ein kleiner Unterschied.
      Mittm Framework-DelegateCommand ab 2013 wird man das LöschCommand so instanzieren:

      VB.NET-Quellcode

      1. Static Temp As New DelegateCommand(Sub() _Items.RemoveAt(0), Function() _Items.Count > 0)
      und die Event-Abonniererei im Konstruktor entfällt.
      Wenn nun also jmd. zunächst dein EnableableDelegateCommand verwendet, aber später auf 2013 umstellt - also ich ziehe prinzipiell die Sachen aussm Framework vor - und dann muss er die Logik seiner ViewmodelKlassen umstellen.
      Statt dass du auch nur ein DelegateCommand schreibst, was grundsätzlich immer enableable ist, und von vorneherein so funzt, wies im nächsten FW der Standard sein wird.
      @ErfinderDesRades
      Aber wie bekommt der Button dann mit, dass sich die Items-Liste geändert hat (also mit anderen Worten, dass sich CanExecute geändert hat)?
      Ich könnte es so umschreiben:
      DelegateCommand:
      Public Sub New(Execute-Callback) -> CanExecute gibt immer True zurück
      Public Sub New(Execute-Callback, CanExecute-Callback) -> CanExecute gibt Ergebnis von CanExecute-Callback zurück.
      Public Sub OnCanExecuteChanged() -> Löst CanExecuteChanged-Event aus.
      EnableableDelegateCommand:
      Public Property IsEnabled As Boolean -> Löst CanExecuteChanged-Event aus, wenn geändert wird.
      Überschreibt CanExecute -> Gibt IsEnabled zurück

      Dadurch bleiben beide Möglichkeiten erhalten.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils
      Nun, das bestätigt dann, was ich gesagt habe. Man muss das Command auf jeden Fall anfassen, damit das Control aktualisiert wird. Und da hab ich mir eben gedacht, dass die IsEnabled-Property passender ist, als ein Callback. Sie ist auf jeden Fall einfacher zu verwenden.
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils
      Ok. Ich bin jetzt beim Durchgucken des System.Windows.Input.CommandManager im ILSpy hängen geblieben. Uuuund... ich versteh's nicht.
      Ich verstehe, dass der CommandManager irgendwann ein Event auslöst, was dazu führt, dass das RelayCommand (bzw. CommandBase) die CanExecute-Funktion erneut ausführt. Aber ich verstehe nicht, woher der das wissen will. Bei welchen Szenarien löst der das aus? Und wann löst er nicht aus?

      VB.NET-Quellcode

      1. Class MainWindowViewModel
      2. Inherits ViewModelBase
      3. Dim _Value As Integer = 0
      4. Public Property Value As Integer
      5. Get
      6. Return _Value
      7. End Get
      8. Set(value As Integer)
      9. If _Value <> value Then
      10. _Value = value
      11. OnPropertyChanged("Value")
      12. End If
      13. End Set
      14. End Property
      15. Public ReadOnly Property TestCommand As ICommand
      16. Get
      17. Static Temp As New RelayCommand(Sub() Value = 5, Function(Parameter) UnrelatedProperty)
      18. Return Temp
      19. End Get
      20. End Property
      21. Public Property UnrelatedProperty As Boolean = True
      22. End Class

      Wie bekommt der CommandManager mit, wenn ich UnrelatedProperty auf False setze?
      "Luckily luh... luckily it wasn't poi-"
      -- Brady in Wonderland, 23. Februar 2015, 1:56
      Desktop Pinner | ApplicationSettings | OnUtils