Hi Alle.
Habe mir eine Listbox selbst gemacht. Zum ersten mal so etwas.
War erstaunt, wie einfach das geht. Und dieser Artikel hätte mir sehr geholfen. Darum schreibe ich ihn.
Allerdings bitte ich auch die, die Verbesserungen/ Kritik haben, diese zu äußern.
Bin nicht empfindlich.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace Codecompletition
{
public struct Items
{
public Items(string txt, int imgNummer, Color col)
{
text = txt;
imgNr = imgNummer;
color = col;
}
// hier kann man noch alles mögliche unterbringen
// BackColor, Font (mit Vorsicht, da feste Größe)
// muss man dann im Paint umsetzten.
public string text;
public int imgNr;
public Color color;
}
public partial class CodeListBox : Form
{
Timer sortTimer = new Timer();
ImageList images = new ImageList();
List<Items> items = new List<Items>();
int highLightedItem = -1;
string selectedItem = "";
int selectedIndex = -1;
public CodeListBox()
{
InitializeComponent();
panel1.Paint += new PaintEventHandler(panel1_Paint);
vScrollBar1.ValueChanged += new EventHandler(vScrollBar1_ValueChanged);
panel1.MouseDown += new MouseEventHandler(panel1_MouseDown);
sortTimer.Interval = 10;
sortTimer.Tick += new EventHandler(sortTimer_Tick);
}
void sortTimer_Tick(object sender, EventArgs e)
{
// eigendlich ist dieser Timer nur aus einer Art Ratlosigkeit entstanden.
// Wenn ich bei jedem AddItem sortiere, so wird das ganze lahm.
items.Sort( new MyComparer());
// Neu Zeichnen einläuten. Es hat sich etwas verändert
panel1.Invalidate();
sortTimer.Stop();
}
void panel1_MouseDown(object sender, MouseEventArgs e)
{
// Da mein Fenster eine feste Größe hat, kann ich hier ganz einfach ermitten, auf was der User geklickt hat.
// sind immer 16 Pixel Abstand.
highLightedItem = e.Y / 16 + vScrollBar1.Value;
selectedItem = items[highLightedItem].text;
selectedIndex = highLightedItem;
// Neu Zeichnen einläuten. Es hat sich etwas verändert
panel1.Invalidate();
}
public string SelectedItem
{
get { return selectedItem; }
set
{
// kommt noch. Damit man den Index selbst setzen kann
}
}
public int SelectedIndex
{
get { return selectedIndex; }
set
{
// kommt noch. Damit man den Index selbst setzen kann
}
}
void vScrollBar1_ValueChanged(object sender, EventArgs e)
{
// Neu Zeichnen einläuten. Es hat sich etwas verändert
panel1.Invalidate();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
// float txtHeight = e.Graphics.MeasureString("W", this.Font).Height;
// habe ich nicht genommen, da ich eine feste Größe habe und die Bilder auch die Größe vorgeben
for (int i = vScrollBar1.Value; i < Math.Min(vScrollBar1.Value + 10, vScrollBar1.Maximum); i++)
{
if (highLightedItem == i)
{
Pen pen = new Pen(Color.CornflowerBlue,16f);
// wollte kein Rectangle füllen, daher habe ich einfach die Linie dicker gemacht. Soll Windows sich darum kümmern
e.Graphics.DrawLine(pen, new Point(20, (i - vScrollBar1.Value) * 16 + 8), new Point(this.Width, (i - vScrollBar1.Value) * 16 + 8));
e.Graphics.DrawString(items[i].text, this.Font, Brushes.White, 20, (i - vScrollBar1.Value) * 16);
}
else
e.Graphics.DrawString(items[i].text, this.Font, Brushes.Black, 20, (i - vScrollBar1.Value) * 16);
e.Graphics.DrawImage(images.Images[items[i].imgNr], 2, (i - vScrollBar1.Value) * 16, 12, 12);
}
}
public void AddImage(Image img)
{
images.Images.Add(img);
}
public void SetImageList(ImageList imgList)
{
images = imgList;
}
public void AddItem(string text, int imgNr, Color col)
{
items.Add(new Items(text, imgNr, col));
// das Maximum der ScrollBar wird auf die Länge der Liste gesetzt. Einfacher.
vScrollBar1.Maximum = items.Count - 1;
if (!sortTimer.Enabled)
sortTimer.Start();
}
}
public class MyComparer : IComparer<Items>
{
public int Compare(Items x, Items y)
{
// Da habe ich gesucht um das zu finden ;) F1 hat mir geholfen.
return ((Items)x).text.CompareTo(((Items)y).text);
}
}
}
Ein Wort noch zum Paint:
Da ich ja nur 10 Zeilen sichtbar habe, brauchen ja auch nur diese gezeichnet werden. Man denkt zwar, oben und unten gehts weiter, aber dem ist nicht so.
Gruß Robert
Kompl. Projekt im Anhang
hallo,
soviel wie ich sehen kann ist diese Listbox konkret in einer Form verankert, also nicht unbedingt wiederverwendbar. Besser wäre daraus ein einzelnes Control zu machen, welches in einer Form eingebunden werden kann.
// Da mein Fenster eine feste Größe hat, kann ich hier ganz einfach ermitten, auf was der User geklickt hat. // sind immer 16 Pixel Abstand
diese Annahme hat auch nicht unbedingt allgemeingültigen Character.
elli
Hallo elli,
danke für die Antwort.
Sicher gibt es Verbesserungen, da bin ich mir bewusst. Ich lerne ja noch.
Das Script habe ich aber einfach so mit ein paar Änderungen in ein bestehendes Projekt eingebunden und damit das Completition-Teil von CSharpDevelop ersetzt.
Wesendlich handlicher (Da ich weis was abläuft) und schneller.
Die Idee von Dir, daraus ein Control zu machen, werde ich auf jeden Fall aufgreifen.
Muss ich halt noch etwas lernen.
Aber genau feststellen, wo ich bin, kann ich. Jede Zeile ist 16 Pixel groß. Dadurch weis ich genau, in welcher der angezeigten Zeile geclickt wurde.
Wollte eigendlich nur zeigen, wie einfach das sein kann.
Gruß Robert
PS: Wenn mir jemand sagen kann, wie ich diesen Timer ersetzten könnte, wäre das nicht schlecht.
So geht es noch einfacher.
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
Wollte das nicht von Listbox ableiten, da durch das drum herum diese bei vielen Einträgen langsamer ist.
Brauche ja nur Bild und Text. 🙂
Hallo Robertico,
naja, ein Control sollte nun mal eigenständig sein und auch von Control oder einer Unterklasse von Control erben. Dabei bietet sich die Unterklasse an, die schon "am nächsten dran" ist, an dem was man will. Vielleicht wäre da ListView geeignet, denn das hat schon einen VirtualMode, den man benutzen kann, um Probleme mit vielen Einträgen zu vermeiden.
herbivore
Hallo herbivore,
Ein wenig mache ich das ja um zu lernen. Daher möchte ich auch alles selbst machen (was geht).
Habe jetzt Panel gewählt. Ist das OK?
Gruß Robert
Hallo Robertico,
tja, was heißt schon ok. Sicher kann man ein Panel als Basis nehmen. Allerdings ist dann m.E. sehr viel mehr zu programmieren, als wenn man eine ListBox oder ein ListView als Basis nimmt.
herbivore
Original von herbivore
Allerdings ist dann m.E. sehr viel mehr zu programmieren, als wenn man eine ListBox oder ein ListView als Basis nimmt.
Genau das will ich ja.
Wenn ich es kann, kann ich ja den leichteren Weg nehmen. 😉
Gruß Robert