TabControl中的WPF MVVM Light主 - 详细信息,使用消息传递所选项

时间:2012-06-12 18:12:22

标签: .net wpf mvvm mvvm-light message

我是WPF MVVM(Light)的新手,我需要一些帮助。

我所拥有的是主要细节场景,主视图中的TabControl,第一个选项卡中的主视图(ProductsView)以及以下选项卡中的多个不同详细信息视图(例如DetailsView)。

根据ProductsView中选择的项目(SelectedProduct),我想在DetailsView中获取项目,但不是立即获取 - 仅当用户单击包含此详细信息视图的tabitem时。

因此,从用户单击相应的详细信息选项卡(他可能根本不会单击它)时,应该以某种方式从数据库中获取详细数据。

这是我的xaml:

<!-- Main View -->
<UserControl x:Class="MyApp.Views.MainView">
  <TabControl>
    <TabControl.Items>
      <TabItem>
        <views:ProductsView />
      </TabItem>
      <TabItem>
        <views:DetailsView />
      </TabItem>
      <!-- More TabItems with details views -->
    </TabControl.Items>
  </TabControl>
</UserControl>

<!-- Products View -->
<UserControl x:Class="MyApp.Views.ProductsView">
  <Grid DataContext="{Binding Source={StaticResource Locator}, Path=ProductsVM}">
    <DataGrid ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct}">
      <DataGridTextColumn Binding="{Binding Path=Model.ProductID}" Header="Product ID" />
    </DataGrid>
  </Grid>
</UserControl>

<!-- Details View -->
<UserControl x:Class="MyApp.Views.DetailsView">
  <Grid DataContext="{Binding Source={StaticResource Locator}, Path=DetailsVM.Details}">
    <StackPanel>
      <TextBox Text="{Binding Path=Model.Field1}" />
      <TextBox Text="{Binding Path=Model.Field2}" />
    </StackPanel>
  </Grid>
</UserControl>

代码:

public class ProductsViewModel : ViewModelBase
{
    private ObservableCollection<Product> products;
    public ObservableCollection<Product> Products
    {
        get
        {
            return products;
        }
        set
        {   // RaisePropertyChanged
            Set("Products", ref products, value);
        }
    }

    private Product selectedProduct;
    public Product SelectedProduct
    {
        get
        {
            return selectedProduct;
        }
        set
        {   // RaisePropertyChanged and broadcast message of type PropertyChangedMessage<Product>
            Set("SelectedProduct", ref selectedProduct, value, true);
        }
    }

    public ProductsViewModel(IDataService dataService)
    {
        this.dataService = dataService;
        Products = dataService.GetAllProducts();
    }
}

public class DetailsViewModel : ViewModelBase
{
    private Details details;
    public Details Details
    {
        get
        {
            return details;
        }
        set
        {   // RaisePropertyChanged
            Set("Details", ref details, value);
        }
    }

    public DetailsViewModel(IDataService dataService)
    {
        this.dataService = dataService;
        Messenger.Default.Register<PropertyChangedMessage<Product>>(this, m => Details = dataService.GetDetails(m.NewValue.Model.ProductID));
    }
}

现在这样可行,但在选择产品后,所有详细信息选项卡中都会立即获取所有详细信息。 我一直在想,也许当用户点击MainView中的一个标签时,MainViewModel应该向ProductviewModel发送带有标签索引的消息,然后ProductviewModel应该根据标签索引发送另一条消息,将SelectedProduct传递给当前请求的tabitem的DetailsViewModel,更新它的详细数据。

但是我如何仅根据选项卡索引向当前请求的tabitem / DetailsView发送消息,而不是全部?

此次往返听起来太复杂了。你能给我一些建议吗? 或者这完全错了? 也许还有另一种更简单,更优雅的解决方案吗?

3 个答案:

答案 0 :(得分:3)

通常我会ViewModel跟踪TabsSelectedTab,并只会加载SelectedTab PropertyChanged事件中的当前标签< / p>

这样的事情:

// Not expanding these to full properties with property change 
// notifications for sake of simplicity here
ObservableCollection<ViewModelBase> Tabs;
ViewModelBase SelectedTab;

void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SelectedTab")
    {
        if (SelectedTab is IDetailsTab)
            ((IDetailsTab)SelectedTab).LoadProduct(SelectedProduct);

            // Or depending on your structure:
            var productsTab = Tabs[0] as ProductsViewModel;
            ((IDetailsTab)SelectedTab).LoadProduct(productsTab.SelectedProduct);
    }
}

你的XAML看起来像这样:

<TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding SelectedTab}">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type local:ProductsViewModel}">
            <local:ProductsView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:DetailsViewModel}">
            <local:DetailsView />
        </DataTemplate>
    </TabControl.Resources>
</TabControl>

答案 1 :(得分:0)

我认为您正在寻找的是经典的pub / sub模型。当用户单击选项卡控件项时,会引发一个名为RequestViewOfProductDetails的事件。您可以使用以下内容在主视图模型中设置发布:

public MasterViewModel()
{
    Messenger.Default.Send<RequestViewOfProductDetails>(_selectedProduct); 
}

然后,在您的详细信息视图模型中,您将设置视图模型以响应该事件:

Messenger.Default.Register<RequestViewOfProductDetails>(this, delegate(Product prod) 
{ 
  // fetch details
});  

您可以为每种详细信息类型设置一个消息,在每个选项卡项上发布该特定详细信息事件。我知道,单调乏味,但将所有标签分开。现在,如果是我,我会做一些与众不同的事情。我会在后台工作线程上执行请求,因此您可以避免将特定选项卡作为目标。选择产品后,只需调度getter以异步填充所有选项卡,并且事情透明地发生。

答案 2 :(得分:0)

选择详细信息选项卡后,通过此事件刷新Tab上的数据。通过事件参数选择产品。