.NET Blog   ·   .NET Casts   ·   .NET GUI Foren   ·   .NET BlogBook   ·   WPF Blogger   ·   visual studio one   ·   ASP.NET professional

  • ACHTUNG - NEUES BLOG

    Ab sofort steht unter http://devtyr.norberteder.com mein neues Blog zur Verfügung. Dieses Blog wird nicht weiter betreut, bleibt aber erhalten. Neue Eintr%auml;ge erfolgen nur mehr im neuen Blog. Kommentare werden ebenfalls nicht mehr behandelt. Wer weiterhin meinen Einträgen und Aktivitäten folgen möchte, möge bitte RSS-Feeds, Verlinkungen etc. an die neue Location anpassen.
Download .NET Essentials Installer
Trickkiste

UserControls im Skype-Stil selbst erstellt

22.05.07 - .NET, Grundlagen, Base Framework, WPF
Beitrag von Norbert Eder
 Beiträge zur Erstellung von UserControls finden sich im Internet wahrlich viele. Dennoch wird immer wieder danach gefragt, wodurch ich mich schließlich hinreissen ließ, eine kurze Demo zu erstellen. Gezeigt wird, wie ein ProgressBar im Skype-Stil erstellt wird.

Grundlegend ist eine neue Klasse zu erstellen, welche von UserControl ableitet. Da das Control via GDI+ gezeichnet wird, muss an dieser Stelle das OnPaint-Event überschrieben werden, was wir entsprechend im Konstruktor mitteilen müssen:
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

Wird ControlStyles.UserPaint auf true gesetzt, müssen alle notwendigen Aktualisierungen der Oberfläche selbst vorgenommen werden. Die beiden weiteren Styles sind lediglich Hilfen, die ein Flackern beim Neuzeichnen verhindern bzw. unterdrücken sollen.

Ist dieser Schritt getan, muss die Logik implementiert werden. Dazu gehören die entsprechenden Eigenschaften zur Bestimmung des Minimums, Maximums und des aktuellen Values (wie wir es von einer Standard-ProgressBar gewohnt sind). Wurde auch dies erledigt, geht es daran, das Control zu zeichnen.

Hierzu werden ich allerdings den gesamten Sourcecode des UserControls auflisten:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace SkypeLookControls
{
    public partial class SkypeProgressBar : UserControl
    {
        private int _min = 0;
        private int _max = 100;
        private int _value = 0;

        private Point[] _borderPoints = null;

        private Color borderColor = Color.FromArgb(174, 179, 179);
        private Color brightDisabledColor = Color.FromArgb(222, 222, 222);

        private Color darkDisabledColor1 = Color.FromArgb(209, 209, 209);
        private Color darkDisabledColor2 = Color.FromArgb(216, 216, 216);

        private Color brightActiveColor = Color.FromArgb(0, 183, 254);
        private Color darkActiveColor1 = Color.FromArgb(0, 167, 233);
        private Color darkActiveColor2 = Color.FromArgb(0, 183, 254);

        /// <summary>
        /// Gets or sets the minimum.
        /// </summary>
        /// <value>The minimum.</value>
        public int Minimum
        {
            get { return this._min; }
            set { this._min = value; }
        }

        /// <summary>
        /// Gets or sets the maximum.
        /// </summary>
        /// <value>The maximum.</value>
        public int Maximum
        {
            get { return this._max; }
            set { this._max = value; }
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public int Value
        {
            get { return this._value; }
            set { this._value = value; this.Invalidate(); this.Update(); }
        }

        /// <summary>
        /// Initializes a new instance of the 
        /// <see cref="SkypeProgressBar"/> class.
        /// </summary>
        public SkypeProgressBar()
        {
            InitializeComponent();

            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            Init();

            this.Resize += new EventHandler(SkypeProgressBar_Resize);
        }

        void SkypeProgressBar_Resize(object sender, EventArgs e)
        {
            Init();

            this.Invalidate();
            this.Update();
        }

        private void Init()
        {
            this._borderPoints = new Point[9];
            Point p1 = new Point(3, 1);
            Point p2 = new Point(this.Width - 3, 1);
            Point p3 = new Point(this.Width - 1, 3);
            Point p4 = new Point(this.Width - 1, this.Height - 3);
            Point p5 = new Point(this.Width - 3, this.Height - 1);
            Point p6 = new Point(3, this.Height - 1);
            Point p7 = new Point(1, this.Height - 3);
            Point p8 = new Point(1, 3);
            Point p9 = new Point(3, 1);

            this._borderPoints[0] = p1;
            this._borderPoints[1] = p2;
            this._borderPoints[2] = p3;
            this._borderPoints[3] = p4;
            this._borderPoints[4] = p5;
            this._borderPoints[5] = p6;
            this._borderPoints[6] = p7;
            this._borderPoints[7] = p8;
            this._borderPoints[8] = p9;
        }

        /// <summary>
        /// Raises the 
        /// <see cref="E:System.Windows.Forms.Control.Paint"></see> 
        /// event.
        /// </summary>
        /// <param name="e">A 
        /// <see cref="T:System.Windows.Forms.PaintEventArgs"></see> 
        /// that contains the event data.</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            Point[] points = new Point[4];
            Point p1 = new Point(0,0);
            Point p2 = new Point(this.Width, 0);
            Point p3 = new Point(this.Width, this.Height);
            Point p4 = new Point(0, this.Height);
            points[0] = p1;
            points[1] = p2;
            points[2] = p3;
            points[3] = p4;

            e.Graphics.DrawPolygon(
                new Pen(new SolidBrush(this.BackColor)), points);

            PaintBorder(e);
            PaintBar(e);
        }

        /// <summary>
        /// Paints the border.
        /// </summary>
        /// <param name="e">The 
        /// <see cref="System.Windows.Forms.PaintEventArgs"/> 
        /// instance containing the event data.</param>
        private void PaintBorder(PaintEventArgs e)
        {
            Pen p = new Pen(new SolidBrush(this.borderColor));
            e.Graphics.DrawLines(p, this._borderPoints);
        }

        /// <summary>
        /// Paints the disabled part of the progress bar.
        /// </summary>
        /// <param name="e">The 
        /// <see cref="System.Windows.Forms.PaintEventArgs"/> 
        /// instance containing the event data.</param>
        private void PaintDisabledPart(PaintEventArgs e)
        {
            int y = (this.Height-6) / 2;

            LinearGradientBrush lgb = 
                new LinearGradientBrush(
                    new Rectangle(3,y, this.Width-5, this.Height-y-3), 
                    this.darkDisabledColor1, 
                    this.darkDisabledColor2, 
                    90f);
            e.Graphics.FillRectangle(lgb, 
                    new Rectangle(3, y+1, this.Width - 5, this.Height-y-3));

            e.Graphics.FillRectangle(
                    new SolidBrush(this.brightDisabledColor), 
                    new Rectangle(3, 3, this.Width - 5, y));
        }

        /// <summary>
        /// Paints the active part of the progress bar.
        /// </summary>
        /// <param name="e">The 
        /// <see cref="System.Windows.Forms.PaintEventArgs"/> 
        /// instance containing the event data.</param>
        private void PaintActivePart(PaintEventArgs e)
        {
            int y = (this.Height - 6) / 2;
            double width = (this.Width - 6d) / 
                (double) this.Maximum * (double)this.Value;

            if (Value == Maximum)
                width = this.Width - 6d;

            if (width > 0)
            {
                LinearGradientBrush lgb = 
                    new LinearGradientBrush(
                        new Rectangle(3, y, (int)width, this.Height - y - 3), 
                        this.darkActiveColor1, 
                        this.darkActiveColor2, 
                        90f);
                e.Graphics.FillRectangle(lgb, 
                    new Rectangle(3, y + 1, (int)width, this.Height - y - 3));

                e.Graphics.FillRectangle(
                    new SolidBrush(this.brightActiveColor), 
                    new Rectangle(3, 3, (int)width, y));
            }
        }

        /// <summary>
        /// Paints the progress bar.
        /// </summary>
        /// <param name="e">The 
        /// <see cref="System.Windows.Forms.PaintEventArgs"/> 
        /// instance containing the event data.</param>
        private void PaintBar(PaintEventArgs e)
        {
            PaintDisabledPart(e);
            PaintActivePart(e);
        }
    }
}

Wichtig ist, dass beim Setzen der Eigenschaft Value durch ein Invalidate das Neuzeichnen des Controls veranlasst wird. Durch den Aufruf der Update-Methode, wird die Aktualisierung sofort vorgenommen. Durch den Auftrag, das Control neu zu zeichnen, wird das OnPaint-Event ausgelöst, welches nun das Control tatsächlich via GDI+ zeichnet.

Das war es mit der "Hexerei".

Das Endergebnis sieht schließlich folgendermaßen aus:



  1 Kommentar - 1815 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Transparente Steuerelemente mit C#

08.03.07 - .NET, Grundlagen, WPF
Beitrag von Norbert Eder
 Eigentlich ein alter Hut, aber ad hoc ist es mir heute auch nicht eingefallen wie eigene Steuerelemente transparent erstellt werden können. Der erste Versuch mit

this.Background = Color.Transparent;

scheiterte kläglich. Hier ein Lösungsweg:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle := 0x20;
        return cp;
    }
}

protected override void OnPaintBackground
    (PaintEventArgs pevent)
{
    // do nothing in this case
}

protected override void OnMove(EventArgs e)
{
    RecreateHandle();
}

Das war es dann auch schon wieder.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Die ideale Workstation-Konfiguration für einen WPF-Entwickler

23.01.07 - .NET, WPF
Beitrag von Norbert Eder
 Wer sich für die Windows Presentation Foundation interessiert und darauf neue Anwendungen aufbauen will, der benötigt auch eine entsprechend konfigurierte Workstation.

Tim Sneath hat eine Übersicht zusammengestellt, die hierzu jedem eine Hilfe sein sollte.

Building a Perfect WPF Developer Workstation

  Kommentar hinzufügen   |  1 Trackbacks   |  Permalink  |  Trackback-URL


ComboBox als DropDownList kann kein Text gesetzt werden

28.12.06 - .NET, WPF
Beitrag von Norbert Eder
 Wer eine ComboBox verwendet und die Eigenschaft DropDownStyle auf DropDownList gesetzt hat, kann keinen Text mehr setzen. Dadurch entfällt auch die Möglichkeit, einen Default-Text zu setzen, wenn kein Item ausgewählt ist/wurde. Dem kann durch eine kurze und schnell Ableitung leicht Abhilfe geschafft werden.

public partial class ComboBoxEx : ComboBox
{
private Label _statusLabel = new Label();
private string _statusText = null;

public string StatusText
{
get { return this._statusText; }
set { this._statusText = value; }
}

public ComboBoxEx()
{
InitializeComponent();

Init();

this.Controls.Add(this._statusLabel);

this._statusLabel.Click += new EventHandler(_statusLabel_Click);
this.SizeChanged += new EventHandler(ComboBoxEx_SizeChanged);
this.SelectedIndexChanged += new EventHandler(ComboBoxEx_SelectedIndexChanged);
}

void _statusLabel_Click(object sender, EventArgs e)
{
this.DroppedDown = true;
}

void ComboBoxEx_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.SelectedItem == null)
Init();
else
this._statusLabel.Visible = false;
}

void ComboBoxEx_SizeChanged(object sender, EventArgs e)
{
Init();
}

public void Init()
{
if (this.DropDownStyle == ComboBoxStyle.DropDownList)
{
this._statusLabel.Visible = true;
this._statusLabel.Location = new Point(1, 1);
this._statusLabel.Size = new Size(this.Width - 20, this.Height - 2 );

if (this._statusText != null)
{
this._statusLabel.Text = this._statusText;
this._statusLabel.Font = this.Font;
}
else
{
this._statusLabel.Text = "[Nothing selected]";
}
this._statusLabel.BringToFront();
}
else
{
this._statusLabel.Visible = false;
}
}
}

Es wird direkt von der ComboBox abgeleitet. Die neue Klasse erhält die Eigenschaft StatusText mit dessen Hilfe ein entsprechender Text gesetzt werden kann, der angezeigt wird, wenn kein Item ausgewählt ist.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Meine GUI friert während der Ausführung ein, was tun?

04.12.06 - .NET, WPF
Beitrag von Norbert Eder
 Ebenfalls eine oft gesehene Frage. Eine Aufgabe wird gerade ausgeführt und dabei friert die Oberfläche der Anwendung ein. Weder ein Fortschrittsbalken wird aktualisiert, noch ein Label, das den Fortschritt anzeigt. Ja selbst beim Verschieben der Form wird diese weiß und zeigt keine Informationen mehr an.

Der Hintergrund liegt einfach daran, dass durch eine ausgeführte Arbeit von dieser Form keine System-Message mehr angenommen wird bzw. diese nicht verarbeitet werden kann. Um dies zu vermeiden (und damit der Benutzer nicht irrtümlich annimmt, die Anwendung sei abgestürzt), bieten sich mehrere Lösungen an:

DoEvents
Durch ein Application.DoEvents() erhält die Anwendung die Möglichkeit, andere Events abzuhandeln und die Form neu zu zeichnen.

Threading
Aufwändige Aufgaben sollten in einem eigenen Thread ausgeführt werden. Dies kann über die Klassen des Namespaces System.Threading geschehen oder über einen BackgroundWorker. Hier ist jedoch zu beachten, dass von Threads nicht direkt auf die GUI-Elemente zugegriffen werden kann. Hierzu muss mit Invoke gearbeitet werden.

Wichtig: Locking
In manchen Fällen ist es wichtig, bestimmte Code-Teile mit einem lock (oder anderen entsprechenden Blockierungs-Maßnahmen) zu versehen. Dadurch kann der gleiche Code nur von einem einzigen Thread aufgerufen und ausgeführt werden. Dies kann mit einem DoEvents nicht ausreichend gelöst werden. In solchen Fällen sollte auf jeden Fall Threading verwendet werden.

  Kommentar hinzufügen   |  2 Trackbacks   |  Permalink  |  Trackback-URL


UserControls eines Namespaces finden

02.11.06 - .NET, Base Framework, WPF
Beitrag von Norbert Eder
 Auf die Frage hin, wie man denn alle UserControls eines bestimmten Namespaces herausfinden kann, um diese dann dynamisch in ein Container-Control zu verfrachten, schrieb ich eine kleine Testanwendung die zeigt, wie man alle Klassen aus einem bestimmten Namespace bekommt. Zusätzlich wird die Information ausgegeben, ob es sich dabei um ein UserControl handelt oder nicht.

Download Beispiel-Projekt (VS 2005 Solution, 40KB)

Bei dieser Lösung ist jedoch anzumerken, dass sich ein Namespace auch über mehrere Assemblies hinweg erstrecken kann. Dies wurde nicht berücksichtigt. Das Beispiel sollte auch eher einen Denkanstoss geben, als eine fix fertige Solution liefern.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Fachartikel: Windows Presentation Foundation - User Interfaces leicht gemacht?

24.10.06 - .NET, WPF
Beitrag von Norbert Eder
 In der aktuellen Ausgabe der Visual Studio One findet sich ein Artikel von mir zum Thema Windows Presentation Foundation - User Interfaces leicht gemacht?.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Die grafische Länge eines Strings mit C# bestimmen

30.08.06 - .NET, Base Framework, WPF
Beitrag von Norbert Eder
 In manchen Fällen (bei der Erstellung von UserControls oder der Verwendung von GDI+) ist es notwendig, die grafische Länge eines Strings zu kennen (also nicht nur die Anzahl der Zeichen). Nachfolgender Code zeigt, wie dies bewerkstelligt werden kann:

string test = "This is a test!";

Font font = new Font("Arial", 10.0F);
Graphics g = this.CreateGraphics();
SizeF sizeInfo = g.MeasureString(test, font);

this ist in diesem Fall eine Form, kann jedoch genausogut eine PictureBox etc. sein.

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Windows Presentation Foundation - Teil 2: XAML und Layouts

26.06.06 - .NET, WPF
Beitrag von Norbert Eder
 Im zweiten Teil der Tutorials-Reihe über die Windows Presentation Foundation werden die Themen XAML und Layouts behandelt. So wird beschrieben, was XAML genau ist und wofür es da ist. Weiters wird gezeigt, welche grundlegenden Layout-Elemente zur Verfügung stehen. Natürlich ist auch dieser Teil wieder voll von Beispiel-Code und Screenshots zur Veranschaulichung.

Windows Presentation Foundation - Teil 2: XAML und Layouts (PDF)

  2 Kommentare - 1207 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


WPF: Rotation und Skalierung einfach gemacht

23.06.06 - .NET, WPF
Beitrag von Norbert Eder
 Beschäftigt man sich näher mit der Materie Windows Presentation Foundation, sieht man schon an sehr einfachen Beispielen, dass die Möglichkeiten schon sehr mächtig sind. Beispielhaft zeige ich an dieser Stelle, wie einfach ein Button skaliert und gedreht werden kann.

Dazu wird im Beispiel ein simpler Button erstellt und plaziert. Über Schieberegeler ist es möglich, die Values für die Rotation bzw. des Zoomfaktors zu setzen. Dies sieht dann folgendermaßen aus:



Um nun die Funktionalität zu implementieren ist nichts weiter zu machen, als die entsprechenden EventHandler zu setzen:

Zoomfaktor

public void sliderZoom_ValueChanged(object sender, RoutedEventArgs e)
{
ScaleTransform st = new ScaleTransform(sliderZoom.Value, sliderZoom.Value);
this.btnTest.LayoutTransform = st;
}


Rotationsfaktor

public void sliderRotate_ValueChanged(object sender, RoutedEventArgs e)
{
RotateTransform rt = new RotateTransform(sliderRotate.Value);
this.btnTest.RenderTransform = rt;
}


Das Ergebnis sieht dann wie folgt aus:



Dies kann jetzt nicht nur auf einen Button angewandt werden, sondern auf die gesamte Oberfläche, oder auch nur einzelne Bereiche. Der dahinterliegenden XAML-Code etc. ist im gesamten Projekt enthalten, welches zum Download bereit steht.

Download WPF Rotation Test Beispiel

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL



Zurück Weiter