Rainer's profileCyrons BlogPhotosBlogListsMore Tools Help

Blog


    December 30

    Interaktivität mit zur Laufzeit generierten Controls, Vorschau

    Dieses Projekt ist damit noch nicht beendet!
    Als nächstes werde ich zeigen, wie die Funktionalität elegant erweitert werden kann. Sie (hier die Methode FadeColor und die beiden Event-Handler) wird in ein Plugin gekapselt und über ein Bridge-Pattern vom Rest getrennt. Auf diese Weise können ganz einfach die unterschiedlichsten Plugins eingesetzt werden, und so wird aus dem Grid z.B. eine Laufschrift-Anzeige, ein Tic Tac Toe-Spiel oder sogar Tetris!
    Als vorerst letzten Schritt (was weiß ich, welche Ideen ich noch bekomme) werde ich das TableLayoutPanel mit den PictureBoxen durch ein eigenes Control ersetzen.

    Interaktivität mit zur Laufzeit generierten Controls, Part 4/5

    Form1Functionality.cs

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace SimpleColorFade
    {
       class Form1Functionality
       {
          #region Global Class members
          private string basicPictureBoxName;
          Form1 m_Form;
          private int numberOfColumns;
          private int numberOfRows;
          private List<PictureBox> pictureBoxList;
          protected delegate void PictureBoxCallBack(int pictureBoxNumber, Color colorState);
          private static object syncLock = new object();
          PictureBox tempPictureBox;
          #endregion
          public Form1Functionality(Form1 frm)
          {
             this.m_Form = frm;
             basicPictureBoxName = "pictureBox_";
             numberOfColumns = 10;
             numberOfRows = 10;
             pictureBoxList = new List<PictureBox>();
          }
          #region Eventhandler
          private void OnMouseMove(object sender, MouseEventArgs e)
          {
             tempPictureBox = (PictureBox)sender;
             pictureBoxList[m_Form.ThatTableLayoutPanel.Controls.GetChildIndex(tempPictureBox)].BackColor =
                Color.Orange;
          }
          private void OnMouseLeave(object sender, EventArgs e)
          {
             tempPictureBox = (PictureBox)sender;
             ThreadPool.QueueUserWorkItem(new WaitCallback(FadeColor),
                m_Form.ThatTableLayoutPanel.Controls.GetChildIndex(tempPictureBox));
          }
          #endregion
          #region Methods
          private void CalculateSize()
          {
             int maxX = 240;
             int maxY = 240;
             // Die Zwischenräume müssen berücksichtigt werden.         
             SubSize.x = maxX / numberOfColumns - ((numberOfColumns + 2) * 5) / numberOfColumns;
             SubSize.y = maxY / numberOfRows - ((numberOfRows + 2) * 5) / numberOfRows;
          }
          /// <summary>
          /// Verändert die Farbe von Orange nach BlueViolet
          /// in 10 Schritten.
          /// </summary>
          /// <param name="pictureBoxNumber">Die Picturebox, deren Farbe geändert werden soll.
          /// Vom Typ object weil der WaitCallback-Delegat das so erwartet.</param>
          // BlueViolet=138/43/226
          // Orange=225/165/0
    private void FadeColor(object pictureBoxNumber) { /* Hier gibt's mit Absicht keinen Lock! * Mit Lock würde die zuerst generierte PictureBox gefadet, * dann erst die Zweite usw. * Ohne den Lock läuft das fließend und sieht viel schöner aus! */ //lock(syncLock) //{ int r = Color.Orange.R; int g = Color.Orange.G; int b = Color.Orange.B; int divR = (r - 138) / 10; int divG = (g - 43) / 10; int divB = 226 / 10; for(int i = 0; i < 10; i++) { r -= divR; g -= divG; b += divB; InvokePictureBox((int)pictureBoxNumber, Color.FromArgb(r, g, b)); Thread.Sleep(100); //} } }
          private void InitializePanel()
          {
             m_Form.ThatTableLayoutPanel.ColumnCount = numberOfColumns;
             m_Form.ThatTableLayoutPanel.RowCount = numberOfRows;
             for(int i = 0; i < m_Form.ThatTableLayoutPanel.RowStyles.Count; i++)
             {
                m_Form.ThatTableLayoutPanel.RowStyles[i].SizeType = SizeType.AutoSize;
             }
             for(int i = 0; i < m_Form.ThatTableLayoutPanel.ColumnStyles.Count; i++)
             {
                m_Form.ThatTableLayoutPanel.ColumnStyles[i].SizeType = SizeType.AutoSize;
             }
          }
          private void InvokePictureBox(int pictureBoxNumber, Color colorState)
          {
             if(pictureBoxList[pictureBoxNumber].InvokeRequired)
             {
                PictureBoxCallBack pbc = new PictureBoxCallBack(InvokePictureBox);
                pictureBoxList[pictureBoxNumber].Invoke(pbc, new object[] { pictureBoxNumber, colorState });
             }
             else
                pictureBoxList[pictureBoxNumber].BackColor = colorState;
          }
          /// <summary>
          /// Füllt das Panel mit Pictureboxen.
          /// </summary>
          internal void PopulatePanel()
          {
             InitializePanel();
             CalculateSize();
             int counter = 0;
             for(int rowCount = 0; rowCount < numberOfRows; rowCount++)
             {
                for(int columnCount = 0; columnCount < numberOfColumns; columnCount++)
                {
                   PictureBox myPictureBox = new PictureBox();
                   myPictureBox.Name = basicPictureBoxName + columnCount.ToString() + rowCount.ToString();
                   myPictureBox.Size = new Size(SubSize.x, SubSize.y);
                   myPictureBox.BackColor = Color.BlueViolet;
                   myPictureBox.MouseMove += new MouseEventHandler(OnMouseMove);
                   myPictureBox.MouseLeave += new EventHandler(OnMouseLeave);
                   pictureBoxList.Add(myPictureBox);
                   m_Form.ThatTableLayoutPanel.Controls.Add(pictureBoxList[counter], columnCount, rowCount);
                   counter++;
                }
             }
          }
          #endregion
       }
    
       public struct SubSize
       {
          public static int x;
          public static int y;
       }
    }

    Interaktivität mit zur Laufzeit generierten Controls, Part 3/5

    Form1.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    namespace SimpleColorFade
    {
       public partial class Form1 : Form
       {
          Form1Functionality functionality;
          public Form1()
          {
             InitializeComponent();
             functionality = new Form1Functionality(this);
          }
    
          #region Control-Porperties
          public TableLayoutPanel ThatTableLayoutPanel { get { return this.tableLayoutPanel1; } }
          #endregion
    
          private void Form1_Load(object sender, EventArgs e)
          {
             functionality.PopulatePanel();         
          }
       }
    }

    Interaktivität mit zur Laufzeit generierten Controls, Part 2/5

    Form1.Designer.cs

    namespace SimpleColorFade
    {
       partial class Form1
       {
          /// <summary>
          /// Erforderliche Designervariable.
          /// </summary>
          private System.ComponentModel.IContainer components = null;
    
          /// <summary>
          /// Verwendete Ressourcen bereinigen.
          /// </summary>
          /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
          protected override void Dispose(bool disposing)
          {
             if(disposing && (components != null))
             {
                components.Dispose();
             }
             base.Dispose(disposing);
          }
    
          #region Vom Windows Form-Designer generierter Code
    
          /// <summary>
          /// Erforderliche Methode für die Designerunterstützung.
          /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
          /// </summary>
          private void InitializeComponent()
          {
             this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
             this.SuspendLayout();
             // 
             // tableLayoutPanel1
             // 
             this.tableLayoutPanel1.AutoSize = true;
             this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
             this.tableLayoutPanel1.BackColor = System.Drawing.Color.MidnightBlue;
             this.tableLayoutPanel1.ColumnCount = 1;
             this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
             this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
             this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
             this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
             this.tableLayoutPanel1.Name = "tableLayoutPanel1";
             this.tableLayoutPanel1.RowCount = 1;
             this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
             this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 1F));
             this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 1F));
             this.tableLayoutPanel1.Size = new System.Drawing.Size(0, 0);
             this.tableLayoutPanel1.TabIndex = 0;
             // 
             // Form1
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.BackColor = System.Drawing.Color.Black;
             this.ClientSize = new System.Drawing.Size(292, 272);
             this.Controls.Add(this.tableLayoutPanel1);
             this.Name = "Form1";
             this.Text = "Sandbox";
             this.Load += new System.EventHandler(this.Form1_Load);         
             this.ResumeLayout(false);
             this.PerformLayout();
    
          }
    
          #endregion
    
          private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
       }
    }
    
    

    Interaktivität mit zur Laufzeit generierten Controls, Part 1/5

    Dieser Eintrag steht in Bezug zu "Control-Generierung und handling zur Laufzeit, Part 1 bis 5".
    Ich bin dabei, dieses Projekt in Schritten zu erweitern. Hier wird gezeigt, wie auch Events von zur Laufzeit generierten Controls abonniert werden.
    Heraus kommt ein PictureBox-Grid in einem TableLayoutPanel, dessen einzelne Zellen ihre Farbe ändern, wenn man mit der Maus darüber fährt.
    In einem separaten Thread pro Zelle wird wieder in die ursprüngliche Farbe zurück gefadet, wenn der Mauszeiger die Zelle verlässt.

     

    Screenshot

    December 17

    Referenzierung von WinForm-Controls

    Hallo,
    ich halte gerne Ordnung in meinem Code. Wer einmal in einem Team entwickelt hat, weiß wie wichtig das ist. Gerade bei der Entwicklung von WinForms-Anwendungen stand ich aber immer wieder vor dem Problem daß man die Controls eines WinForms nicht in einer anderen Klasse referenzieren kann - jedenfalls nicht über Standardinstantiierung, aber dazu komme ich gleich. Dabei wollte ich doch so gerne meinen Code sauber halten, und ausser den Control-Eventhandlern nichts in meinem Form1-Code sehen!

    Form1 formInstance = new Form1();

    ergibt zwar keine Fehlermeldung, aber versucht man, etwas aus dieser Instanz heraus zu holen (z.B. formInstance.PictureBox_soundso), merkt man sofort daß das nicht funzt. Nach langen und fruchtlosen Versuchen holte ich mir Rat in einem Forum. Callback-Delegate war das Schlagwort. Das ist eine tolle Sache, aber sehr umständlich. Letztes Wochenende kam nun endlich der Durchbruch mit dem Buch "Visual C# 2005 für Profis"! Ich kann das Buch nur empfehlen. Nach kurzem Studium eines Kapitels sieht die Lösung zu dem Problem wie folgt aus:

    Man schreibt für jedes zu referenzierenede Control ein Property in die Form1-Klasse (oder ein anderes Form). Das geht ganz einfach (in diesem Beispiel möchte ich ein Label mit dem Namen lbl_Test referenzieren):

    internal Label lbl get return lbl_Test; }}

    In einer anderen Klasse kann man nun auf diese Properties zugreifen. Die Instantiierung der Forms-Klasse ist zwar etwas umständlicher, aber immer noch easy, wenn man es einmal weiß:

    public class TestClass
    {
       private Form1 m_Form;

       /// <summary>
       /// Konstruktor für die Referenzierung einer Forms-Klasse.
       /// </summary>
       /// <param name="frm">Der Name der Forms-Klasse, die diese Klasse referenziert.</param>
       public TestClass(Form1 frm)
       {
          m_Form = frm;

       }
       

       public
    void SetLabelText()

       {
          m_Form.lbl.Text = "Dieser Text stammt aus der Testklasse!";

       }
    }

    Stylesheet, Einbindung

    Im Seitencode unter <title>...</title>
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />

    Dropdown-Liste in einem GridView mit foreign keys

    Eine Tabelle T_PC besitzt einen foreign key "ID_Zustand", der mit dem Primary key der Tabelle "T_Zustand" verbunden ist. Das Update wird über eine Stored Procedure gefahren:

     

    ALTER PROCEDURE dbo.UpdatePC

    (

    @PC_ID uniqueidentifier,

    @Bezeichnung varchar(50),

    @Inventarnummer varchar(50),

    @Kostentraeger varchar(50),

    @Seriennummer varchar(50),

    @Zustand varchar(50),

    @Sonderregelung varchar(255),

    @LieferantName varchar(50),

    @Herstellername varchar(50)

    )

    AS

    SET NOCOUNT OFF

    DECLARE @ID_Hersteller uniqueidentifier

    DECLARE @ID_Lieferant uniqueidentifier

    DECLARE @ID_Zustand uniqueidentifier

    -- Zugehörige IDs holen

    SET @ID_Hersteller = (SELECT Hersteller_ID FROM T_Hersteller WHERE (Herstellername = @Herstellername))

    SET @ID_Lieferant = (SELECT Lieferant_ID FROM T_Lieferant WHERE (LieferantName = @LieferantName))

    SET @ID_Zustand = (SELECT Zustand_ID FROM T_Zustand WHERE (Zustand = @Zustand))

    -- Update

    UPDATE T_PC SET [Bezeichnung] = @Bezeichnung, [Inventarnummer] = @Inventarnummer, [Kostentraeger] = @Kostentraeger, [Seriennummer] = @Seriennummer, [ID_Zustand] = @ID_Zustand, [Sonderregelung] = @Sonderregelung, [ID_Lieferant] = @ID_Lieferant, [ID_Hersteller] = @ID_Hersteller WHERE (PC_ID = @PC_ID)

    RETURN

     

    Die Gridview wird mit folgender SQL-Anweisung befüllt:

    SELECT T_PC.PC_ID, T_PC.Bezeichnung, T_PC.Inventarnummer, T_PC.Kostentraeger, T_PC.Seriennummer, T_PC.Sonderregelung,
    T_Hersteller.Herstellername, T_Lieferant.LieferantName, T_Zustand.Zustand, T_Lieferant.Lieferant_ID, T_Zustand.Zustand_ID,
    T_Hersteller.Hersteller_ID
    FROM T_PC INNER JOIN
    T_Zustand ON T_PC.ID_Zustand = T_Zustand.Zustand_ID INNER JOIN
    T_Lieferant ON T_PC.ID_Lieferant = T_Lieferant.Lieferant_ID INNER JOIN
    T_Hersteller ON T_PC.ID_Hersteller = T_Hersteller.Hersteller_ID

    Die ID-Felder werden unsichtbar gemacht, indem man das Column-Property "visible" auf false setzt.

    Damit der User den Zustand nicht in eine Textbox eingibt, soll er über eine Dropdown-Liste ausgewählt werden.

    Dazu ändert man das Feld "Zustand" in der GridView in ein Template field.

    Über Edit templates wählt man aus der Template-Liste "EditItemTemplate" des Zustand-Columns aus.

    Im Container befindet sich eine Textbox.

    Diese wird gelöscht und aus der Toolbox wird eine Dropdown-Liste eingesetzt.

    Als DataSource kann nicht die der GridView gewählt werden, weil diese durch den kombinierten SELECT (INNER JOIN) für den Zustand nur Werte zur Verfügung stellt, die durch diese Funktion auch zu Tage treten.

    Man wählt also eine eigene DataSource, in diesem Fall eine SqlDataSource von "T_Zustand". Es müßte auch mit einer ObjectDataSource aus dem entsprechenden TableAdapter funktionieren. Das werde ich morgen im Institut ausprobieren.

    Hier ist der komplette aspx-Code der Dropdown-Liste:

     

    <asp:DropDownList ID="grdv_ddl_Zustand" runat="server" DataSourceID="SqlDataSource_Zustand"

    DataTextField="Zustand" DataValueField="Zustand" SelectedValue='<%# Bind("Zustand") %>'>

    </asp:DropDownList>

     

    Damit die Parameter mit der Stored Procedure zusammenpassen, gibt man sowohl für DataTextField, als auch für DataValueField und SelectedValue "Zustand" an.

    Der aspx-Code der SqlDataSource:

     

    <asp:SqlDataSource ID="SqlDataSource_Zustand" runat="server" ConnectionString="<%$ ConnectionStrings:AMS2ConnectionString1 %>"

    SelectCommand="SELECT [Zustand] FROM [T_Zustand] ORDER BY [Zustand]">

    </asp:SqlDataSource>

     

    Die ID wird nicht benötigt.

    ASP.net Development Server ohne VS starten

    DOS-Batchfile mit folgendem Inhalt anlegen:

    @echo off
    :input
    SET /p input=Pfad angeben:
    c:
    cd
    cd %WinDir%Microsoft.NETFrameworkv2.0.50727
    WebDev.WebServer.EXE /port:8080 /path:"%input%"

    Eigene 404-Page

    Damit eine individuelle 404-Page gefeuert wird, muß in der Web.config nur die CustomErrors-Sektion dekommentiert werden. Selbstredend daß man noch entsprechende Errorpages designed und zum Projekt hinzufügt.

    Exception handling in ASP.net

    1. Beliebige Errorpage gestalten.

    2. Die Headerzeile der Default-Page (*) um den Eintrag ErrorPage erweitern. Siehe Codebeispiel:

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ErrorPage="~/Error.aspx" %>

    (*): Oder einer anderen Seite, falls individuelle Errorpages angefahren werden sollen.

    Automatisches Anpassen einer Grafik an die Fenstergröße

    <head>
    <title>Untitled Page</title>
    <style type="text/css">
    img {width: 100%;height: 100%;z-index: 1;}
    </style>
    </head>

     

    Das Bild selber kann in der Breite nicht in Prozent angegeben werden, aber der DIV, in den das Bild eingebettet ist. Darauf ist zu achten. Wenn die Breite des DIV in Pixeln angegeben ist, funktioniert das Ganze nicht.

    Schritt für Schritt:

    1. Layer auf die Seite setzen.

    2. Bild in den Layer schieben.

    3. Layerposition korrigieren.

    4. im HTML-Code die width- und height-Properties des Layers von px auf % ändern.

    5. Obigen Code in der Head-Sektion platzieren.

    Scrollposition merken

    <%@ Page Language="C#" MaintainScrollPositionOnPostback="true" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

    Durch das boolean Property MaintainScrollPositionOnPostback behält die Webseite ihre augenblickliche Position, statt nach einem Postback wieder an das obere Ende zu springen.

    Control-Generierung und handling zur Laufzeit, Part 3/5: Form1.cs

    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using System.Threading;
    using System.Drawing;
    
    namespace ControlGenerationDynamic
    {
       public partial class Form1 : Form
       {
          #region Global Class members
          private string basicPictureBoxName;
          private Thread bgThread;
          private int numberOfSubControls;
          private List<PictureBox> pictureBoxList;
          protected delegate void PictureBoxCallBack(int pictureBoxNumber, Color colorState);
          private static Object syncLock = new Object();
          #endregion
          #region Constructors
          public Form1()
          {
             basicPictureBoxName = "pictureBox_";
             bgThread = new Thread(new ThreadStart(DoColorSequence));
             numberOfSubControls = 25;
             pictureBoxList = new List<PictureBox>();
    
             InitializeComponent();
          }
          #endregion
          #region EventHandler
          private void Form1_FormClosing(object sender, FormClosingEventArgs e)
          {
             AbortAndJoinThread();
          }
    
          private void Form1_Load(object sender, EventArgs e)
          {
             PopulateFlowLayoutPanel();
             bgThread.Name = "ShowThread";
             bgThread.Start();
          }
    
          private void radioButton_Sequential_CheckedChanged(object sender, EventArgs e)
          {
             AbortAndJoinThread();
             if(radioButton_Sequential.Checked)
             {
                ResetControls();
                bgThread = new Thread(new ThreadStart(DoColorSequence));
                bgThread.Start();
             }
          }
    
          private void radioButton_Random_CheckedChanged(object sender, EventArgs e)
          {
             AbortAndJoinThread();
             if(radioButton_Random.Checked)
             {
                ResetControls();
                bgThread = new Thread(new ThreadStart(DoRandom));
                bgThread.Start();
             }
          }
          #endregion
          #region Methods      
          private void AbortAndJoinThread()
          {
             bgThread.Abort();
             bgThread.Join();
          }
    
          private void DoColorSequence()
          {
             lock(syncLock)
             {
                while(true)
                {
                   for(int i = 0; i < numberOfSubControls; i++)
                   {                  
                      InvokePictureBox(i, Color.Orange);
                      Thread.Sleep(250);
                      InvokePictureBox(i, Color.BlueViolet);
                   }               
                }
             }
          }
    
          private void DoRandom()
          {
             lock(syncLock)
             {
                Random rnd = new Random();
                int randomNumber = 0;
                while(true)
                {
                   randomNumber = rnd.Next(25);
                   InvokePictureBox(randomNumber, Color.Orange);
                   Thread.Sleep(250);
                   InvokePictureBox(randomNumber, Color.BlueViolet);
                }
             }
          }
    
          private void InvokePictureBox(int pictureBoxNumber, Color colorState)
          {
             if(pictureBoxList[pictureBoxNumber].InvokeRequired)
             {
                PictureBoxCallBack pbc = new PictureBoxCallBack(InvokePictureBox);
                pictureBoxList[pictureBoxNumber].Invoke(pbc, new object[] { pictureBoxNumber, colorState });
             }
             else
                pictureBoxList[pictureBoxNumber].BackColor = colorState;
          }
    
          private void PopulateFlowLayoutPanel()
          {
             for(int i = 0; i < numberOfSubControls; i++)
             {
                PictureBox myPictureBox = new PictureBox();
                myPictureBox.Name = basicPictureBoxName + (i + 1).ToString();
                myPictureBox.Size = new Size(32, 32);
                myPictureBox.BackColor = Color.BlueViolet;
                pictureBoxList.Add(myPictureBox);
                this.flowLayoutPanel1.Controls.Add(pictureBoxList[i]);
             }
          }
    
          private void ResetControls()
          {
             for(int i = 0; i < numberOfSubControls; i++)
             {
                pictureBoxList[i].BackColor = Color.BlueViolet;
             }
          }
          #endregion
       }
    }

    Control-Generierung und handling zur Laufzeit, Part 2/5: Form1.Designer.cs

    namespace ControlGenerationDynamic
    {
       partial class Form1
       {
          /// <summary>
          /// Erforderliche Designervariable.
          /// </summary>
          private System.ComponentModel.IContainer components = null;
    
          /// <summary>
          /// Verwendete Ressourcen bereinigen.
          /// </summary>
          /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
          protected override void Dispose(bool disposing)
          {
             if(disposing && (components != null))
             {
                components.Dispose();
             }
             base.Dispose(disposing);
          }
    
          #region Vom Windows Form-Designer generierter Code
    
          /// <summary>
          /// Erforderliche Methode für die Designerunterstützung.
          /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
          /// </summary>
          private void InitializeComponent()
          {
             this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
             this.radioButton_Sequential = new System.Windows.Forms.RadioButton();
             this.radioButton_Random = new System.Windows.Forms.RadioButton();
             this.SuspendLayout();
             // 
             // flowLayoutPanel1
             // 
             this.flowLayoutPanel1.BackColor = System.Drawing.Color.Navy;
             this.flowLayoutPanel1.Location = new System.Drawing.Point(50, 40);
             this.flowLayoutPanel1.Name = "flowLayoutPanel1";
             this.flowLayoutPanel1.Size = new System.Drawing.Size(190, 190);
             this.flowLayoutPanel1.TabIndex = 0;
             // 
             // radioButton_Sequential
             // 
             this.radioButton_Sequential.AutoSize = true;
             this.radioButton_Sequential.Checked = true;
             this.radioButton_Sequential.ForeColor = System.Drawing.SystemColors.Control;
             this.radioButton_Sequential.Location = new System.Drawing.Point(50, 243);
             this.radioButton_Sequential.Name = "radioButton_Sequential";
             this.radioButton_Sequential.Size = new System.Drawing.Size(75, 17);
             this.radioButton_Sequential.TabIndex = 1;
             this.radioButton_Sequential.TabStop = true;
             this.radioButton_Sequential.Text = "Sequential";
             this.radioButton_Sequential.UseVisualStyleBackColor = true;
             this.radioButton_Sequential.CheckedChanged += new System.EventHandler(this.radioButton_Sequential_CheckedChanged);
             // 
             // radioButton_Random
             // 
             this.radioButton_Random.AutoSize = true;
             this.radioButton_Random.ForeColor = System.Drawing.SystemColors.Control;
             this.radioButton_Random.Location = new System.Drawing.Point(175, 243);
             this.radioButton_Random.Name = "radioButton_Random";
             this.radioButton_Random.Size = new System.Drawing.Size(65, 17);
             this.radioButton_Random.TabIndex = 2;
             this.radioButton_Random.Text = "Random";
             this.radioButton_Random.UseVisualStyleBackColor = true;
             this.radioButton_Random.CheckedChanged += new System.EventHandler(this.radioButton_Random_CheckedChanged);
             // 
             // Form1
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.BackColor = System.Drawing.Color.Black;
             this.ClientSize = new System.Drawing.Size(292, 272);
             this.Controls.Add(this.radioButton_Random);
             this.Controls.Add(this.radioButton_Sequential);
             this.Controls.Add(this.flowLayoutPanel1);
             this.Name = "Form1";
             this.Text = "Form1";
             this.Load += new System.EventHandler(this.Form1_Load);
             this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
             this.ResumeLayout(false);
             this.PerformLayout();
    
          }
    
          #endregion
    
          private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
          private System.Windows.Forms.RadioButton radioButton_Sequential;
          private System.Windows.Forms.RadioButton radioButton_Random;
       }
    }
    
    

    Control-Generierung und handling zur Laufzeit, Part 1/5: Beschreibung

    Dieses Demo zeigt, wie zur Laufzeit Controls generiert und angesteuert werden.
    Im Detail:
    Ein FlowLayoutPanel (zur Designzeit erstellt) wird zur Laufzeit mit einem Array aus 5x5 PictureBoxen befüllt.
    Zuerst wird im Klassenkonstruktor ein Objekt vom Typ List<PictureBox> erzeugt. Es handelt sich hier also um eine generische Liste vom Typ PictureBox.
    In einer For-Schleife wird 25 mal eine Instanz der PictureBox-Klasse erzeugt und mit spezifischen Grundeigenschaften, wie Name und Farbe, versehen. Anschließend wird dieses Objekt der generischen PictureBox-Liste hinzugefügt. Schließlich wird dieses Array in das FlowLayoutPanel eingesetzt, wobei man sich hier keine weiteren Gedanken um das Layout zu machen braucht. Das übernimmt das FlowLayoutPanel.
    Das alles passiert in der Methode PopulateFlowLayoutPanel().
    Je nachdem, welcher RadioButton geklickt wird, durchläuft das PictureBox-Array entweder eine sequentielle Farbänderung über DoColorSequence(), oder eine Zufällige über DoRandom(). daß heißt, eine PictureBox nach der anderen ändert seine Hintergrundfarbe von violett auf Orange und wieder zurück, oder diese Farbänderung geschieht nach Zufallsgenerator.

    Was das Demo noch zeigt:
    Die beiden Farblaufmethoden laufen als eigener Thread. Hier wird Threading mit einfachem Lock gezeigt und darüber hinaus Zugriff auf Controls über Invoke.

    December 09

    Fehler oder nicht?

    Am 4.12. schrieb ich, es sei ein Fehler daß die FadingMessageBox für jeden Aufruf neu instanziiert werden muß. Seit ein paar Tagen bin ich mir da nicht mehr so sicher. Eigentlich ist dieser Umstand doch positiv zu bewerten - es spart Resourcen und ein "manuelles" Dispose zur Designzeit entfällt (z.B. über using).