Edytowalny ListBox
Dużo czasu spędzam ostatnio przy mini projekcicku [więcej info wkrótce]. Potrzebowałem kontrolki listboxa ale z edytowalnymi polami. Jako że w .necie taka kontrolka nie jest dostępna standardowo to postanowiłem “wyciosać” własną. .Net daje nam spore pole do popisu jeżeli chodzi o tworzenie własnych “customowych” kontrolek.
By tego dokonać będziemy potrzebować dwóch nowych kontrolek.
- TextBoxa który będzie komunikował się z danymi ListBoxa
- ListBoxa wykorzystującego tego TextBoxa
Opis Działania.
ListBox normalnie wyświetli Itemy. W momencie wybrania któregoś pola w miejscu Itema wygeneruje odpowiednią kontrolkę TextBoxa. Po wprowadzeniu textu do TextBoxa będzie on znikał i tracił “focus” przesyłając zmiany do ListBoxa , [ skupienie :D jak to dziwnie brzmi ostatnio zauważyłem że niektóre wydawnictwa tłumaczą “singleton” jako “samotnik” ].
TextBox
Zaczniemy od TextBoxa bo zawiera on mniej modyfikacji.
class EdditableTextBox : TextBox
{
private int _index = -1;
public int Index
{
set { _index = value; }
}
...
Dziedziczymy po standardowum TextBoxie i definiujemy pole Index , które będzie zawierać numer naszego obiektu , itemu , w listBoxie.
protected override void OnLeave(EventArgs e)
{
((ListBox)this.Parent).Items[_index] = this.Text;
base.OnLeave(e);
}
Przeciążamy metodę wywoływaną w momencie wychodzenia z textBoxa tak by wszelkie wprowadzone zmiany zmieniały także obiekt w naszym ListBoxie do tego jest nam potrzebny parametr Index , który określa do którego konkretnie elementu się odnosimy.Odwołanie do ListBoxa realizujemy poprzez propercje Parent.
Na wszelki wypadek wywołujemy bazową metodę.
Można oczywiście zrobić inny mechanizm. Np akceptowanie zmian w momencie wciśnięcia odpowiegniego klawisza.
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
((ListBox)this.Parent).Items[_index] = this.Text;
}
base.OnKeyPress(e);
}
W tym przypadku Enter
ListBox
Przejdzmy teraz do ListBoxa. Tutaj już będzie troszeczkę więcej zmian.
class EdditableListBox : ListBox
{
private static int MarginBeetwenItems = 5;
private EdditableTextBox tbox;
Dziedziczymy po ListBoxie tworzymy propercje oznaczającą odstęp pomiędzy wyświetlanymi itemamy i tworzymy lokalna kopię naszego zmodyfikowanego TextBoxa
public EdditableListBox()
{
tbox = new EdditableTextBox();
tbox.Hide();
tbox.Parent = this;
Controls.Add(tbox);
}
Tworzymy Text Boxa ustawiamy jego “rodzica” . Propercja Parent będzie nam służyła do komunikacji pomiędzy TextBoxem i ListBoxem.
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Index > -1)
{
string s = Items[e.Index].ToString();
Rectangle rect = new Rectangle(
e.Bounds.X, e.Bounds.Y + (e.Index * MarginBeetwenItems),
e.Bounds.Width, e.Bounds.Height);
e.Graphics.DrawString(s, Font, new SolidBrush(SystemColors.WindowText), rect);
}
}
Musimy przeciążyć metodę odrysowywującą. Na wszelki wypadek badamy index Obiektu w ListBoxie , wyznaczamy prostokąt na podstawie jego parametrów , uwzględniając MarginBeetwenItems. Rysujemy napis w odpowiednim miejscu.
protected override void OnMouseUp(MouseEventArgs e)
{
int index = IndexFromPoint(e.X, e.Y);
if (index != ListBox.NoMatches && index != 65535)
{
if (e.Button == MouseButtons.Left)
{
string s = Items[index].ToString();
Rectangle rect = GetItemRectangle(index);
tbox.Location = new Point(rect.X, rect.Y + (index * MarginBeetwenItems));
tbox.Size = new Size(rect.Width, rect.Height);
tbox.Text = s;
tbox.Index = index;
tbox.SelectAll();
tbox.Show();
tbox.Focus();
}
}
base.OnMouseUp(e);
}
Główna metoda tworząca [chociaż bardziej pasuje słowo modyfikująca] odpowiednio kontrolke TextBoxa , przy kliknięciu Lewym Przyciskiem myszki. Parametry TextBoxa modyfikujemy tak aby wyświetlał się w miejscu klikniętego obiektu na ListBoxie.
protected override void OnSelectedIndexChanged(EventArgs e)
{
tbox.Hide();
base.OnSelectedIndexChanged(e);
}
protected override void OnLeave(EventArgs e)
{
tbox.Hide();
base.OnLeave(e);
}
Odpowiednie funkcje powodujące zniknięcie naszego TextBoxa przy wyjściu z kontrolki i przy zmianie obiektu.