Laden...

[GDI+] Komponente zum einfachen Zeichnen von Elementen (inkl. Scrolling, Zooming + Hittesting)

Erstellt von winSharp93 vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.388 Views
winSharp93 Themenstarter:in
5.742 Beiträge seit 2007
vor 11 Jahren
[GDI+] Komponente zum einfachen Zeichnen von Elementen (inkl. Scrolling, Zooming + Hittesting)

Hallo zusammen,

ausgehend von ein paar Threads hier im Forum, lade ich hier mal eine kleine Komponente hoch, die zeigt, wie man einigermaßen performant und komfortabel einige Items unter GDI zeichnet und dabei Scrolling + Zooming implementiert, ohne, dass es anfängt zu flackern.

Man kann entweder von CanvasControl ableiten (direkt verwenden kann man es im Designer nicht, da es generisch ist) und eine Ableitung von ItemsModel<T> bereitstellen bzw. das Interface ICanvasModel (für mehr Kontrolle) direkt implementieren.
Oder man nimmt die Komponente "auseinander", um Dinge wie AutoScrollPosition zu verstehen und sich generell ein wenig inspirieren lassen 😉

Das Beispielprogramm lässt sich mit ein paar Parametern konfigurieren (nach Änderung "Apply" drücken) und bietet die Möglichkeit, unterschiedlich große Zeichnungen anzuzeigen, automatisches Refreshing nach Zeit zu aktivieren und Scrolling, Zooming und Selektion von Elementen zu testen.

Obwohl die Komponente sicherlich noch den einen oder anderen Bug enthält, ist sie doch einigermaßen einsatzbereit.
Noch ein Hinweis: Die Komponente lag eine Weile bei mir auf der Festplatte und wurde nie wirklich "bereinigt"; es finden sich mit Sicherheit also auch einige suboptimale Codekonstrukte.

Auch handelt es sich noch nicht um ein hochoptimiertes "Nonplusultra", bei weiteren Optimierungen ist sicherlich noch einiges an Spielraum nach oben.
Vielmehr handelt es sich um eine Bündelung verschiedener Tipps und somit quasi um ein GDI-Tutorial für Fortgeschrittene in Codeform 😁

Verbesserungsvorschläge können gerne gemacht werden; auf der TODO-Liste steht (wenn auch mit extrem niedriger Priorität^^) die Möglichkeit, das Zeichnen in einen Hintergrundthread auszulagern, um den UI-Thread bei komplexeren Modellen zu entlasten.

Zur Lizenz:
Über Nennungen in den Credits von Programmen, welch die Komponente verwenden, freue ich mich natürlich - ihr dürft aber mit dem Code machen, was ihr wollt, d.h. beliebig verändern und auch anschließend kommerziell in Closed Source-Projekten nutzen.

Schlagwörter: GDI, WinForms, DoubleBuffer, Scrolling, Zooming, AutoScrollPosition, Performance, Flackern, flackerfrei, Hittesting

winSharp93 Themenstarter:in
5.742 Beiträge seit 2007
vor 11 Jahren

Und hier noch ein Screenshot vom Beispielprogramm:
(Entschuldigt den unkreativen Inhalt, aber ich bin halt nun mal Entwickler und kein Designer 8) )

Zudem noch ein wenig Beispielcode, um einen Eindruck zu vermitteln, was die Komponente einem an Arbeit abnimmt und wie komfortabel die bereitgestellte API ist. ([/EIGENLOB] 😉


protected override void MouseEnter(SampleItem item)
{
    item.IsMouseOver = true;
}
protected override void MouseLeave(SampleItem item)
{
    item.IsMouseOver = false;
}
protected override bool MouseDownOn(SampleItem item)
{
    bool oldSelection = item.IsSelected;
    if (!Control.ModifierKeys.HasFlag(Keys.Control)
        && !Control.ModifierKeys.HasFlag(Keys.Shift))
        this.ClearSelection();

    item.IsSelected = !oldSelection;
    return true;
}
protected override void MouseUp()
{
    this.ClearSelection();
}
        
protected override SizeF LayoutItems(SizeF available)
{
    float width = Math.Max(this.Columns * MIN_COLUMN_WIDTH, available.Width);
    float height = Math.Max(this.Rows * MIN_ROW_HEIGHT, available.Height);

    SizeF size = new SizeF(width, height);

    this._columnWidth = size.Width / this.Columns;
    this._rowHeight = size.Height / this.Rows;

    this._brushes.SetBrushSize(new SizeF(this._columnWidth, this._rowHeight));
    base.LayoutItems(available);

    return size;
}
protected override void LayoutItem(SampleItem item)
{
    item.Layout(this._columnWidth, this._rowHeight);
}
protected override void DrawBackground(Graphics graphics)
{
    base.DrawBackground(graphics);

    RectangleF area = graphics.VisibleClipBounds;
    int startColumn = Math.Max((int)(area.X / this._columnWidth - 1), 0);
    int endColumn = Math.Min(startColumn + (int)(area.Width / this._columnWidth + 2), this.Columns);
    for (int column = startColumn; column <= endColumn; column++)
    {
        float x = column * this._columnWidth;
        graphics.DrawLine(this._brushes.GridPen, x, 0, x, this.Size.Height);
    }

    int startRow = Math.Max((int)(area.Y / this._rowHeight - 1), 0);
    int endRow = Math.Min(startRow + (int)(area.Height / this._rowHeight + 2), this.Rows);
    for (int row = startRow; row <= endRow; row++)
    {
        float y = row * this._rowHeight;
        graphics.DrawLine(this._brushes.GridPen, 0, y, this.Size.Width, y);
    }
}