Bestimmte Eigenschaften eines Controls per Laufzeit ändern.

  • VB.NET

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von GimpTutWorks.

    Bestimmte Eigenschaften eines Controls per Laufzeit ändern.

    Hallo zsm.
    Wie schon im Titel erwähnt, möchte ich mir eine Funktion erstellen, die es mir
    möglich macht, Eigenschaften von Controls zu verändern.

    Ich hab mir das ganze so gedacht:
    1. Ich gebe ein Control an, wo ich eine Eigenschaft verändern möchte
    2. Dann gebe ich mit, welche Eigenschaft ich denn verändern möchte.
    3. Gebe ich den Wert an, der in der Eigenschaft festgesetzt werden soll.

    Funktion sieht momentan so aus:

    VB.NET-Quellcode

    1. Private Sub SetProperty(ByVal iControl As Control, ByVal iProperty As Object, ByVal iValue As Object)
    2. End Sub



    Da steht jetzt nichts drin, weil ich grad ne kleine Denkblockade habe..
    Könnt ihr mir mal Tipps geben, wie ich hier am besten anfange?

    Mir kommts nicht in den Sinn, wie ich denn diese bestimmte Eigenschaft anspreche.
    Danke im Voraus.
    Hey,

    1 => Sprich das Control direkt an.
    2 => Sprich das Control direkt an und ändere die Property.
    3 => Sprich das Control direkt an und ändere die Property auf den gewünschten Wert.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    @GimpTutWorks:
    Ich habe folgende Klasse:

    VB.NET-Quellcode

    1. Class Foo
    2. Public Property A As Integer
    3. End Class

    Und ich rufe jetzt Deine Funktion so auf:

    VB.NET-Quellcode

    1. Dim Instance As New Foo
    2. Instance.A = 100
    3. SetProperty(Instance, "A", 200)
    4. SetProperty(Instance, "B", 300)

    Wobei hier "A" uns "B" nur irgendeinen Verweis auf eine A- und B-Property sein soll.
    Was passiert? Gehen wir mal davon aus, dass beim ersten Aufruf korrekt der Wert der A-Property verändert wird.
    Aber beim zweiten Aufruf?
    Es gibt keine B-Property und somit kann auch kein Wert gesetzt werden. Nur: Woher soll der Compiler wissen, was Du zur Laufzeit für Objekte übergibst? Garnicht. Kann er nicht wissen. Und deshalb gibt's auch keine Möglichkeit, sowas zu machen.

    Wobei ich dazusagen muss: Es gibt zwei Möglichkeiten, das doch zu machen: Spätes Binden und Reflection.

    Spätes Binden wird vom VB-Compiler gemacht, wenn man Option Strict Off hat und z.B. sowas macht:

    VB.NET-Quellcode

    1. Dim Foo As Object
    2. Foo = New Form
    3. Foo.Text = "Blubb"

    Denn Object hat eigentlich keinen Member, der Text heißt. Zur Laufzeit wird nachgeguckt, was denn tatsächlich in der Foo-Variable drin ist, und wenn das Ding da drin irgendwas hat, was Text heißt, dann wird einfach das verwendet. Das ist natürlich extrem unsauber, weil man sich die Compiler-Unterstützung wegnimmt und Fehler, die eigentlich sofort angezeigt werden würden, erst zur Laufzeit bemerkt werden.

    Dann gibt's noch Reflection. Da holt man sich den System.Type vom Objekt, dann per GetProperty die Property mit dem Namen und dann kann man SetValue aufrufen. Das funktioniert zwar, ist aber erstens wieder nicht typenfreundlich (man kann einer Property vom Typ String einen BackGroundWorker geben und das bemerkt man dann auch erst zur Laufzeit) und zweitens ist es ziemlich unübersichtlich.


    Also bevor Du diesen Aufwand auf Dich nimmst, möchte ich fragen, was Du genau vorhast. Möchtest Du eine Art Binding-System bauen? Das gibt's nämlich schon für Controls.
    Sowas selbst zu implementieren ist ein ganzer Haufen Arbeit.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    Und deshalb gibt's auch keine Möglichkeit, sowas zu machen.
    Das ließe sich per Reflection machen, wäre allerdings very oversized.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany:
    Hab ich das so gut versteckt?
    Wobei ich dazusagen muss: Es gibt zwei Möglichkeiten, das doch zu machen: Spätes Binden und Reflection.
    [...]
    [Absatz über Reflection]

    :P
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Niko Ortner:: Sorry, der Kaffee ist eben erst fertig geworden. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hier mal ein Beispiel für eine Erweiterungsfunktion per Reflection. Damit hast du auf quasi jedem Objekt die Möglichkeit, mit der Methode SetPropertyIndirect(Name, Value) eine Eigenschaft zu setzen.
    Für das Lesen von Properties würde das dann analog funktionieren.

    VB.NET-Quellcode

    1. <Runtime.CompilerServices.Extension()>
    2. Public Sub SetPropertyIndirect(ByVal obj As Object, prop As String, value As Object)
    3. Dim allProps = obj.GetType().GetProperties()
    4. Dim theProp = Array.Find(Of Reflection.PropertyInfo)(allProps, Function(p)
    5. Return p.Name.Equals(prop, StringComparison.CurrentCultureIgnoreCase)
    6. End Function)
    7. If theProp IsNot Nothing Then
    8. theProp.SetValue(obj, value, Nothing)
    9. Else
    10. Throw New IndexOutOfRangeException(String.Format("Property {0} not defined in class {1}", prop, obj.GetType().Name))
    11. End If
    12. End Sub

    Du kannst bei obj.GetType().GetProperties() zusätzlich BindingFlags angeben um damit z.B. auch Zugriff auf private deklarierte Eigenschaften zu bekommen oder Felder (GetFields() statt GetProperties()) in die Suche mit aufnehmen.
    Ist der zugewiesene Wert nicht mit dem Typ der Eigenschaft "kompatibel" wird es eine Exception geben, die hier nicht abgefangen und damit einfach durchgereicht wird. Gibst du einen Eigenschaftennamen an, den die Klasse des Objekts nicht unterstützt, gibts eine IndexOutOfRangeException (ist vllt. nicht ganz glücklich gewählt, aber zur Demonatration reicht es).

    Ach ja, fast vergessen: Eigenschaften können auch indiziert sein, das wird hier ignoriert, als Indizes wird hier grundsätzlich "Nothing" übergeben. Man könnte das aber auch als Funktionsparameter einbauen.

    Nachtrag 2: So wie oben muss der Code in ein Modul. Du kannst es auch in eine Klasse packen (z.B. eine statische), dann muss die Funktion allerdings als Shared deklariert sein.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    @Arby: Das funktioniert so.
    Geht auch kürzer mit GetProperty:

    VB.NET-Quellcode

    1. Obj.GetType.GetProperty(Name, BindingFlags...).SetValue(Obj, Value, Nothing)

    Aber es stellt sich immer noch die Frage, was erreicht werden soll.
    Im normalfall braucht man das nicht. Und wie gesagt gibt's Bindings für Controls schon.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Niko Ortner schrieb:

    Aber es stellt sich immer noch die Frage, was erreicht werden soll.
    Im normalfall braucht man das nicht. Und wie gesagt gibt's Bindings für Controls schon.

    Gefragt war, wie man auf die Properties eines zur Compile-zeit "unbekannten" Objekts über deren Namen zugreifen kann. Ob das Ziel, das letztendlich damit erreicht werden soll, evtl. auch mit anderen Mitteln besser lösen kann, sollte voll und ganz dem OP überlassen sein, es sei denn er fragt gleichzeitig nach Alternativen. Bis dahin ist es aber für eine Beantwortung irrelevant, was damit erreicht werden soll. ;)
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Hi
    don't. Reflection ist nicht für sowas gemacht. Was ihr da versucht funktioniert zwar, aber es ist nicht regelkonform, da zur Bindung mehr gehört, als nur das. Die Property kann nicht mehr eindeutig in einen "semantischen Kontext" (ich nenn's jetz' einfach mal so) eingeordnet werden, d.h. man kann ihren Sinn nicht eindeutig bestimmen. Die Beschreibung, die vom Menschen gewählt wird, ordnet sie nicht eindeutig ihrer Funktionsweise zu, da es noch Überladungen, etc. gibt.
    Reflection nach Namen verwendet man, denk' ich, nie, wenn es sich nicht um ein Programmanalyseprogramm oder dergleichen handelt, man modifiziert stattdessen die Programmarchitektur. Es gibt ein paar Ausnahmen, wie bspw. eine Architektur, die die "semantische" Zuordnung erlaubt (bspw. INotifyPropertyChanged-Interface, was ich auch grotesk gelöst finde). Späte Bindung ist übrigens der gleiche Mist, das macht man einfach nicht.
    Es könnte bspw. so aussehen:

    VB.NET-Quellcode

    1. Shared Function SetProperty(Of TInstance, TValue)(instance As TInstance, handler As Action(Of TInstance, TValue), value As TValue)
    2. handler(instance, value)
    3. End Sub

    Was da der Sinn von ist, sei mal dahingestellt.
    Problematisch würde die Reflection-Verwendung bspw. bei Shadowing. Reflection selber ist extrem langsam, daher wäre es sogar besser, den Set-Accessor der Property in einem Delegaten zu verwenden, sofern das möglich ist. Der nicht-dynamische Aufruf an Delegaten ist effizienter, da der Member indirekt über das Metatoken aufgerufen werden muss und eine Typüberprüfung und Bindung bereits erfolgt sind.

    Es ist auf jeden Fall notwendig, dass der Zweck einer Sache in jedem Fall zu 100% eindeutig bestimmt werden kann. Dazu kann man
    - Voraussetzungen/Kontrakte setzen oder von Basisklasse erben (bspw. IList(Of T))
    - Attribute verwenden (auch auf der Assembly!, bspw. SerializableAttribute, hier agiert das ebenfalls mehr oder weniger als Kontrakt, vgl. aber auch ISerializable-Interface)
    - Eindeutige Identifikation von Membern (könnte man bspw. über Attribute oder eine statische Klasse, die die Membernamen semantisch eindeutig zuordnet, würde ich aber nicht machen)
    - Einfach die Member des Typs verwenden und die Typeinschränkungen beachten und casten
    - Delegaten verwenden
    - Die Aufrufrichtung umkehren

    Casten wäre auch das, was ich oben vermuten würde:

    VB.NET-Quellcode

    1. Dim obj As Object = New TValue()
    2. Dim tv As TValue = DirectCast(obj, TValue)


    @Arby: Häufig formulieren die TEs ihre Frage konkreter, als nötig und wählen deshalb einen "falschen" Weg.

    Gruß
    ~blaze~
    @~blaze~: Wow, also das... wow.

    @Arby: Da muss ich Dir widersprechen. Es kann nie schaden, nach dem Sinn zu fragen. Ganz besonders bei so unorthodoxen Sachen.
    Extrembeispiel aus dem Forum:
    Der TE fragt, wie er einen Timer starten kann, einen gewissen Code ausführen kann und in diesem ausgeführten Code den Timer wieder stoppen kann.
    Man kann jetzt natürlich Timer von oben bis unten erklären, oder man fragt nach, wofür er das braucht.
    Wenn sich jetzt herausstellt, dass der TE mit dem Timer prüfen will, ob der WebBrowser das Dokument vollständig geladen hat, kann man ihm erklären, dass der WebBrowser ein DocumentCompleted-Event hat, mit dem man Code ausführen kann, sobald das Dokument vollständig geladen wurde... und zwar komplett ohne Timer.
    Man kann sich nicht darauf verlassen, dass der TE bereits die ideale Methode ausgewählt hat, weil es gut möglich ist, dass er eine idealere Methode noch gar nicht kennt.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Achjo da is man mal fürn paar Tage nicht mehr hier online und schon
    stürzt sich gleich ne Meute auf den Thread um zu helfen.
    Dafür erstmal ein Lob an euch.
    Ich hab mir die Antworten noch nicht richtig angeschaut(Mach ich später, wenn ich die Zeit finde), aber
    ich danke jetzt schon mal für die Zahlreichen Antworten.

    Hoch erfreut,
    Re3ker(GTW)