2012-08-12 23 views
9

Cách đơn giản và sạch sẽ nhất để hiển thị một mục hộp danh sách tập trung/được chọn với kiểu Office XP là gì?Cách tạo hộp danh sách với chủ đề Office XP như hình chữ nhật chọn?

Xem hình ảnh mẫu này để hiển thị các ý tưởng hơn rõ ràng hơn:

enter image description here

Tôi nghĩ tôi cần phải thiết lập Style Listbox hoặc là lbOwnerDrawFixed hoặc lbOwnerDrawVariable và sau đó sửa đổi các sự kiện OnDrawItem?

Đây là nơi tôi đang gặp khó khăn, tôi không thực sự chắc chắn những gì mã để viết trong đó, cho đến nay tôi đã cố gắng:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
begin 
    with (Control as TListBox).Canvas do 
    begin 
    if odSelected in State then 
    begin 
     Brush.Color := $00FCDDC0; 
     Pen.Color := $00FF9933; 
     FillRect(Rect); 
    end; 

    TextOut(Rect.Left, Rect.Top, TListBox(Control).Items[Index]); 
    end; 
end; 

tôi nên của biết rằng sẽ không làm việc, tôi nhận được tất cả các loại sôi nổi điều đang xảy ra:

enter image description here

tôi đang làm gì sai, quan trọng hơn là những gì tôi cần phải thay đổi để làm cho nó hoạt động?

Cảm ơn.

+0

Tôi không nghĩ rằng có loại đó của một hộp danh sách theo chủ đề lựa chọn. Có các phần ['LBCP_ITEM'] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb(v = vs.85) .aspx) cho hộp danh sách, nhưng chúng trông giống hệt như hộp danh sách không được chủ sở hữu rút ra - nhàm chán. Vì vậy, có thể bạn có thể mượn các phần lựa chọn theo chủ đề, ví dụ: từ chế độ xem dạng cây như Andrew được mô tả trong ['bài đăng này'] (http://stackoverflow.com/a/10936108/960757). Bạn sẽ chỉ cần sửa đổi mã đó cho các trạng thái hộp danh sách. – TLama

+0

Đây là những gì tôi đã lo lắng về, không giống như Treeview và Listview mà sơn theo chủ đề lựa chọn các Listbox không. –

+3

Khi màu mã hóa cứng, đừng quên rằng người dùng có thể có một bảng phối màu hoàn toàn khác. –

Trả lời

13

Bạn quên vẽ các mục cho các trạng thái khác nhau. Bạn cần xác định trạng thái hiện tại của mục và theo đó vẽ nó.

Những gì bạn có trên ảnh của mình, bạn có thể nhận được theo cách này. Tuy nhiên điều này không có vẻ tốt nếu bạn đã kích hoạt multiselect và chọn nhiều hơn một mục:

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
var 
    Offset: Integer; 
begin 
    with (Control as TListBox) do 
    begin 
    Canvas.Font.Color := Font.Color; 
    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := $00FF9932; 
     Canvas.Brush.Color := $00FDDDC0; 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
    end; 
    Canvas.Rectangle(Rect); 
    Canvas.Brush.Style := bsClear; 
    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

Và kết quả với ItemHeight thiết lập để 16:

enter image description here

Bonus - lựa chọn liên tục :

Đây là một giải pháp phức tạp thực hiện lựa chọn liên tục. Nguyên tắc là để vẽ các mục như trước nhưng sau đó overdraw của đường biên giới hàng đầu và dòng dưới cùng với các dòng của một màu sắc tùy thuộc vào trạng thái lựa chọn của các mục trước đó và tiếp theo. Ngoại trừ điều đó, phải được trả lại bên ngoài mục hiện tại, vì việc chọn mục không tự nhiên gọi các vật phẩm lân cận được sơn lại. Vì vậy, các đường ngang được vẽ một pixel ở trên và một pixel bên dưới giới hạn mục hiện tại (màu của các dòng này cũng phụ thuộc vào trạng thái lựa chọn tương đối).

Khá lạ ở đây là việc sử dụng các đối tượng mục để lưu trữ trạng thái đã chọn của từng mục. Tôi đã làm điều đó, bởi vì khi sử dụng kéo lựa chọn mục thả &, thuộc tính Selected không trả về trạng thái thực cho đến khi bạn nhả nút chuột. May mắn thay, sự kiện OnDrawItem tất nhiên sẽ xảy ra với trạng thái thực, do đó, như một giải pháp thay thế, tôi đã sử dụng lưu trữ các trạng thái này từ sự kiện OnDrawItem.

Chú ý:

Thông báo, mà tôi đang sử dụng các đối tượng mục để lưu trữ trạng thái lựa chọn thực tế, vì vậy hãy cẩn thận, và khi bạn đang sử dụng đối tượng mục cho cái gì khác, lưu trữ thực tế này tiểu bang ví dụ vào một mảng Boolean.

procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer; 
    Rect: TRect; State: TOwnerDrawState); 
const 
    SelBackColor = $00FDDDC0; 
    SelBorderColor = $00FF9932; 
var 
    Offset: Integer; 
    ItemSelected: Boolean; 
begin 
    with (Control as TListBox) do 
    begin 
    Items.Objects[Index] := TObject((odSelected in State));  

    if (odSelected in State) then 
    begin 
     Canvas.Pen.Color := SelBorderColor; 
     Canvas.Brush.Color := SelBackColor; 
     Canvas.Rectangle(Rect); 
    end 
    else 
    begin 
     Canvas.Pen.Color := Color; 
     Canvas.Brush.Color := Color; 
     Canvas.Rectangle(Rect); 
    end; 

    if MultiSelect then 
    begin 
     if (Index > 0) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index - 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Top); 
      Canvas.LineTo(Rect.Right - 1, Rect.Top); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Top - 1); 
     Canvas.LineTo(Rect.Right - 1, Rect.Top - 1); 
     end; 

     if (Index < Items.Count - 1) then 
     begin 
     ItemSelected := Boolean(ListBox1.Items.Objects[Index + 1]); 
     if ItemSelected then 
     begin 
      if (odSelected in State) then 
      begin 
      Canvas.Pen.Color := SelBackColor; 
      Canvas.MoveTo(Rect.Left + 1, Rect.Bottom - 1); 
      Canvas.LineTo(Rect.Right - 1, Rect.Bottom - 1); 
      end 
      else 
      Canvas.Pen.Color := SelBorderColor; 
     end 
     else 
      Canvas.Pen.Color := Color; 
     Canvas.MoveTo(Rect.Left + 1, Rect.Bottom); 
     Canvas.LineTo(Rect.Right - 1, Rect.Bottom); 
     end; 
    end; 

    Offset := (Rect.Bottom - Rect.Top - Canvas.TextHeight(Items[Index])) div 2; 
    Canvas.Brush.Style := bsClear; 
    Canvas.Font.Color := Font.Color; 
    Canvas.TextOut(Rect.Left + Offset + 2, Rect.Top + Offset, Items[Index]); 
    end; 
end; 

Và kết quả:

enter image description here

+2

Vâng, tôi quên mất việc kiểm tra xem tiểu bang đã được chọn chưa, nhưng ngay cả khi tôi còn cách xa một câu trả lời - ít nhất bây giờ tôi có thể thấy những thay đổi tôi nên làm. Ngoài ra tôi quên về việc xem xét nhiều lựa chọn, hiệu quả không phải là xấu mặc dù :) Dù sao câu trả lời tốt như luôn luôn cảm ơn TLama;) –

+2

Bạn đang chào đón! Về việc chọn lựa nhiều hơn, nó phức tạp hơn khi bạn có một đường viền xung quanh mục đó. Trong trường hợp này, bạn cần phải kiểm tra thêm nếu mục trước đó (nếu có) và mục tiếp theo (nếu có) được chọn hay không. Nếu vậy, sau đó bạn cần phải * ẩn * đường viền trên hoặc dưới của mục hiện được hiển thị để có được vùng chọn liên tục ['như thế này'] (http://i.imgur.com/hG7zG.png). – TLama

+1

Giải pháp tuyệt vời, tôi đã cố gắng làm việc nhiều lựa chọn như được hiển thị trong ảnh chụp màn hình thứ hai nhưng tôi không ở đâu gần so với phiên bản cập nhật của bạn. Tôi sẽ chấp nhận câu trả lời của bạn hai lần nếu có thể :) –

2

Bạn cần xem xét giá trị của biến trạng thái được chuyển vào hàm. Điều này cho bạn biết nếu mục được chọn hay không và sau đó bạn có thể đặt bàn chải và bút một cách thích hợp.

Các vấn đề liên quan