2012-01-30 16 views
8

Mam ListView gdzie chcę poprawić rysunek elementów (na przykład podświetlanie niektórych ciągów w widokach widoku listy), jednak nie chcę radykalnie zmieniać sposobu wyświetlania elementów.Domyślna implementacja ListView WłaścicielDod

Ustawiłem OwnerDraw na true i mogę zorientować się, jak narysować efekt wyróżnienia, jednak za każdym razem, gdy próbuję odroczyć domyślną implementację, aby narysować resztę elementu widoku listy, coś pójdzie nie tak i jestem pozostawione z całym mnóstwem problemów graficznych wskazujących, że w rzeczywistości kompletnie poszedłem źle.

Czy jest coś, co mogę zobaczyć, co robią "Domyślne" programy obsługi zdarzeń DrawItem i DrawSubItem, dzięki czemu mogę lepiej zrozumieć i łatwiej podkręcić mój kod?

Dla odniesienia Oto fragment pokazujący co mam aktualnie robi:

public MyListView() 
{ 
    this.OwnerDraw = true; 
    this.DoubleBuffered = true; 
    this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader); 
    this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem); 
    this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem); 
} 

private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
{ 
    // Not interested in changing the way columns are drawn - this works fine 
    e.DrawDefault = true; 
} 

private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e) 
{ 
    e.DrawBackground(); 
    e.DrawFocusRectangle(); 
} 

private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) 
{ 
    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 
    if (index >= 0) 
    { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
    } 

    e.DrawText(); 
} 
+0

Chyba trzeba dodać więcej informacji o tym, co się dzieje źle. Przetestowałem twój kod i było ... ok. Podkreślono słowo "Term" żółtym polem. Może pokazać obraz wyników rysunku. – LarsTech

+0

@LarsTech Otrzymuję szeroki zakres problemów, począwszy od efektu selekcji, który nie jest już widoczny (co widać w powyższym przykładzie), aż tekst pozycji jest "podwójnie renderowany", a także znikający po najechaniu myszą na element. Nie oczekuję, że ktoś będzie w stanie magicznie naprawić wszystkie/wszystkie te problemy (to też byłoby zbyt szczegółowe, by pomóc przyszłym użytkownikom), raczej chciałbym zobaczyć przykładową implementację, która odda wszystko tak blisko jak to możliwe standardowa implementacja – Justin

Odpowiedz

10

Nie mam teraz czasu na napisanie pełnej odpowiedzi, więc zamiast tego odłożę kilka szybkich notatek i wrócę do nich później.

Jak powiedział LarsTech, właściciel rysunek kontrolę ListView jest ból - klasa .Net ListView jest owinięcie wokół leżącej Win32 List View Control i zdolność do „Właściciel rysować” jest zapewniana przez NM_CUSTOMDRAW notification code. W związku z tym nie ma "domyślnej implementacji sieci .Net" - domyślnie używa się kontrolki Win32.

Aby uczynić życie jeszcze trudniejsze istnieje szereg dodatkowych rozważań, aby:

  • Jak LarsTech wskazał, pierwszy podpunkt w rzeczywistości reprezentuje samą pozycję nadrzędną, a więc jeśli uchwyt renderowanie zarówno DrawItem i DrawSubItem możesz równie dobrze rysować zawartość pierwszej komórki dwa razy.
  • Jest to błąd w podstawowej kontroli widoku listy (udokumentowanego na banknocie o this page), który oznacza, że ​​zdarzenie DrawItem nastąpi bez odpowiednich DrawSubItem zdarzeń, co oznacza, że ​​jeśli narysować tło w przypadku DrawItem a następnie narysować tekst w zdarzeniu DrawSubItem tekst twojego przedmiotu zniknie po kliknięciu myszą.
  • Niektóre z renderowania również domyślnie nie są buforowane podwójnie
  • Zauważyłem również, że właściwość ItemState nie zawsze jest poprawna, na przykład tuż po zmianie rozmiaru kolumny. W związku z tym nie mogłem na nim polegać.
  • Musisz również upewnić się, że tekst nie jest podzielony na wiele linii, w przeciwnym razie zobaczysz kilka górnych pikseli dolnej linii wyświetlanych na dole komórki.
  • Należy również zwrócić szczególną uwagę podczas renderowania pierwszej komórki, aby uwzględnić dodatkowe dopełnienie używane w widoku listy rodzimej.
  • Ponieważ zdarzenie DrawItem występuje najpierw, wszystko, co rysujesz w procedurze obsługi DrawItem (na przykład efekt selekcji), może być nałożone na rzeczy, które wykonujesz w procedurze obsługi DrawSubItem (np. Mając określone komórki o innym kolorze tła).

W sumie magazynowe właściciela rysunku jest dość zaangażowany sprawa - Uważam, że najlepiej obsłużyć wszystko rysunek wewnątrz przypadku DrawSubItem, jej również najlepiej wykonywać swoją podwójne buforowanie za pomocą klasy BufferedGraphics.

Znalazłem również bardzo przydatne przy sprawdzaniu kodu źródłowego dla ObjectListView.

Na koniec, wszystko to po to, aby obsłużyć tryb szczegółów widoku listy (jedyny tryb, którego używam), jeśli chcesz, aby inne tryby również działały, wierzę, że są dodatkowe rzeczy do wzięcia pod uwagę .

Kiedy dostanę szansę, postaram się opublikować mój przykładowy kod.

3

ja nie wiem, czy będzie to całkowicie pomóc, ale dodam kilka uwag:

Należy pamiętać, że DrawSubItem również narysuje pierwszy element i prawdopodobnie jest to miejsce, z którego uzyskuje się widok double-rendered.

Niektóre rzeczy spróbować (nie uwzględnione na prędkości):

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) { 
    e.DrawBackground(); 
    if ((e.State & ListViewItemStates.Selected) == ListViewItemStates.Selected) { 
    Rectangle r = new Rectangle(e.Bounds.Left + 4, e.Bounds.Top, TextRenderer.MeasureText(e.Item.Text, e.Item.Font).Width, e.Bounds.Height); 
    e.Graphics.FillRectangle(SystemBrushes.Highlight, r); 
    e.Item.ForeColor = SystemColors.HighlightText; 
    } else { 
    e.Item.ForeColor = SystemColors.WindowText; 
    } 
    e.DrawText(); 
    e.DrawFocusRectangle(); 
} 

Do rutynowych DrawSubItem, upewnij się, że nie czerpią w pierwszej kolumnie i dodałem rutynę DrawBackground(). Dodałem trochę przycinania do prostokąta podświetlania, aby nie malować poza parametrami kolumny.

private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { 
    if (e.ColumnIndex > 0) { 
    e.DrawBackground(); 

    string searchTerm = "Term"; 
    int index = e.SubItem.Text.IndexOf(searchTerm); 

    if (index >= 0) { 
     string sBefore = e.SubItem.Text.Substring(0, index); 

     Size bounds = new Size(e.Bounds.Width, e.Bounds.Height); 
     Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds); 
     Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds); 

     Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height); 

     e.Graphics.SetClip(e.Bounds); 
     e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect); 
     e.Graphics.ResetClip(); 
    } 

    e.DrawText(); 
    } 
} 

Generalnie właściciel rysujący kontrolkę ListView jest przyjazny w świecie cierpienia. Nie rysujesz już w stylach wizualnych, musiałbyś to też zrobić sam. Ugh.

2

Zmieniono kolor wybranej pozycji tylnej. Domyślnie niebieski w oknach. Ten kod pozwoli na u w dowolnych kolorach:

private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     for (int ix = 0; ix < listView1.Items.Count; ++ix) 
     { 
      var item = listView1.Items[ix]; 
      item.BackColor = (ix % 2 == 0) ? Color.Gray : Color.LightGray; 

     } 

    } 

    private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) 
    { 
     e.DrawDefault = true; 
    } 

    } 
} 
0
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) 
    { 
     e.DrawBackground(); 
     if (e.Item.Selected) 
     { 
      e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds); 
     } 
     e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds); 

    }