2008-09-14 14 views
43

Tôi đang làm ví dụ cho người chưa nhận ra rằng các điều khiển như ListBox không phải chứa chuỗi; anh ta đã lưu trữ các chuỗi định dạng và nhảy qua các vòng phân tích cú pháp phức tạp để lấy dữ liệu ra khỏi số ListBox và tôi muốn cho anh ta thấy có một cách tốt hơn.Làm cách nào để tôi làm cho ListBox làm mới văn bản mục của nó?

Tôi nhận thấy rằng nếu tôi có một đối tượng được lưu trữ trong ListBox thì hãy cập nhật giá trị ảnh hưởng đến ToString, số ListBox không tự cập nhật. Tôi đã thử gọi RefreshUpdate trên điều khiển nhưng không hoạt động. Dưới đây là đoạn code của ví dụ tôi đang sử dụng, nó đòi hỏi bạn phải kéo một ListBox và một nút vào biểu mẫu:

Public Class Form1 

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) 
     MyBase.OnLoad(e) 

     For i As Integer = 1 To 3 
      Dim tempInfo As New NumberInfo() 
      tempInfo.Count = i 
      tempInfo.Number = i * 100 
      ListBox1.Items.Add(tempInfo) 
     Next 
    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     For Each objItem As Object In ListBox1.Items 
      Dim info As NumberInfo = DirectCast(objItem, NumberInfo) 
      info.Count += 1 
     Next 
    End Sub 
End Class 

Public Class NumberInfo 

    Public Count As Integer 
    Public Number As Integer 

    Public Overrides Function ToString() As String 
     Return String.Format("{0}, {1}", Count, Number) 
    End Function 
End Class

tôi nghĩ rằng có lẽ vấn đề đã được sử dụng các lĩnh vực và cố gắng thực hiện INotifyPropertyChanged, nhưng điều này không có hiệu lực. (Lý do tôi sử dụng các trường là vì đó là một ví dụ và tôi không cảm thấy muốn thêm vài chục dòng không liên quan gì đến chủ đề mà tôi đang trình bày.)

Thành thật mà nói, tôi chưa bao giờ thử cập nhật các mặt hàng tại chỗ như thế này trước đây; trước đây tôi đã luôn thêm/xóa các mục, chứ không phải chỉnh sửa chúng. Vì vậy, tôi chưa bao giờ nhận thấy rằng tôi không biết làm thế nào để thực hiện công việc này.

Vì vậy, tôi đang thiếu gì?

Trả lời

24

BindingList xử lý việc tự cập nhật các ràng buộc.

using System; 
using System.ComponentModel; 
using System.Windows.Forms; 

namespace TestBindingList 
{ 
    public class Employee 
    { 
     public string Name { get; set; } 
     public int Id { get; set; } 
    } 

    public partial class Form1 : Form 
    { 
     private BindingList<Employee> _employees; 

     private ListBox lstEmployees; 
     private TextBox txtId; 
     private TextBox txtName; 
     private Button btnRemove; 

     public Form1() 
     { 
      InitializeComponent(); 

      FlowLayoutPanel layout = new FlowLayoutPanel(); 
      layout.Dock = DockStyle.Fill; 
      Controls.Add(layout); 

      lstEmployees = new ListBox(); 
      layout.Controls.Add(lstEmployees); 

      txtId = new TextBox(); 
      layout.Controls.Add(txtId); 

      txtName = new TextBox(); 
      layout.Controls.Add(txtName); 

      btnRemove = new Button(); 
      btnRemove.Click += btnRemove_Click; 
      btnRemove.Text = "Remove"; 
      layout.Controls.Add(btnRemove); 

      Load+=new EventHandler(Form1_Load); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      _employees = new BindingList<Employee>(); 
      for (int i = 0; i < 10; i++) 
      { 
       _employees.Add(new Employee() { Id = i, Name = "Employee " + i.ToString() }); 
      } 

      lstEmployees.DisplayMember = "Name"; 
      lstEmployees.DataSource = _employees; 

      txtId.DataBindings.Add("Text", _employees, "Id"); 
      txtName.DataBindings.Add("Text", _employees, "Name"); 
     } 

     private void btnRemove_Click(object sender, EventArgs e) 
     { 
      Employee selectedEmployee = (Employee)lstEmployees.SelectedItem; 
      if (selectedEmployee != null) 
      { 
       _employees.Remove(selectedEmployee); 
      } 
     } 
    } 
} 
+0

Điều này thực sự ít hoạt động hơn so với câu trả lời hiện được chấp nhận. Tuyệt vời! Tôi đã chỉnh sửa bài đăng của bạn để bao gồm một ví dụ. – OwenP

+1

Bạn thực sự có thể cải thiện thêm nữa mà tôi nghĩ. Bạn có thể áp dụng các ràng buộc cha và con để kiểm soát ý nghĩa mà bạn có thể làm mà không có trình xử lý sự kiện _SelectedIndexChanged. Tôi quên mã chính xác mặc dù .....: ( – Quibblesome

+0

Tôi đã cập nhật ví dụ, loại bỏ trình xử lý sự kiện SelectedIndexChanged và thay thế bằng 2 dòng mới trong trình xử lý tải. :) – Quibblesome

9

Sử dụng thuộc tính nguồn dữ liệu và đối tượng BindingSource ở giữa nguồn dữ liệu và thuộc tính nguồn dữ liệu của hộp danh sách. Sau đó làm mới đó.

cập nhật ví dụ được thêm vào.

Giống như vậy:

Public Class Form1 

    Private datasource As New List(Of NumberInfo) 
    Private bindingSource As New BindingSource 

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs) 
     MyBase.OnLoad(e) 

     For i As Integer = 1 To 3 
      Dim tempInfo As New NumberInfo() 
      tempInfo.Count = i 
      tempInfo.Number = i * 100 
      datasource.Add(tempInfo) 
     Next 
     bindingSource.DataSource = datasource 
     ListBox1.DataSource = bindingSource 
    End Sub 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 
     For Each objItem As Object In datasource 
      Dim info As NumberInfo = DirectCast(objItem, NumberInfo) 
      info.Count += 1 
     Next 
     bindingSource.ResetBindings(False) 
    End Sub 
End Class 

Public Class NumberInfo 

    Public Count As Integer 
    Public Number As Integer 

    Public Overrides Function ToString() As String 
     Return String.Format("{0}, {1}", Count, Number) 
    End Function 
End Class 
+0

Tuyệt vời. Đối với một số lý do, dữ liệu ràng buộc trong WinForms không bao giờ nhảy ra khỏi tôi như là một giải pháp không có vấn đề bao nhiêu tôi sử dụng nó trong WPF. – OwenP

+0

Heh nó thậm chí còn thú vị hơn thế này. Một cái gì đó như: ((CurrencyManager) this.BindingContext [ListBox1]). Refresh(); Lấy đối tượng "ẩn" ra khỏi BindingContext và sau đó truyền đối tượng đó đến trình quản lý đơn vị tiền tệ. Mặc dù đây là C# như tôi chưa bao giờ làm điều này trong VB.NET. – Quibblesome

+0

Đây là một câu trả lời hay, nhưng cuối cùng đề xuất của Geno để sử dụng BindingList dẫn đến ít công việc hơn. – OwenP

-1

Tôi không biết nhiều về vb.net nhưng trong C# bạn nên sử dụng nguồn dữ liệu và sau đó liên kết nó bằng cách gọi listbox.bind() sẽ làm các trick.

+2

Thats cho teh internets. Methinks người này quan tâm đến WinForm. – Quibblesome

1

Nếu bạn lấy được từ ListBox, bạn có thể gọi phương thức được bảo vệ bằng RefreshItem. Chỉ cần phơi bày lại phương pháp này theo kiểu của riêng bạn.

public class ListBox2 : ListBox { 
    public void RefreshItem2(int index) { 
     RefreshItem(index); 
    } 
} 

Sau đó, thay đổi tệp thiết kế của bạn để sử dụng loại của riêng bạn (trong trường hợp này là ListBox2).

32

Tôi sử dụng lớp này khi tôi cần có hộp danh sách cập nhật.

Cập nhật đối tượng trong danh sách và sau đó gọi một trong hai phương pháp được bao gồm, tùy thuộc vào việc bạn có chỉ mục có sẵn hay không. Nếu bạn đang cập nhật một đối tượng có trong danh sách, nhưng bạn không có chỉ mục, bạn sẽ phải gọi cho RefreshItems và cập nhật tất cả các mục.

public class RefreshingListBox : ListBox 
{ 
    public new void RefreshItem(int index) 
    { 
     base.RefreshItem(index); 
    } 

    public new void RefreshItems() 
    { 
     base.RefreshItems(); 
    } 
} 
+0

Lưu ý, 'RefreshItem' chỉ hoạt động nếu thuộc tính' DisplayMember' được đặt. –

0

Có chút không chuyên nghiệp nhưng hoạt động. Tôi vừa xóa và thêm mục đó (cũng đã chọn lại). Danh sách được sắp xếp theo thuộc tính "được hiển thị và thay đổi", vì vậy, một lần nữa, là tốt cho tôi. Tác dụng phụ là sự kiện bổ sung (chỉ số thay đổi) được nâng lên.

if (objLstTypes.SelectedItem != null) 
{ 
PublisherTypeDescriptor objType = (PublisherTypeDescriptor)objLstTypes.SelectedItem; 
objLstTypes.Items.Remove(objType); 
objLstTypes.Items.Add(objType); 
objLstTypes.SelectedItem = objType; 
} 
+0

tại sao phiếu giảm giá ?? – nawfal

+0

Điều này sẽ luôn đặt mục đã chọn ở cuối ListBox! –

-3

Nếu objLstTypes là tên ListBox của bạn Sử dụng objLstTypes.Items.Làm tươi(); Hy vọng công trình này ...

+0

Không có phương thức làm mới. –

29
lstBox.Items[lstBox.SelectedIndex] = lstBox.SelectedItem; 
+5

đây là câu trả lời hợp lệ trong thực tế! – nawfal

+3

Người này nhận được phiếu bầu của tôi! Đơn giản, có thể được thực hiện mà không cần thêm bất kỳ lớp hoặc đoạn mã mới nào. Đây là tất cả những gì tôi cần. – Rob

+1

Tuyệt vời - đơn giản và hoàn thành công việc, cảm ơn! –

16
typeof(ListBox).InvokeMember("RefreshItems", 
    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, 
    null, myListBox, new object[] { }); 
+0

Tôi thấy điều này hữu ích: nó có thể được sử dụng để làm cho hộp danh sách làm mới động –

0

Nếu bạn sử dụng một phương pháp bốc thăm như:

private void listBox1_DrawItem(object sender, DrawItemEventArgs e) 
{ 
    e.DrawBackground(); 
    e.DrawFocusRectangle(); 

    Sensor toBeDrawn = (listBox1.Items[e.Index] as Sensor); 
    e.Graphics.FillRectangle(new SolidBrush(toBeDrawn.ItemColor), e.Bounds); 
    e.Graphics.DrawString(toBeDrawn.sensorName, new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold), new SolidBrush(Color.White),e.Bounds); 
} 

Sensor là lớp học của tôi.

Vì vậy, nếu tôi thay đổi lớp Color nơi nào đó, bạn chỉ có thể cập nhật nó như:

int temp = listBoxName.SelectedIndex; 
listBoxName.SelectedIndex = -1; 
listBoxName.SelectedIndex = temp; 

Color sẽ cập nhật, chỉ là một giải pháp :)