Haben Sie das auch schon mal erlebt: Sie haben eine Anwendung erstellt, sorgfältig alle Controls auf der Form ausgerichtet, der Anwender hat aber einen größeren oder kleineren Bildschirm und nun müssen Sie für diesen einen Kunden alle Controls erneut anpassen und ausrichten. Wie schön wäre es da, wenn Ihre Anwendung genau so reagiert wie professionelle Anwendungen, die der Anwender mit der Maus in beliebige Größen ziehen kann und die Anwendung sieht trotzdem genau so aus wie vorher. Heute können wir Ihnen eine universelle Lösung präsentieren, die Sie nur noch in Ihre Anwendung einfügen müssen. Der Grundgedanke ist folgender: Zu allen Controls auf der Form werden die Ursprungspositionen und Eigenschaften gespeichert. Nach einer Größenveränderung wird der Veränderungsfaktor zu der Ursprungsgröße errechnet und alle Controls entsprechend angepasst. Nun kommen wir zu unserer ersten Frage: Wie können wir die Eigenschaften aller Controls schnell und effizient speichern? Genau - Sie denken da sicherlich an das Gleiche wie ich, nämlich an eine Collection von Klassen. Bei diesem Stichwort möchte ich darauf hinweisen, dass es für das Verständnis von Vorteil ist, wenn Sie den Workshop Kommen wir zum Grundgerüst: die Klasse clsControl. Hier werden für jedes Objekt die einzelnen Eigenschaften gespeichert. Zuerst überlegen wir uns, welche Eigenschaften wir sichern müssen. Für die Größenänderung brauchen wir die Position, sowie Breite und Höhe. Das wären die Eigenschaften Top, Left, Width und Height. Fangen wir einfach mal an zu schreiben: Option Explicit ' lokale Variablen zum Zwischenspeichern der Werte Private mvar_Tag As Long Private mvar_Top As Long Private mvar_Left As Long Private mvar_Width As Long Private mvar_Height As Long Damit können wir aber leider nicht alles abdecken, da z.B. das Line-Control über diese Werte nicht verfügt. Hierfür brauchen wir dann noch x1, x2, y1 und y2. Private mvar_X1 As Long Private mvar_X2 As Long Private mvar_Y1 As Long Private mvar_Y2 As Long Da wir zusätzlich auch noch die Schriftgrößen sowie ein Unterdrücken der Größenänderung an einem Objekt mit abdecken wollen, brauchen wir noch Folgendes: Private mvar_ResizeOption As Integer Private mvar_FontSize As Single Private mvar_FontSizing As Boolean Nun benötigen wir für alle diese Variablen noch die entsprechenden Zuweisungs- und Lese-Operationen (eine zusätzliche Beschreibung wie dies genau abläuft finden Sie in unserem Workshop OOP - Objektorientierte Programmierung): ' Zuweisen & Abrufen mvar_Tag Public Property Let Tag(ByVal vData As Long) mvar_Tag = vData End Property Public Property Get Tag() As Long Tag = mvar_Tag End Property .... Erstellen einer Collection-Class Für diese Klasse clsControl wird jetzt noch eine Collection benötigt, die die einzelnen Controls der Form "sammelt". Fangen wir einfach einmal mit dem Grundgerüst an: Diese Klasse bezeichnen wir als col_Controls und verweisen für eine weitere Beschreibung wiederum auf unserem Workshop Option Explicit Private mCol As Collection Private Sub Class_Initialize() ' Erstellt das Collection Objekt beim ' erstem Zugriff auf die Klasse Set mCol = New Collection End Sub Private Sub Class_Terminate() ' Zerstört das Collection Objekt wenn ' die Klasse beendet wird Set mCol = Nothing End Sub ' Objekt der Collection zurückgeben Public Property Get Item(ByVal Index As Long) As clsControl Set Item = mCol(Index) End Property ' Anzahl der Objekte durchreichen Public Property Get Count() As Long Count = mCol.Count End Property Zu dem Grundgerüst fehlt nun aber noch die wichtigste Funktion: Add. Gleichzeitig müssen wir aber auch daran denken, wie wir das dem Control zugehörige Item für den späteren Zugriff speichern. Hierfür bieten sich der Name und Index an, die jedes Objekt besitzen. Public Function Add(ByVal lTop As Long, _ ByVal lLeft As Long, _ ByVal lWidth As Long, _ ByVal lHeight As Long, _ ByVal sName As String, _ ByVal iFontSize As Single) As clsControl On Local Error GoTo 0 Dim objNewMember As clsControl Set objNewMember = New clsControl ' Eigenschaften zuweisen With objNewMember .Top = lTop .Left = lLeft .Height = lHeight .Width = lWidth .Tag = mCol.Count + 1 .Name = sName .Fontsize = iFontSize End With ' Objekt der Collection zuweisen mCol.Add objNewMember, sName ' Objekt zurückgeben Set Add = objNewMember ' neues Objekt zerstören Set objNewMember = Nothing End Function Diese Funktion fügt der Collection ein neues Objekt der Klasse clsControl hinzu und speichert die übergebenen Werte. Als Index für unser neues Objekt in der Collection nehmen wir den Namen das Objektes, den wir vor dem Aufruf der Funktion ermitteln werden. Was uns nun aber sofort auffällt ist, dass noch eine Add Funktion für ein Objekt des Typs Line fehlt. Diese ist gleich aufgebaut, nur dass hier die Werte x1, x2, y1 und y2 übergeben und gespeichert werden. Public Function AddLine(ByVal lX1 As Long, _ ByVal lX2 As Long, _ ByVal lY1 As Long, _ ByVal lY2 As Long, _ sName As String) As clsControl On Local Error GoTo 0 Dim objNewMember As clsControl Set objNewMember = New clsControl ' Eigenschaften zuweisen With objNewMember .X1 = lX1 .X2 = lX2 .Y1 = lY1 .Y2 = lY2 .ResizeOption = 0 .Tag = mCol.Count + 1 .Name = sName End With ' Objekt der Collection zuweisen mCol.Add objNewMember, sName ' Objekt zurückgeben Set AddLine = objNewMember ' neues Objekt zerstören Set objNewMember = Nothing End Function Damit hätten wir die zweite Klasse fertig und "erschlagen". Eigenschaften der Form speichern Nun müssen wir noch die Eigenschaften und Positionen der übergebenen Form speichern, und weil uns heute danach ist, machen wir das auch in einer Klasse. Hierfür erstellen wir eine weitere Klasse, die nur die Werte Top, Left, Width und Height einer Form speichert. Diese Klasse nennen wir dann clsForm. Wenn Sie damit fertig sind, sollten Sie eine Klasse haben, die in etwa so ausschauen sollte: Option Explicit ' lokale Variablen zum Zwischenspeichern der Werte Private mvar_Top As Long Private mvar_Left As Long Private mvar_Width As Long Private mvar_Height As Long ' Zuweisen & Abrufen mvar_Top Public Property Let Top(ByVal vData As Long) mvar_Top = vData End Property Public Property Get Top() As Long Top = mvar_Top End Property ' Zuweisen & Abrufen mvar_Left Public Property Let Left(ByVal vData As Long) mvar_Left = vData End Property Public Property Get Left() As Long Left = mvar_Left End Property ' Zuweisen & Abrufen mvar_Width Public Property Let Width(ByVal vData As Long) mvar_Width = vData End Property Public Property Get Width() As Long Width = mvar_Width End Property ' Zuweisen & Abrufen mvar_Height Public Property Let Height(ByVal vData As Long) mvar_Height = vData End Property Public Property Get Height() As Long Height = mvar_Height End Property Damit hätten wir das nötige Grundgerüst um alle benötigten Daten speichern zu können. Die Klasse "Resize" Kommen wir nun zu unserem letztem Streich: der Klasse clsResize: Fassen wir erst einmal zusammen, welche Funktionen und Objekte wir in dieser Klasse benötigen. Dazu möchte ich das Prinzip noch einmal erläutern: Zu Beginn des Programms speichern wir die Größe, Position, usw., aller Objekte welche auf der Form angeordnet sind. Als zweiten Schritt berechnen wir nach jeder Größenänderung das Verhältnis zwischen der ursprünglichen und aktuellen Größe der Form. Mit diesem errechneten Verhältnis multiplizieren wir, wenn gewünscht, die einzelnen Werte neu und setzen diese. Zusätzlich möchten wir für einzelne Objekte noch folgende Eigenschaften festlegen können: Größenänderung nur in der Höhe, Breite oder gar nicht, aber immer mit Anpassung der Position des Objektes, sowie das optionale Verhindern des Vergrößerns der Schriftart. Ebenfalls möchten wir gerne eine Funktion haben, die uns den Ursprungszustand wieder herstellt. Des weiteren sollte die Klasse eine Eigenschaft besitzen, mit der wir festlegen können, ab welcher prozentualen Veränderung eine Größenanpassung aller Controls hergestellt wird. Fangen wir einfach einmal an, die Inhalte der Klasse zusammen zu fassen: Wir benötigen: ' lokale Variaben: Private oControls As col_Controls Private oForm_First As clsForm Private oForm_Now As Form Private mvar_ScaleMinMax As Single ' Funktionen: Public Sub Initialize() Public Sub DoResize() Public Sub DoResize_First() Public Sub ResizeOption(xControl As Control, _ Optional iResizeOption As _ eToResizeOption = eToRO_FullResize, _ Optional bFontSizing = True) Damit können wir bereits das Grundgerüst anlegen: Option Explicit ' lokale Variaben: Private oControls As col_Controls Private oForm_First As clsForm Private oForm_Now As Form Private mvar_ScaleMinMax As Single Public Enum eToResizeOption eToRO_FullResize = 0 eToR0_Width = 1 eToR0_Height = 2 eToRO_WidthHeight = 3 End Enum Private Sub Class_Initialize() ' Anlegen der Objekte Set oControls = New col_Controls Set oForm_First = New clsForm End Sub Private Sub Class_Terminate() ' Zerstören der Objekte Set oControls = Nothing Set oForm_First = Nothing Set oForm_Now = Nothing End Sub Public Property Let oForm(ByRef vData As Variant) Set oForm_Now = vData End Property Public Property Get ScaleMinMax() As Single ScaleMinMax = mvar_ScaleMinMax End Property Public Property Let ScaleMinMax(ByVal vNewValue As Single) mvar_ScaleMinMax = vNewValue End Property ' Funktion initialisiert die Objekte Public Sub Initialize() .... End Sub ' Funktion führt Größenänderung durch: ' Our Main Function Public Sub DoResize() .... End Sub ' Funktion stellt Ursprungszustand wieder her Public Sub DoResize_First() .... End Sub ' Funktion setzt Größenänderungsoptionen Public Sub ResizeOption(xControl As Control, _ Optional iResizeOption As _ eToResizeOption = eToRO_FullResize, _ Optional bFontSizing = True) .... End Sub Als Erstes müssen wir innerhalb der Klasse auf die aktuelle Form zugreifen können. Dazu die Property Let oForm. Dies muss in der Anwendung direkt nach dem Erzeugen des Objektes durchgeführt werden. Initialize-Prozedur Fangen wir nun mit der Prozedur Initialize an: Was tut diese Funktion? Dim oControl As Control Dim sControlName As String Dim iFontSize As Integer Dim lTag As Long With oForm_First .Height = oForm_Now.Height .Width = oForm_Now.Width .Top = oForm_Now.Top .Left = oForm_Now.Left End With Nun müssen wir alle Controls der Form durchlaufen und die Controls der Collection hinzufügen. Wichtig dabei ist, dass wir in dieser Funktion ein On Error Resume Next hinzufügen, welches eventuell auftretende Fehler bei Timer-Objekten usw. übergeht. Damit dieses Objekt in der Collection aber auch wieder dem entsprechendem Control zugeordnet werden kann, benötigen wir noch den Namen des Controls. Dabei ist ganz wichtig, dass die Elemente eines Steuerelementfeldes immer den gleichen Namen mit einem unterschiedlichem Index haben. Außerdem müssen wir noch beachten, dass ein Laufzeitfehler auftritt, wenn wir den Index eines Controls auslesen möchten das keinen hat. Deswegen machen wir dies in zwei Schritten:
' Alle Controls durchlaufen und Eigenschaften speichern For Each oControl In oForm_Now.Controls On Error Resume Next With oControl ' Objektnamen ermitteln sControlName = oControl.Name sControlName = sControlName & CStr(oControl.Index) ' Schriftgröße ermitteln iFontSize = 0 Err.Clear iFontSize = oControl.Fontsize ' Falls Fehler Objekt unterstützt Eigenschaft oder Methode nicht If Err.Number = 438 Then iFontSize = oControl.Font.Size If TypeOf oControl Is Line Then lTag = oControls.AddLine(.X1, .X2, .Y1, .Y2, sControlName).Tag Else lTag = oControls.Add(.Top, .Left, .Width, .Height, sControlName, iFontSize).Tag End If End With Next Nun fehlt noch die Möglichkeit, für ein Objekt bestimmte Eigenschaften festlegen zu können. Hierfür greifen wir über die Klasse clsResize auf ein Item der Collection zu und verändern die zwei Eigenschaften Fontsizing und ResizeOption. Wir übergeben der Funktion das Control, ermitteln den Namen in der Funktion und setzen dann die Eigenschaften: ' Funktion setzt Resize-Attribute zu einzelnen Controls der Form Public Sub ResizeOption(xControl As Control, _ Optional iResizeOption As _ eToResizeOption = eToRO_FullResize, _ Optional bFontSizing = True) Dim sControlName As String On Error Resume Next sControlName = xControl.Name sControlName = sControlName & CStr(xControl.Index) With oControls.Item(sControlName) .ResizeOption = iResizeOption .FontSizing = bFontSizing End With End Sub Prozedur DoResize Kommen wir nun zu unserem vorletzten Streich: der Funktion DoResize. Dafür benötigen wir noch eine Hilfsfunktion: ' Funktion liefert Minimum zweier ' Gleitkommazahlen einfacher Genauigkeit Private Function Min(ByVal x As Single, ByVal y As Single) As Single If x <= y Then Min = x Else Min = y End Function Das Prinzip ist im Grunde genommen ganz einfach: wir berechnen die verhältnismäßige Größenänderung der Form zu den gespeicherten Werten. Für die Schriftgröße nehmen wir das Minimum beider Werte. Wenn das Größenverhältnis den eingestellten Wert überschreitet, gehen wir alle Objekte durch und berechnen die neuen Top-, Left-, Width- und Height-Werte. Dabei müssen wir folgendes beachten: Die Move-Methode wird nicht von einer ComboBox oder DriveListBox unterstützt. Ebenso den Sonderfall Line, da hier die Werte x1, x2, y1 und y2 gesetzt werden müssen. Vor der Move-Methode müssen wir dann noch beachten, dass einige Steuerelemente eventuell nicht vollständig verändert werden sollen. Dafür fragen wir dort per Select-Case-Anweisung die Eigenschaft ResizeOption des jeweiligen Items der Collection ab. Sollte das Größenverhältnis den eingestellten Wert unterschreiten, stellen wir die ursprünglichen Größen wieder her. Die fertige Funktion schaut nun wie folgt aus: Public Sub DoResize() ' Etwaige Fehler wie bei Timern usw. übergehen On Error Resume Next Dim oControl As Control Dim sngFaktor_Height As Single Dim sngFaktor_Width As Single Dim sngFaktor_FontSize As Single Dim sControlName As String ' Faktor der Größenveränderung der Breite berechnen sngFaktor_Width = oForm_Now.Width / oForm_First.Width ' Faktor der Größenveränderung der Höhe berechnen sngFaktor_Height = oForm_Now.Height / oForm_First.Height ' Faktor der Größenveränderung der Schrift sngFaktor_FontSize = Min(sngFaktor_Width, sngFaktor_Height) ' nur skalieren, wenn neues Größenverhältnis +- ScaleMinMax (z.B. 10%) If sngFaktor_Width < 1 - ScaleMinMax / 100 Or _ sngFaktor_Width > 1 + ScaleMinMax / 100 Or _ sngFaktor_Height < 1 - ScaleMinMax / 100 Or _ sngFaktor_Height > 1 + ScaleMinMax / 100 Then ' Alle Controls durchlaufen und der Größe nach anpassen For Each oControl In oForm_Now.Controls sControlName = oControl.Name sControlName = sControlName & CStr(oControl.Index) With oControls.Item(sControlName) If .FontSizing = True Then Err.Clear oControl.Fontsize = .Fontsize * sngFaktor_FontSize If Err.Number = 438 Then _ oControl.Font.Size = .Fontsize * sngFaktor_FontSize End If ' Die Move-Methode wird nicht von der ' ComboBox und DriveListBox unterstützt. If (TypeOf oControl Is ComboBox Or _ TypeOf oControl Is DriveListBox) Then Select Case .ResizeOption Case 1 ' Breite nicht anpassen oControl.Top = .Top * sngFaktor_Height oControl.Left = .Left * sngFaktor_Width oControl.Width = .Width oControl.Height = .Height * sngFaktor_Height Case 2 ' Höhe nicht anpassen oControl.Top = .Top * sngFaktor_Height oControl.Left = .Left * sngFaktor_Width oControl.Width = .Width * sngFaktor_Width oControl.Height = .Height Case 3 ' Höhe und Breite nicht anpassen oControl.Top = .Top * sngFaktor_Height oControl.Left = .Left * sngFaktor_Width oControl.Width = .Width oControl.Height = .Height Case Else oControl.Top = .Top * sngFaktor_Height oControl.Left = .Left * sngFaktor_Width oControl.Width = .Width * sngFaktor_Width oControl.Height = .Height * sngFaktor_Height End Select ' Sonderfall Line-Element, da anders definiert ElseIf (TypeOf oControl Is Line) Then oControl.X1 = .X1 * sngFaktor_Width oControl.X2 = .X2 * sngFaktor_Width oControl.Y1 = .Y1 * sngFaktor_Height oControl.Y2 = .Y2 * sngFaktor_Height ' alle anderen Controls Else Select Case .ResizeOption Case 1 ' Breite nicht anpassen oControl.Move _ .Left * sngFaktor_Width, _ .Top * sngFaktor_Height, _ .Width, _ .Height * sngFaktor_Height Case 2 ' Höhe nicht anpassen oControl.Move _ .Left * sngFaktor_Width, _ .Top * sngFaktor_Height, _ .Width * sngFaktor_Width, _ .Height Case 3 ' Breite und Höhe nicht anpassen oControl.Move _ .Left * sngFaktor_Width, _ .Top * sngFaktor_Height, _ .Width, .Height Case Else oControl.Move _ .Left * sngFaktor_Width, _ .Top * sngFaktor_Height, _ .Width * sngFaktor_Width, _ .Height * sngFaktor_Height End Select End If End With Next Else ' In diesem Fall die Größe der Form nicht auf ' Ursprungszustand setzen DoResize_First False End If End Sub Prozedur DoResize_First Als Letztes kopieren die Funktion nach DoResize_First und arbeiten diese so um, dass die ursprünglichen Positionen wieder hergestellt werden. ' Ursprungszustand wieder herstellen Public Sub DoResize_First(ByVal bWithForm As Boolean) ' Etwaige Fehler wie bei Timern usw. übergehen On Error Resume Next Dim oControl As Control Dim sngFaktor_Height As Single Dim sngFaktor_Width As Single Dim sControlName As String ' Faktor der Größenveränderung der Breite berechnen sngFaktor_Width = 1 ' Faktor der Größenveränderung der Höhe berechnen sngFaktor_Height = 1 ' Alle Controls durchlaufen und der Größe nach anpassen For Each oControl In oForm_Now.Controls sControlName = oControl.Name sControlName = sControlName & CStr(oControl.Index) With oControls.Item(sControlName) Err.Clear oControl.Fontsize = .Fontsize If Err.Number = 438 Then oControl.Font.Size = .Fontsize ' Die Move-Methode wird nicht von der ' ComboBox und DriveListBox unterstützt. If (TypeOf oControl Is ComboBox Or _ TypeOf oControl Is DriveListBox) Then oControl.Top = .Top * sngFaktor_Height oControl.Left = .Left * sngFaktor_Width oControl.Width = .Width * sngFaktor_Width oControl.Height = .Height * sngFaktor_Height ' Sonderfall Line-Element, da anders definiert ElseIf (TypeOf oControl Is Line) Then oControl.X1 = .X1 * sngFaktor_Width oControl.X2 = .X2 * sngFaktor_Width oControl.Y1 = .Y1 * sngFaktor_Height oControl.Y2 = .Y2 * sngFaktor_Height ' alle anderen Controls Else oControl.Move _ .Left * sngFaktor_Width, _ .Top * sngFaktor_Height, _ .Width * sngFaktor_Width, _ .Height * sngFaktor_Height End If End With Next If bWithForm Then ' zu guter Letzt Größe der Form wieder herstellen oForm_Now.Height = oForm_First.Height oForm_Now.Width = oForm_First.Width End If End Sub Geschafft ! *schweißabtupf* Anwendung der Resize-Klassen Nun kommen wir zu der alles entscheidenden Frage: Wie verwende ich nun diese 4 Klassen in meiner Anwendung? Hierfür fügen wir in den Allgemeinteil der gewünschten Form folgende Anweisung: Dim oResize As clsResize In das Form_Load-Event setzen wir folgende Anweisungen: Set oResize = New clsResize oResize.oForm = Me oResize.Initialize ' Für alle Objekte, die zusätzlich beachtet werden ' müssen, setzen wir folgende Anweisung ein: oResize.ResizeOption .... Zu guter Letzt fügen Sie in das Form_Resize-Event folgende Anweisung ein: If Me.WindowState <> vbMinimized Then oResize.DoResize End If Wenn Sie den ursprünglichen Zustand wieder herstellen möchten, brauchen Sie nur folgenden Code auszuführen: oResize.DoResizeFirst True Wir wünschen Ihnen viel Spaß mit diesen Klassen. Sollten Sie Fragen zu diesem Workshop haben, so können Sie diese gerne in unser Diskussionsforum stellen. Anmerkung: Dieser Workshop wurde bereits 31.152 mal aufgerufen.
Anzeige
![]() ![]() ![]() (einschl. Beispielprojekt!) Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren [email protected]! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
sevOutBar 4.0 ![]() Vertikale Menüleisten á la Outlook Erstellen von Outlook ähnlichen Benutzer- interfaces - mit beliebig vielen Gruppen und Symboleinträgen. Moderner OfficeXP-Style mit Farbverläufen, Balloon-Tips, u.v.m. Tipp des Monats ![]() Dieter Otter PopUp-Menü wird nicht angezeigt :-( In diesem Tipp verraten wir Ihnen, wie Sie Probleme mit PopUp-Menüs umgehen können, wenn diese unter bestimmten Umständen einfach nicht angezeigt werden. [email protected] CD Vol.6 ![]() ![]() Geballtes Wissen aus mehr als 8 Jahren [email protected]! Online-Update-Funktion Entwickler-Vollversionen u.v.m. |
|||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |