我應該能夠訪問屬於視圖的Dispatcher我需要將它傳遞給ViewModel。但視圖不應該瞭解任何有關ViewModel的內容,那麼你如何通過它?引入一個接口,或者不是將它傳遞給實例,而是創建一個將由View編寫的全局調度器單例?你如何在你的MVVM應用程序和框架中解決這個問題?如何將UI調度程序傳遞給ViewModel
編輯:請注意,因爲我的ViewModels可能創建在後臺線程中,我不能只是在ViewModel的構造函數中做Dispatcher.Current
。
我應該能夠訪問屬於視圖的Dispatcher我需要將它傳遞給ViewModel。但視圖不應該瞭解任何有關ViewModel的內容,那麼你如何通過它?引入一個接口,或者不是將它傳遞給實例,而是創建一個將由View編寫的全局調度器單例?你如何在你的MVVM應用程序和框架中解決這個問題?如何將UI調度程序傳遞給ViewModel
編輯:請注意,因爲我的ViewModels可能創建在後臺線程中,我不能只是在ViewModel的構造函數中做Dispatcher.Current
。
我一直在使用的接口IContext抽象的調度。
我使用MEF(託管擴展框架)將接口注入到ViewModels中。另一種可能性是構造參數。 但是,我更喜歡使用MEF進行注射。
更新(例如從引擎收錄鏈接在評論):
public sealed class WpfContext : IContext
{
private readonly Dispatcher _dispatcher;
public bool IsSynchronized
{
get
{
return this._dispatcher.Thread == Thread.CurrentThread;
}
}
public WpfContext() : this(Dispatcher.CurrentDispatcher)
{
}
public WpfContext(Dispatcher dispatcher)
{
Debug.Assert(dispatcher != null);
this._dispatcher = dispatcher;
}
public void Invoke(Action action)
{
Debug.Assert(action != null);
this._dispatcher.Invoke(action);
}
public void BeginInvoke(Action action)
{
Debug.Assert(action != null);
this._dispatcher.BeginInvoke(action);
}
}
我得到的ViewModel存儲當前的調度成員。
如果ViewModel是由視圖創建的,那麼您知道創建時的當前調度器將是View的調度器。
public interface IContext
{
bool IsSynchronized { get; }
void Invoke(Action action);
void BeginInvoke(Action action);
}
這樣做的好處是可以更輕鬆地單元測試你的ViewModels:
class MyViewModel
{
readonly Dispatcher _dispatcher;
public MyViewModel()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
}
在我的場景中,ViewModels是在線程中創建的。這就是我首先要問的原因。 – bitbonk 2010-03-01 07:57:29
但單元測試存在問題。你如何編寫這種代碼和單元測試你的虛擬機? – 2010-03-01 16:52:55
@Roy:看到這個問題http://stackoverflow.com/questions/1106881/using-the-wpf-dispatcher-in-unit-tests/1303762#1303762 – 2010-03-01 21:54:53
您可能實際上並不需要的調度。如果將viewmodel上的屬性綁定到視圖中的GUI元素,那麼WPF綁定機制會自動使用調度程序將GUI更新整理到GUI線程。
編輯:
此編輯爲響應伊薩克薩沃的評論。
微軟內部的處理結合屬性,你會發現下面的代碼代碼:
if (Dispatcher.Thread == Thread.CurrentThread)
{
PW.OnPropertyChangedAtLevel(level);
}
else
{
// otherwise invoke an operation to do the work on the right context
SetTransferIsPending(true);
Dispatcher.BeginInvoke(
DispatcherPriority.DataBind,
new DispatcherOperationCallback(ScheduleTransferOperation),
new object[]{o, propName});
}
此代碼乘警任何UI更新線程UI線程,這樣即使你更新的屬性取結合的部分從不同的線程中,WPF會自動將調用序列化到UI線程。
但是有很多情況,您可能需要這樣做,想象一下綁定到UI的ObservableCollection,並且您試圖調用_collection .Add()來自工作線程 – 2010-03-01 08:58:13
我知道。當然,通常的考慮因素仍然適用。 – 2010-03-01 09:01:46
目前,我們只需將調度程序添加到ObservableCollection中即可。 – bitbonk 2010-03-01 09:25:02
我的一些WPF項目面臨同樣的情況。在我的MainViewModel(Singleton實例)中,我得到了我的CreateInstance()靜態方法需要調度器。然後創建實例從視圖中調用,以便我可以從那裏傳遞分派器。 而ViewModel測試模塊調用CreateInstance()無參數。
但是在複雜的多線程場景中,在View端有一個接口實現總是好的,以便獲得當前窗口的正確Dispatcher。
如果你只需要在另一個線程修改綁定集合調度看看這裏的SynchronizationContextCollection http://kentb.blogspot.com/2008/01/cross-thread-collection-binding-in-wpf.html
效果很好,使用與ASP SynchronizationContextCollection性能視圖模型只時,我發現問題。 NET同步上下文,但很容易解決。
HTH 山姆
哇,有趣,像這樣的東西應該是WPF的一部分 – bitbonk 2010-10-04 09:25:15
喜也許我太晚了,因爲它已經因爲你的第一篇文章已有8個月〜 我在silverlight mvvm應用程序中遇到同樣的問題。我發現我的解決方案是這樣的。對於我有的每個模型和視圖模型,我也有一個叫做控制器的類。 像
public class MainView : UserControl // (because it is a silverlight user controll)
public class MainViewModel
public class MainController
我MainController分管指揮和模型視圖模型和之間的連接。在構造函數中,實例化視圖及其視圖模型,並將視圖的datacontext設置爲其視圖模型。
mMainView = new MainView();
mMainViewModel = new MainViewModel();
mMainView.DataContext = mMainViewModel;
//(在我的命名約定我有成員變量的前綴米)
我也有我的MainView的類型的公共屬性。這樣
public MainView View { get { return mMainView; } }
(這mMainView是對公共財產的局部變量)
,現在我做。我只需要使用我的調度員對我的UI therad這樣的...
mMainView.Dispatcher.BeginInvoke(
() => MessageBox.Show(mSpWeb.CurrentUser.LoginName));
(在這個例子中我是問我的控制器,讓我的SharePoint 2010的登錄名,但你可以做什麼你的需要)
我們幾乎做了你還需要定義你的根在App.xaml中視這樣
var mainController = new MainController();
RootVisual = mainController.View;
幫我通過我的應用程序。也許它可以幫助你太...
另一種常見的模式(也就是現在看到的框架多大用處)爲SynchronizationContext。
它使您可以同步和異步分派。您也可以在當前線程上設置當前的SynchronizationContext,這意味着它很容易被嘲弄。 DispatcherSynchronizationContext由WPF應用程序使用。 WCF和WF4使用SynchronizationContext的其他實現。
這是我認爲最好也是最簡單的方法。您可以將ViewModel的SynchronizationContext默認爲創建ViewModel的線程當前上下文,並且如果UserControl需要更改它,則可以隨意更改它。 – ken 2012-04-16 16:49:18
我已經找到另一個(最簡單的)方式:
添加到瀏覽模式的行動,就是要在調度員可撥打:
public class MyViewModel
{
public Action<Action> CallWithDispatcher;
public void SomeMultithreadMethod()
{
if(CallWithDispatcher != null)
CallWithDispatcher(() => DoSomethingMetod(SomeParameters));
}
}
,並考慮構造函數中添加此動作處理程序:
public View()
{
var model = new MyViewModel();
DataContext = model;
InitializeComponent();
// Here
model.CallWithDispatcher += act => _taskbarIcon.Dispatcher
.BeginInvoke(DispatcherPriority.Normal, act) ;
}
現在你沒有測試問題,而且很容易實現。 我已將它添加到我的site
您不需要將UI調度程序傳遞給ViewModel。 UI調度程序可從當前應用程序單例中獲得。
App.Current.MainWindow.Dispatcher
這將使您的ViewModel依賴於視圖。根據您的應用程序,可能會也可能不會。
MainWindow可能爲null。 – Cologler 2015-06-02 01:42:54
你爲什麼不使用
System.Windows.Application.Current.Dispatcher.Invoke(
(Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); }));
,而不是保持參照GUI調度。
WPF和Windows應用商店的應用程序使用: -
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => {ObservableCollectionMemeberOfVM.Add("xx"); }));
保存參考GUI調度員是不是真的正確的方式。
如果不工作(例如在窗口電話8個應用的情況下),然後使用: -
Deployment.Current.Dispatcher
作爲MVVM燈5.2的,庫現在包括一個DispatcherHelper
類GalaSoft.MvvmLight.Threading
命名空間暴露一個函數CheckBeginInvokeOnUI()
,它接受一個委託並在UI線程上運行它。如果您的ViewModel正在運行一些影響您的UI元素所綁定的VM屬性的工作線程,則非常方便。
DispatcherHelper
必須在應用程序生命週期的早期階段通過調用DispatcherHelper.Initialize()
進行初始化(例如App_Startup
)。然後,您可以使用下面的調用運行任何委託(或lambda):
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
//Your code here
});
注意類是當你通過的NuGet添加它默認不引用GalaSoft.MvvmLight.Platform
庫中定義。您必須手動添加對此庫的引用。
從WPF版本4開始。5可以使用CurrentDispatcher
Dispatcher.CurrentDispatcher.Invoke(() =>
{
// Do GUI related operations here
}, DispatcherPriority.Normal);
這個單例在多線程場景中不能很好地工作。 – bitbonk 2016-05-17 15:18:58
也許我有點晚了這次討論,但我發現1好文章https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
有1款
此外,查看以外的所有代碼圖層(即ViewModel 和模型圖層,服務等)不應取決於綁定到特定UI平臺的任何類型 。任何直接使用Dispatcher (WPF/Xamarin/Windows Phone/Silverlight),CoreDispatcher(Windows 存儲)或ISynchronizeInvoke(Windows窗體)是一個壞主意。 (SynchronizationContext略微好一點,但幾乎沒有。)對於 示例,Internet上有很多代碼用於執行一些異步工作,然後使用Dispatcher更新UI;更多的 便攜式和不太麻煩的解決方案是使用等待異步 工作和更新UI而不使用分派器。
假設您可以正確使用異步/等待,這不是問題。
你能否提供一個wpf UserControl實現的例子? – Femaref 2010-06-10 09:37:38
是的,可以提供有關實施的任何示例?謝謝。 – K2so 2010-11-01 13:46:35
比從未更好地遲到;-)嘗試以下方法:http://pastebin.com/eXTQf9vm – Matthias 2011-07-10 10:03:57