[英]Sharing data between different ViewModels
我正在嘗試開發一個簡單的 MVVM 項目,它有兩個窗口:
第一個窗口是一個文本編輯器,我在其中綁定了一些屬性,例如FontSize
或BackgroundColor
:
<TextBlock FontSize="{Binding EditorFontSize}"></TextBlock>
它的DataContext
是MainWindowViewModel :
public class MainWindowViewModel : BindableBase
{
public int EditorFontSize
{
get { return _editorFontSize; }
set { SetProperty(ref _editorFontSize, value); }
}
.....
<Slider Maximum="30" Minimum="10" Value="{Binding EditorFontSize }" ></Slider>
它的DataContext
是OptionViewModel :
public class OptionViewModel: BindableBase
{
public int EditorFontSize
{
get { return _editorFontSize; }
set { SetProperty(ref _editorFontSize, value); }
}
.....
我的問題是我必須在選項窗口中獲取滑塊的值,然后我必須用這個值修改我的 TextBlock 的 FontSize 屬性。 但我不知道如何將字體大小從 OptionViewModel 發送到 MainViewModel 。
我認為我應該使用:
我希望你能幫助我。 這是我的第一個 MVVM 項目,英語不是我的主要語言 :S
謝謝
另一種選擇是將此類“共享”變量存儲在某種SessionContext
類中:
public interface ISessionContext: INotifyPropertyChanged
{
int EditorFontSize { get;set; }
}
然后,將其注入您的視圖模型(您正在使用依賴注入,對嗎?)並注冊到PropertyChanged
事件:
public class MainWindowViewModel
{
public MainWindowViewModel(ISessionContext sessionContext)
{
sessionContext.PropertyChanged += OnSessionContextPropertyChanged;
}
private void OnSessionContextPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "EditorFontSize")
{
this.EditorFontSize = sessionContext.EditorFontSize;
}
}
}
有很多方法可以在視圖模型和很多點之間進行交流,什么點是最好的。 你可以看到它是如何完成的:
在我看來,最好的方法是使用Prism
框架的EventAggregator
模式。 Prism 簡化了 MVVM 模式。 但是,如果您還沒有使用Prism
,您可以使用Rachel Lim 的教程 - Rachel Lim 的 EventAggregator 模式的簡化版本。 . 我強烈推薦你 Rachel Lim 的方法。
如果您使用 Rachel Lim 的教程,那么您應該創建一個通用類:
public static class EventSystem
{...Here Publish and Subscribe methods to event...}
並將事件發布到您的OptionViewModel
:
eventAggregator.GetEvent<ChangeStockEvent>().Publish(
new TickerSymbolSelectedMessage{ StockSymbol = “STOCK0” });
然后你在另一個MainViewModel
構造函數中訂閱一個事件:
eventAggregator.GetEvent<ChangeStockEvent>().Subscribe(ShowNews);
public void ShowNews(TickerSymbolSelectedMessage msg)
{
// Handle Event
}
Rachel Lim 的簡化方法是我見過的最好的方法。 但是,如果您想創建一個大型應用程序,那么您應該閱讀Magnus Montin和CSharpcorner 的這篇文章, 並附有示例。
更新:對於 5 之后的Prism
版本, CompositePresentationEvent
在版本 6 中已棄用並完全刪除,因此您需要將其更改為PubSubEvent
其他一切都可以保持不變。
我用 WPF 做了一個很大的 MVVM 應用程序。 我有很多窗戶,我遇到了同樣的問題。 我的解決方案可能不是很優雅,但效果很好。
第一個解決方案:我做了一個獨特的 ViewModel,使用部分類將它拆分到各種文件中。
所有這些文件都以:
namespace MyVMNameSpace
{
public partial class MainWindowViewModel : DevExpress.Mvvm.ViewModelBase
{
...
}
}
我正在使用 DevExpress,但是,查看您的代碼,您必須嘗試:
namespace MyVMNameSpace
{
public partial class MainWindowViewModel : BindableBase
{
...
}
}
第二種解決方案:無論如何,我還有幾個不同的 ViewModel 來管理其中一些窗口。 在這種情況下,如果我有一些變量要從一個 ViewModel 讀取到另一個,我將這些變量設置為static 。
例子:
public static event EventHandler ListCOMChanged;
private static List<string> p_ListCOM;
public static List<string> ListCOM
{
get { return p_ListCOM; }
set
{
p_ListCOM = value;
if (ListCOMChanged != null)
ListCOMChanged(null, EventArgs.Empty);
}
}
也許第二個解決方案更簡單,仍然可以滿足您的需要。
我希望這很清楚。 如果你願意,問我更多細節。
我自己不是 MVVM 專業人士,但我在處理此類問題時遇到的問題是,擁有一個將所有其他視圖模型作為屬性的主類,並將此類設置為所有窗口的數據上下文,我不這樣做不知道它是好是壞,但對於您的情況,這似乎就足夠了。
有關更復雜的解決方案,請參閱此
對於更簡單的,
你可以做這樣的事情,
public class MainViewModel : BindableBase
{
FirstViewModel firstViewModel;
public FirstViewModel FirstViewModel
{
get
{
return firstViewModel;
}
set
{
firstViewModel = value;
}
}
public SecondViewModel SecondViewModel
{
get
{
return secondViewModel;
}
set
{
secondViewModel = value;
}
}
SecondViewModel secondViewModel;
public MainViewModel()
{
firstViewModel = new FirstViewModel();
secondViewModel = new SecondViewModel();
}
}
現在您必須為傳遞視圖模型的OptionWindow創建另一個構造函數。
public SecondWindow(BindableBase viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
這是為了確保兩個窗口都在視圖模型的同一個實例上工作。
現在,無論你在哪里打開第二個窗口,都使用這兩行
var window = new SecondWindow((ViewModelBase)this.DataContext);
window.Show();
現在您將第一個窗口的視圖模型傳遞給第二個窗口,以便它們在 MainViewModel的同一實例上工作。
一切都完成了,只是你必須解決綁定為
<TextBlock FontSize="{Binding FirstViewModel.EditorFontSize}"></TextBlock>
<TextBlock FontSize="{Binding SecondViewModel.EditorFontSize}"></TextBlock>
而且不用說First window的data context是MainViewModel
在 MVVM 中,模型是共享數據存儲。 我會在實現INotifyPropertyChanged
的OptionsModel
保留字體大小。 任何對字體大小感興趣的視圖模型都訂閱了PropertyChanged
。
class OptionsModel : BindableBase
{
public int FontSize {get; set;} // Assuming that BindableBase makes this setter invokes NotifyPropertyChanged
}
在 FontSize 改變時需要更新的 ViewModel 中:
internal void Initialize(OptionsModel model)
{
this.model = model;
model.PropertyChanged += ModelPropertyChanged;
// Initialize properties with data from the model
}
private void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(OptionsModel.FontSize))
{
// Update properties with data from the model
}
}
我是 WPF 的新手,我想出了一個解決方案,我很好奇更多知識淵博的人對它的對與錯的想法。
我有一個考試選項卡和一個模板選項卡。 在我的簡單概念證明中,我希望每個選項卡都“擁有”一個Exam
對象,並且能夠訪問另一個選項卡的Exam
。
我將每個選項卡的 ViewModel 定義為static
因為如果它是一個普通的實例屬性,我不知道一個選項卡將如何獲得另一個選項卡的實際實例。 對我來說感覺不對,雖然它有效。
namespace Gui.Tabs.ExamsTab {
public class GuiExam: INotifyPropertyChanged {
private string _name = "Default exam name";
public string Name {
get => _name;
set {
_name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName="") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class ExamsHome : Page {
public ExamsHome() {
InitializeComponent();
DataContext = ViewModel;
}
public static readonly ExamsTabViewModel ViewModel = new ExamsTabViewModel();
}
public class ExamsTabViewModel {
public GuiExam ExamsTabExam { get; set; } = new GuiExam() { Name = "Exam from Exams Tab" };
public GuiExam FromTemplatesTab { get => TemplatesHome.ViewModel.TemplatesTabExam; }
}
}
namespace Gui.Tabs.TemplatesTab {
public partial class TemplatesHome : Page {
public TemplatesHome() {
InitializeComponent();
DataContext = ViewModel;
}
public static readonly TemplatesTabViewModel ViewModel = new TemplatesTabViewModel();
}
public class TemplatesTabViewModel {
public GuiExam TemplatesTabExam { get; set; } = new GuiExam() { Name = "Exam from Templates Tab" };
public GuiExam FromExamTab { get => ExamsHome.ViewModel.ExamsTabExam; }
}
}
然后所有內容都可以在 xaml 中訪問:
TemplatesHome.xaml
(摘錄)<StackPanel Grid.Row="0">
<Label Content="From Exams Tab:"/>
<Label FontWeight="Bold" Content="{Binding FromExamTab.Name}"/>
</StackPanel>
<StackPanel Grid.Row="1">
<Label Content="Local Content:"/>
<TextBox Text="{Binding TemplatesTabExam.Name, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" Width="200" FontSize="16"/>
</StackPanel>
ExamsHome.xaml
(摘錄)<StackPanel Grid.Row="0">
<Label Content="Local Content:"/>
<TextBox Text="{Binding ExamsTabExam.Name, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Center" Width="200" FontSize="16"/>
</StackPanel>
<StackPanel Grid.Row="1">
<Label Content="From Templates Tab:"/>
<Label FontWeight="Bold" Content="{Binding FromTemplatesTab.Name}"/>
</StackPanel>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.