none
Erstellen eines dynamischen Arrays von Steuerelementen RRS feed

  • Frage

  • Hallo,

    ich bin Umsteiger von Visual Basic 6.0.

    In einer Anwendung, die ich dort erstellt habe, konnte ich Steuerelemente (PushButtons, Textfelder, Lables, ...) in variabler Anzahl zur Laufzeit in einem Form erzeugen. In VB hatte jedes Steuerelement die Index-Eigenschaft, mit der man durch Hochzählen des Index beliebig viele "Kopien" in das Form mit individuellen Eigenschaften laden konnte.

    Gibt es eine vergleichbare Möglichkeit in VC# in der 2010 Express Version? Die Index-Eigenschaft scheint es für Steuerelemente nicht zu geben.

    Gruß

    Andreas

    Mittwoch, 6. März 2013 12:32

Alle Antworten

  • Hallo,

    in .NET (also auch C# und VB.NET) hat jedes Container-Element eine Auflistung von Kindern. Diese Auslistung nennt sich oftmals Items, wobei auch Children oder andere Namen verwendet wurden sein könnten.

    Du hast also deine Form. Dieser fügst du Controls hinzu. Dieser werden dann zu -Auflistung hinzugefügt. Um un alle Durchzu gehen brauchst du nur die Controls-Auflistung durch zu gehen.

    Ein Beispiel:

                for (int i = 0; i < this.Controls.Count; ++i)
                    MessageBox.Show(this.Controls[i].Name);
    //oder mit foreach
    Hinzufügen kannst du Controls mit der Add-Methode zur Auflistung.


    Koopakiller [kuːpakɪllɐ] | Webseite | Code Beispiele | Facebook | Snippets

    Mittwoch, 6. März 2013 12:53
  • Hallo Koopakiller,

    danke für Deine Antwort und sorry, daß ich erst jetzt antworte, aber mir war just an diesem Nachmittag mein Bildschirm gestorben.  -> Murphy :(

    So, nochmal zurück zum Thema:

    Ich bin mir nicht sicher, ob ich das jetzt richtig verstanden habe.

    Bei der Aufgabenstellung geht es nicht um eine parent <--> child Beziehung, sondern eher um soetwas wie Klonen von Steuerelementen zur Laufzeit. Also die Vervielfachung eines während der Entwurfszeit angelegten Steuerelements.

    Ein konkretes Beispiel:

    Das Form soll eine Reihe von Pushbuttons zeigen. Wird einer der Buttons gedrückt, gibt er in einem Textfeld seinen Index (seine Nummer, die er im Array of Pushbuttons hat) aus.

    Wieviele PushButtons erzeugt werden, kann der User im Form eingeben, woraufhin die entsprechende Anzahl von Pushbuttons (mit natürlich individuellen, aus dem Index berechneten, Positionen) erzeugt wird.

    Während der Entwurfszeit wird natürlich nur der Prototyp (Index 0) dieses Pushbuttons mit der Ausgabefunktion "textfeld.text = 'Ich bin Nummer ' & Me.Index" im Form angelegt.

    So hatte ich es zumindest in VB6 konstruiert, was ja auch ein Teil von Visual Studio ist.

    Da ich noch absoluter Neuling in C# bin und in den Tutorials zwar etwas zu Auflistungen, aber nichts passendes zu Vervielfachungen (Kopien) von Steuerelementen gefunden habe, wäre etwas detaillierterer Code sehr hilfreich.

    Welche Maßnahmen müssen als Voraussetzung bei der Variablen-Deklaration getroffen werden, usw.?

    Gruß

    Andreas

    Samstag, 16. März 2013 11:20
  • Einige Klassen bieten die Möglichkeit eine "tiefe Kopie" zu erstellen. Also wirklich alles zu kopieren. Die normelen WinForms Controls haben allerdings keine, solche Methode. Bei denen muss man alles von Hand neu zuweisen, was man haben möchte.

    Ich habe mal versucht ein kleines Beipiel zu schreiben, wo Button dynamisch hinzugefügt werden und wenn man auf einen button klick, sich dieser in der Reihe darunter "kopiert". Danach hat ein klick auf den 1. Button keine wirkung mehr, der neue dagegen würde sich wiederrum kopieren. Das beispiel (VS 2012) findest du hier. Die .csproj-Datei müsstest du öffnen können, ansonsten ist der relavante code in der Form1.cs und das Design in der Form1.Designer.cs.

    Wenn das noch nicht ganz deinen Vorstellungen/Fragen entspricht, dann poste mal deinen VB6 Code, das ich mal versuchen kann ihn umzuschreiben.


    <Code:13/> - Koopakiller [kuːpakɪllɐ]
    Webseite | Code Beispiele | Facebook | Snippets

    Samstag, 16. März 2013 12:04
  • Hallo Koopakiller,

    die .csproj-Datei kann ich mit der 2010er Version leider nicht öffnen. Aber ich konnte mir ja den Code als normalen Text anschauen.

    Ich glaube, meine Zielrichtung ist eher das, was Du im ersten Absatz Deines letzten Posts ansprichst.

    Es soll einen Button als Prototyp geben. Abhängig von einem Parameter, den das Form beim Laden liest, soll es dann n weitere vollständige Klone dieses Buttons erzeugen. Bei VB war das Erzeugen der Klone sehr einfach, wenn man beim Anlegen des Prototyps durch die Index-Eigenschaft sagen konnte, daß dieses Control das 0te Element des Arrays für diesen Control-Typ ist. Danach reicht ein simples Load mit Hochzählen des Index zur Erzeugung der Klone während der Laufzeit.

    Auch nur im Prototyp ist die Funktion deklariert, die jeder dieser Buttons beim Click-Ereignis ausführen soll.

    Nachfolgend versuche ich mal, die wesentlichen Auszüge aus dem Code anzuhängen.

    Zunächst die Deklaration des "Prototys" nach Einfügen des Buttons auf grafischem Weg ins Form und Definition einiger Basis-Eigenschaften.

    ...
       Begin VB.CommandButton CmdF 
          Caption         =   "F0"
          Height          =   372
          Index           =   0
          Left            =   840
          Style           =   1  'Grafisch
          TabIndex        =   18
          Top             =   1080
          Width           =   492
       End
    

    Danach die Funktion beim Click-Ereignis

    (Es wird eine Funktion aufgerufen unter Mitgabe des Index - der wievielte Button dieser Art)

    Das (Index as Integer) bekommt das Sub _Click automatisch in VB nur, wenn dieses Control in einem Array definiert wurde (siehe die Index-Eigenschaft bei der Button Deklaration oben).

    Private Sub CmdF_Click(index As Integer)
    
        FSchalt index, 0
    
    End Sub
    

    Dann die eigentliche Vervielfältigung im Load-Ereignis des Forms auf Basis eines Parameters aus einem Klassen-Objekt "my_Loko"

    Private Sub Form_Load() Dim AnzFkts As Integer, FktInd As Integer, RowInd As Integer, a As Integer, b As Integer ... AnzFkts = myLoko.Funktionen ' AnzFkts gibt die Anzahl der Buttons "CmdF" RowInd = (AnzFkts - 2) \ 8

    'Nach CmdF(8) beginnt eine neue Zeile For FktInd = 1 To AnzFkts - 1 Load CmdF(FktInd) CmdF(FktInd).Caption = "F" & FktInd CmdF(FktInd).Top = CmdF(0).Top + ((FktInd - 1) \ 8) * 32 CmdF(FktInd).Left = CmdF(FktInd).Left + FktInd * 40 - ((FktInd - 1) \ 8) * 8 * 40 CmdF(FktInd).Visible = True CmdF(FktInd).Tag = 0 Next ...

    Ich hoffe, das hilft beim Verständnis der Anforderung. Sonst einfach fragen.

    Gruß

    Andreas

    Samstag, 16. März 2013 14:11
  • Hallo Andreas,

    einige Dinge gibt es in Visual Basic .NET nicht mehr.

    Das eine wäre das Ansprechen eines Steuerelement über einen Index. Das ist auch nicht notwendig, da die Steuerelement-Instanz bei der Ereignis-Behandlung übergeben wird.

    Auf der anderen Seite kann man Steuerelemente ohne Probleme selbst im Code erzeugen.

    Dein Code sähe in etwa so aus:

    Option Strict On    ' Dringend empfohlen
    Option Infer On
    Imports System
    Imports System.Drawing
    Imports System.Windows.Forms
    
    Public Class ButtonsForm
        Public Sub New()
            InitializeComponent()
    
            ' Einige Buttons erzeugen
            ErzeugeButtons(33)
        End Sub
    
        ' Gibt die Größe für eienn Button vor
        Dim ButtonBounds As New System.Drawing.Rectangle(8, 8, 40, 24)
    
        Private Sub ErzeugeButtons(anzahl As Integer)
            ' In .NET arbeitet man nullbasiert
            For index As Integer = 0 To anzahl - 1
                ' Text = Caption, Visible ist sowieso True und Tag anfangs Nothing (ein Object)
                ' über die Name Eigenschaft kann man ein Steuerelement wiederfinden
                Dim button = New Button() With
                {
                    .Name = String.Format("F{0}Button", index),
                    .Text = "F" & index.ToString(),
                    .Top = ButtonBounds.Top + (index \ 8) * 32,
                    .Left = ButtonBounds.Left + index * 40 - ((index \ 8) * 8 * 40),
                    .Width = ButtonBounds.Width,
                    .Height = ButtonBounds.Height
                }
                AddHandler button.Click, AddressOf Me.ButtonClicked
                Me.Controls.Add(button)
            Next
        End Sub
    
        ''' <summary>Gemeinsame Ereignisbehandlungsroutine für die Buttons</summary>
        ''' <param name="sender">Die Instanz des Buttons</param>
        Private Sub ButtonClicked(sender As Object, e As System.EventArgs)
            Dim button = DirectCast(sender, Button)
            MessageBox.Show("ButtonClick " & button.Text)
        End Sub
    End Class

    Beachte dass Koordinaten im Standard in Pixeln angegeben werden und nicht mehr in Twips. Windows Forms skaliert anhand der AutoScaleMode Einstellung. Siehe Automatische Skalierung in Windows Forms

    Gruß Elmar

    • Bearbeitet Elmar Boye Samstag, 16. März 2013 17:50
    Samstag, 16. März 2013 17:47
  • Hallo Andreas,

    upps, da hatte ich übersehen, das C# gefragt war, das gleiche in Grün:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1.Forms
    {
        public partial class ButtonsForm : Form
        {
            public ButtonsForm()
            {
                InitializeComponent();
                // Einige Buttons erzeugen
                ErzeugeButtons(33);
            }
    
            // Gibt die Größe für einen Button vor
            System.Drawing.Rectangle ButtonBounds = new System.Drawing.Rectangle(8, 8, 40, 24);
    
            private void ErzeugeButtons(int anzahl)
            {
                // In .NET arbeitet man nullbasiert
                for (int index = 0; index < anzahl; index++)
                {
                    // Text = Caption, Visible ist sowieso True und Tag anfangs null (ein Object)
                    // über die Name Eigenschaft kann man ein Steuerelement wiederfinden
                    var button = new Button
                    {
                        Name = string.Format("F{0}Button", index),
                        Text = "F" + index.ToString(),
                        Top = ButtonBounds.Top + (index / 8 * 32),
                        Left = ButtonBounds.Left + index * 40 - (index / 8 * 8 * 40),
                        Width = ButtonBounds.Width,
                        Height = ButtonBounds.Height
                    };
                    button.Click += this.ButtonClicked;
                    this.Controls.Add(button);
                }
            }
    
            /// <summary>Gemeinsame Ereignisbehandlungsroutine für die Buttons</summary>
            /// <param name="sender">Die Instanz des Buttons</param>
            private void ButtonClicked(object sender, EventArgs e)
            {
                var button = (Button)sender;
                MessageBox.Show("ButtonClick " + button.Text);
            }
        }
    }
    

    Gruß Elmar

    Samstag, 16. März 2013 18:35