WPF / Silverlight binding másik szálból

WPF / Silverlight binding másik szálból
2011-05-27T13:33:10+02:00
2011-05-27T16:18:16+02:00
2022-11-22T00:35:36+01:00
yxcvbnm
Sziasztok!

Silverlight alatt a ViewModel-emben indítok több szálat, és ezek a szálak változtatják azokat a property-ket, amikhez XAML-ből bind-ol a felhasználói felületem.

Olvasgatom a StackOverflow-t, ott ezt írta valaki:

If you bind properties on your viewmodel to GUI elements in your view, the WPF binding mechanism automatically marshals the GUI updates to the GUI thread using the dispatcher.


Én ezt kipróbáltam egy egyszerű példakóddal, de nem működik:

public class MainPageDataContext : INotifyPropertyChanged { private string caption = "clickme"; public string Caption { get { return caption; } set { if (caption != value) { caption = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Caption")); } } } RelayCommand clickCommand; public RelayCommand ClickCommand { get { if (clickCommand == null) clickCommand = new RelayCommand(ClickExecute, CanClickExecute); return clickCommand; } } public void ClickExecute() { Thread thread = new Thread(new ThreadStart(A)); thread.Start(); } private void A() { Caption = "Modified!"; } public bool CanClickExecute() { return true; } public event PropertyChangedEventHandler PropertyChanged; }


A XAML-em így néz ki:

<Button Content="{Binding Path=Caption}" Command="{Binding Path=ClickCommand}" />

Persze ez így exception-t dob, nyavalyog, hogy más szálból piszkálom az UI szál objektumait.

Namost én rontok el valamit (mit???), és igaza van a fickónak aki a fenti idézetet írta;

- vagy pedig nincs igaza, és ne is próbálkozzak azzal, hogy a másik szálból a Silverlight automatikusan átszóljon az UI thread-emnek?
Mutasd a teljes hozzászólást!
Dispatcher-rel nem kötöd a View-t a modelhez...lásd Deployment.Current.Dispatcher...ez a bevett módja...
Vagy SynchronizationContext.Current csak vigyázz, hogy a másik szálban ne hívd ezt: SynchronizationContext.Current, mert vagy null lesz vagy teljesen más context-et kapsz...

Thread vs ThreadPool:
Thread vs ThreadPool
When should I not use the ThreadPool in .Net?
Mutasd a teljes hozzászólást!

  • ClickExecute()-tal indítasz egy másik szálat, amiből módosítod a Bindolt property-t...Szóval a leírás is azt mondja, hogy a GUI update-eket, amik a felületről jönnek automatikusan a dispathcer felszinkronizálja a GUI szálra; tehát a te általad bemutatott kód ennek pont az ellenkezője...Tehát neked is szinronizálni kell a GUI szálra, ezt megteheted többféleképpen, pl:
    private void A() { Deployment.Current.Dispatcher.BeginInvoke( ()=> {Caption = "Modified!";} ) }

    Vagy
    public MainPageDataContext() { _synchronizationContext = SynchronizationContext.Current; } private void A() { _synchronizationContext.Post(delegate { SuccessCallback.Invoke(result); }, null); }

    _synchronizationContext.Post aszinkron hívás, ha Send-et használsz helyette, akkor szinkron hívás lesz...
    Amúgy az új szál létrehozását direktbe nem javasolnám, inkább használj helyett ThreadPool.QueueUserWorkItem()-et, vagy Action-t, aminek a BeginInvoke-ját hívod...
    Mutasd a teljes hozzászólást!
  • Írtad:

    private void A() { Deployment.Current.Dispatcher.BeginInvoke( ()=> {Caption = "Modified!";} ) }

    egy hasonlóval próbálkoztam korábban én is, csak én úgy csináltam, hogy a ViewModel konstruktorában paraméterben átvettem az ablakot reprezentáló osztály Dispatcher-ét, és azt hívogattam. Aztán gondoltam úgy, hogy ez "csúnya", mert elméletileg a ViewModelnek logikailag függetlennek kellene lennie a UI-tól, nem?


    Thread indítását azért nem javaslod, mert lassabb, mint a ThreadPool-ból indítani valamit?
    Mutasd a teljes hozzászólást!
  • Dispatcher-rel nem kötöd a View-t a modelhez...lásd Deployment.Current.Dispatcher...ez a bevett módja...
    Vagy SynchronizationContext.Current csak vigyázz, hogy a másik szálban ne hívd ezt: SynchronizationContext.Current, mert vagy null lesz vagy teljesen más context-et kapsz...

    Thread vs ThreadPool:
    Thread vs ThreadPool
    When should I not use the ThreadPool in .Net?
    Mutasd a teljes hozzászólást!
  • Maradok akkor a Deployment.Current.Dispatcher-nél, ha szerinted is ez a bevett mód. Lehet, hogy úgy fogom megoldani, hogy bevezetek valami ilyesmi metódust:

    public void NotifyPropertyChanged(string property) { if (PropertyChanged != null) Deployment.Current.Dispatcher.BeginInvoke( ()=> { PropertyChanged(this, new PropertyChangedEventArgs(property)); } ); }

    És akkor elég minden property setterébe beírni a NotifyPropertyChanged("propertyneve")-t, s nem kell mindenhová kiírogatni a Deployment.Current.Dispatcher.BeginInvoke -ot...Talán ez tűnik a legletisztultabb megoldásnak...

    Köszönöm a segítséget!
    Mutasd a teljes hozzászólást!
  • Igen ezt a kódot beteheted a base model-edbe és abból származik majd minden viewmodel, Deployment.Current.Dispatcher-t meg elmentheted egy változóba a konstruktorban...
    Mutasd a teljes hozzászólást!
  • Tényleg. Erre nem is gondoltam, de tetszik. Köszi!
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd