【问题标题】:WPF communication between multiple ViewModels多个 ViewModel 之间的 WPF 通信
【发布时间】:2018-11-15 21:26:21
【问题描述】:

我目前正在使用 WPF 应用程序并且有几个问题。到目前为止,我一直将所有业务逻辑放在一个 ViewModel 中,现在我注意到 ViewModel 没有特定的上下文。

一个例子是:

        <telerik:RadSplitContainer Orientation="Vertical" telerik:DockingPanel.InitialSize="750,200">
            <telerik:RadPaneGroup IsContentPreserved="False" telerik:ProportionalStackPanel.RelativeSize="200,300" >
                <telerik:RadPane 
                        <ContentControl ContentTemplate="{StaticResource CategoryTemplate}"  
                                    Content="{Binding CategoryViewModel}" />
                </telerik:RadPane>
            </telerik:RadPaneGroup>
            <telerik:RadPaneGroup IsContentPreserved="False" telerik:ProportionalStackPanel.RelativeSize="100,120">
                <telerik:RadPane Header="Items list" 
                                 CanUserClose="False" CanUserPin="False"
                                 CanDockInDocumentHost="True">
                    <ContentControl ContentTemplate="{StaticResource ItemsListTemplate}"  
                                    Content="{Binding ItemsViewModel}" />
                </telerik:RadPane>
            </telerik:RadPaneGroup>
        </telerik:RadSplitContainer>

然而,这就是 mainwindows.xaml 的样子,它是由多个视图构建的,这些视图被指定为一个数据模板。此时我决定从庞大的通用视图模型中提取业务逻辑,并为每个视图创建一个视图模型。

我有两个视图,一个用于类别,一个用于与类别对应的项目。这两个视图包含一个gridview,它显示CategoryView中的类别和与该类别对应的Items。

包含所有项目的视图模型最初是空的,类别只是从数据库中检索所有现有类别。我在 CategoryViewModel 中还有一个 SelectedCategory,其中包含我选择的网格行项目。

我想做的是在 categoryView 的网格中选择一行,我希望 CategoryViewModel 告诉 ItemsViewModel 检索与我从 CategoryViewModel 检索的 categoryID 对应的项目。然后用 INotifyPropertychanged 刷新 itemsViewModel 中的 itemscollection 并刷新网格上的视图。

我的 mainView 视图和视图模型:

    public MainViewModel()
    {
        this.InitializeCommands();
        this.ItemsViewModel = new ItemsViewModel();
        this.CategoryViewModel = new CategoryViewModel();

    }

    public ViewModelBase CategoryViewModel
    {
        get { return this._categoryViewModel; }
        set
        {
            if (this._categoryViewModel != value)
            {
                this._categoryViewModel = value;
            }
        }
    }

    public ViewModelBase ItemsViewModel
    {
        get
        {
            if (this._itemsViewModel == null)
            {
                this._itemsViewModel;
            }
            return this._itemsViewModel;
        }
        set
        {
            if (this._itemsViewModel != value)
            {
                this._itemsViewModel = value;
            }
        }
    }

在 MainView 中使用的另一个视图数据模板的示例:

<DataTemplate x:Key="NewsTemplate">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
                   <telerik:RadBusyIndicator x:Name="BusyIndicator">
            <telerik:RadGridView Name="gridView"
                                                 ItemsSource="{Binding 
         Category}"
                                                 SelectedItem="{Binding SelectedCategory, Mode=TwoWay}">   
                <telerik:RadGridView.Columns>
                    <telerik:GridViewDataColumn DataMemberBinding={Binding Name}" Header="Name"/>     
            </telerik:RadGridView>
        </telerik:RadBusyIndicator>
    </Grid>
</DataTemplate>`

为项目添加了视图模型:

    public ObservableCollection<Item> GetItems
    {
        get
        {
            this._getItems = GetFromDb.GetItems(CategoryID)

            return this._getItems;
        }
        set
        {
            if (this._getItems != value)
            {
                this._getItems = value;
                this.OnPropertyChanged("GetItems");
            }
        }
    }

    public QueryableCollectionView Items
    {
        get
        {
            return this._items = new QueryableCollectionView(GetItems(CategoryID));
        }
        set
        {
            if (this._items != value)
            {
                this._items = value;
                this.OnPropertyChanged(() => Items);
            }
        }
    }

MainViewModel(如果你看一下,我有一个方法 GetFromDb.GetItems(CategoryID) 可以检索项目):

    public MainViewModel()
    {
        this.InitializeCommands();          
        this.CategoryViewModel = new CategoryViewModel();
        this.ItemsViewModel = new ItemsViewModel();

        this.CategoryViewModel.OnChanged += (s, e) => {

            //this one?
            this.ItemsViewModel.ContractMetaDatas(CategoryViewModel.ID)
            //this one?
            this.ItemsViewModel.ContractMetaDatas = new QueryableCollectionView(GetFromDb.GetItems(CategoryID));
        };
    }

我怎样才能做到这一点?

【问题讨论】:

  • 您是否每页使用 1 个视图模型?
  • 嗨@Stefan。是的,我在每个视图/页面或更确切地说是组件使用视图模型。我得到了一个 1000x1000 的网格,我将它分成了几个包含不同视图/视图资源的区域。这是一个单页应用程序,同一页面上有许多视图。
  • 好的;假设您在某种容器中拥有这些视图模型:在 CategoryViewModel 中定义 categoryID 已更改的事件(您可以为此使用 INotifyPropertyChanged 事件,但这并不理想)。在您的容器中捕获它并将其传递给 ItemsViewModel。

标签: c# wpf xaml


【解决方案1】:

我想做的是在 categoryView 的网格中选择一行,我希望 CategoryViewModel 告诉 ItemsViewModel 检索与我从 CategoryViewModel 检索的 categoryID 对应的项目。然后用 INotifyPropertychanged 刷新 itemsViewModel 中的 itemscollection 并刷新网格上的视图。

我怎样才能做到这一点?

嗯,基本上你可以使用带有事件的典型设置:

从您的 CategoryViewModel 触发一个事件并在整个容器中处理它。

 public class MainViewModel: INotifyPropertyChanged
 {
     public CategoryViewModel CategoryViewModel {get;set;}
     public ItemsViewModel ItemsViewModel {get;set;}

     public MainViewModel()
     {   
         this.InitializeCommands();
         this.ItemsViewModel = new ItemsViewModel();
         this.CategoryViewModel = new CategoryViewModel();
         //wire up the event 
         this.CategoryViewModel.OnChanged += (s,e) => {
            //perform update here
            this.ItemsViewModel.UpdateWithId(this.CategoryViewModel.SelectedId);
        };
     }

    ///...
 }

类别:

public class CategoryViewModel : INotifyPropertyChanged
{
    //fire this when appropiate
    public event EventHandler OnChanged;
    ///...
}

【讨论】:

  • 感谢您的回复,我想测试您的方法,但我有几个问题。 //这里执行更新,那里会插入什么逻辑?那么 public YourMainViewModel(){} 呢,这会是构造函数吗?当您在 SelectedCategory 更改时实际更新 ItemsViewModel 获取值/更改时,您能给我一个现实的例子吗?
  • 稍微更新了一下:perform update 表示:在那里更新您的数据。 public YourMainViewModel(){}` 确实是一个构造函数,我把它改成了你的定义。根据您提供的信息,我认为这将是一个现实的例子。我可以了解更多细节,但这需要对您的代码有更多的了解。
  • 谢谢,我会试试这个。你知道你的方法是否适用于异步吗?通过使用私有 CategoryViewModel _categoryViewModel;内容无法识别视图模型,但通过将其设置为 viewmodelbase,它可以工作。我可以只使用视图模型库吗? private CategoryViewModel _categoryViewModel; private ViewModelBase _categoryViewModel; &lt;ContentControl ContentTemplateSelector="{StaticResource CategoryTemplate}" Content="{Binding CategoryViewModel}" /&gt;
  • 是的,你可以这样做;我已经将它们公之于众了……最重要的是事件。定义它并处理它。它不应该影响您的其余代码。
  • 你好,我也更新了线程中的代码。我的 ItemViewModel 如何接收为 MainViewmodel 发送的 ID?
猜你喜欢
  • 1970-01-01
  • 2015-07-11
  • 1970-01-01
  • 2016-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
相关资源
最近更新 更多