2009-04-20 21 views
5

L'immagine sotto mostra come funziona il mio codice. Quando premo il pulsante 2, la listbox viene aggiornata, ma non quando premo il pulsante 1. Perché?La lista delle Winform non si aggiorna quando i dati associati cambiano

pseudo code http://i44.tinypic.com/mj69oj.gif

è il problema filettatura correlato? Se lo è, dove dovrei aggiungere la chiamata a (Begin) Invoke?

Una cosa interessante da notare è che se premo prima il pulsante 1 e poi il pulsante 2, i dati generati dal clic del pulsante1 vengono visualizzati quando faccio clic sul pulsante2. Quindi sembra che i dati generati da doFoo vengano memorizzati in un buffer da qualche parte, e poi spinti alla lista quando premo il pulsante 2.

EDIT:

Ho provato ad aggiungere AddNumber al codice del modulo, e ha aggiunto una chiamata a Invoke quando listBox1.InvokeRequired restituisce true. Questo risolve il problema, ma non è il più bello dei progetti. Non voglio che la GUI debba "preoccuparsi" di come aggiungere elementi a un elenco che fa parte del modello.

Come posso mantenere la logica dietro l'aggiunta alla lista interna alla classe list, mentre sto ancora aggiornando la GUI quando la lista cambia?

EDIT 2:

Ora che abbiamo confermato che si tratta di un problema di threading Ho aggiornato l'immagine in modo da riflettere più da vicino il design del codice vero e proprio su cui sto lavorando.

Mentre il suggerimento di Lucero risolve ancora il problema, speravo in qualcosa che non richiedesse che il modulo fosse a conoscenza della dll o CDllWrapper.

Il modello (ListBoxDataBindingSource ecc) deve sapere nulla circa la vista (listboxes, bottoni, etichette ecc)

risposta

2

La mia ipotesi è che questo è dovuto al messaggio di aggiornamento viene gestito sul thread sbagliato. Sfondo: ogni thread ha la sua coda di messaggi. I messaggi inseriti nella coda dei messaggi verranno inseriti nello stesso thread del chiamante per impostazione predefinita. Pertanto, il callback pubblicherà un messaggio sul thread sbagliato.

Provare questo: spostare il metodo AddNumber() nel modulo e utilizzare Invoke() (ereditato da Control) per aggiungere l'elemento nel thread corretto. Questo potrebbe sbarazzarsi del problema.

Modificare per riflettere il follow-up: L'interfaccia utente non deve conoscere il componente. Quello di cui hai bisogno è solo una corretta sincronizzazione tra l'aggiunta dell'elemento al tuo elenco e l'interfaccia utente, dal momento che gli aggiornamenti dell'interfaccia utente funzioneranno se il thread coincide. Pertanto, è possibile che si desideri fornire il controllo alla classe che include BindingList e quindi eseguire l'Invoke nell'elenco stesso. Ciò rende l'elenco preoccupante di attivare l'aggiornamento sul thread dell'interfaccia utente e si preoccupa sia dell'interfaccia utente sia del componente esterno di richiamare il gestore sul thread corretto.

Ti piace questa:

internal class ListBoxDataBindingSource { 
    private readonly Control uiInvokeControl; 
    private readonly BindingList<Item> list = new BindingList<Item>(); 

    public ListBoxDataBindingSource(Control uiInvokeControl) { 
     if (uiInvokeControl == null) { 
      throw new ArgumentNullException("uiInvokeControl"); 
     } 
     this.uiInvokeControl = uiInvokeControl; 
     CDIIWrapper.setFP(AddNumber); 
    } 

    public void AddNumber(int num) { 
     Item item = new Item(num.ToString()); 
     if (uiInvokeControl.InvokeRequired) { 
      uiInvokeControl.Invoke(list.Add, item); 
     } else { 
      list.Add(item); 
     } 
    } 

    private BindingList<Item> List { 
     get { 
      return list; 
     } 
    } 
} 
+0

Capisco qual è il problema, ora dobbiamo solo trovare una buona soluzione :) Sto provando a progettarlo in base al pattern MVC, quindi lasciare ListBoxDa taBindingSource (parte del Modello) sapere di un controllo (parte della Vista) è generalmente visto come una cattiva pratica. – Tobbe

+0

Beh, non è davvero sapere del controllo, ma ha solo bisogno di sapere hot per effettuare il marshalling della chiamata add al thread corretto. Puoi creare la tua classe di marshalling che conosce il controllo e viene passata all'elenco, in modo che questo aspetto sia nascosto correttamente. – Lucero

+0

(* @ Tobbe: * Forse [questa risposta] (http://stackoverflow.com/questions/3381536/winforms-data-binding-to-business-objects-in-a-multi-threaded-scenario-without-in/3381685 # 3381685) a una delle mie precedenti domande - [Si applicheranno le Winform data-binding agli oggetti business in uno scenario multi-thread senza InvokeRequired?] (Http://stackoverflow.com/q/3381536/240733) - qui.) – stakx

0

invece di avere la setFP impostare il callback a lbDataBindingSource.AddNumber, creare un metodo privato nel codice per gestire il callback e quindi chiamare lbDataBindingSource.AddNumber da quella richiamata.

void MyForm_Load(object sender, EventArgs e) 
{ 
    //... 

    cdll.setFP(FPCallback); 
} 

private void FPCallback(int num) 
{ 
    lbDataBindingSoruce.AddNumber(num); 
} 
1

So che questo è vecchio, anche se ho avuto un problema molto simile.

Ecco la soluzione: BindingList not updating bound ListBox.

+0

Grazie. Ho messo questo progetto sul ghiaccio molto tempo fa, ma quando lo raccoglierò di nuovo vedrò se questo aiuta :) – Tobbe

0

Devo chiamare mio punto di vista del modello di aggiungere le cose al BindingList, quindi ho bisogno di scrivere una funzione anonima

riferimento alla risposta del Lucero e seguente post: Anonymous method in Invoke call

My Code:

listBox.Invoke((Action)delegate 
{ 
    MyViewModel.AddItem(param1, param2); 
}); 
Problemi correlati