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.