Catel:具有一个View的多个ViewModel。可能吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Catel:具有一个View的多个ViewModel。可能吗?相关的知识,希望对你有一定的参考价值。

我为我的用户控件的ViewModel获得了一个通用基类:

public class SuggestModule<TEntity> : ViewModelBase 
        where TEntity : class, ISuggestable, new()
    {
        public SuggestModule(ISomeService someService)
        {
            // Some logic
        }

        // Some private fields, public properties, commands, etc...
    }
}

其中有许多可继承的类。这是其中两个,例如:

public class CitizenshipSuggestViewModel : SuggestModule<Citizenship>
{
    public CitizenshipSuggestViewModel(ISomeService someService) 
        : base(someService) { }       
}

public class PlaceOfBirthSuggestViewModel : SuggestModule<PlaceOfBirth>
{
    public PlaceOfBirthSuggestViewModel(ISomeService someService) 
        : base(someService) { }       
}

这是视图实现:

<catel:UserControl
    x:Class="WPF.PRC.PBF.Views.UserControls.SuggestUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:catel="http://schemas.catelproject.com"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:pbf="clr-namespace:WPF.PRC.PBF">

    <Grid>
        <TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"> />
        <ListBox ItemsSource="{Binding ItemsCollection}" />
        // Other elements, behaviors, other extensive logic...
    </Grid>

</catel:UserControl>

现在,在MainWindow中创建两个ContentControl:

<catel:Window
    x:Class="WPF.PRC.PBF.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:catel="http://schemas.catelproject.com">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ContentControl Grid.Row="0" Content="{Binding CitizenshipSuggestViewModel, Converter={catel:ViewModelToViewConverter}}" />
        <ContentControl Grid.Row="1" Content="{Binding PlaceOfBirthSuggestViewModel, Converter={catel:ViewModelToViewConverter}}" />

    </Grid>

</catel:Window>

由于违反了命名约定,手动解析App.xaml.cs中的ViewModel:

    var viewModelLocator = ServiceLocator.Default.ResolveType<IViewModelLocator>();
    viewModelLocator.Register(typeof(SuggestUserControl), typeof(CitizenshipSuggestViewModel));
    viewModelLocator.Register(typeof(SuggestUserControl), typeof(PlaceOfBirthSuggestViewModel));

    var viewLocator = ServiceLocator.Default.ResolveType<IViewLocator>();
    viewLocator.Register(typeof(CitizenshipSuggestViewModel), typeof(SuggestUserControl));
    viewLocator.Register(typeof(PlaceOfBirthSuggestViewModel), typeof(SuggestUserControl));

但现在我有两个相同的ViewModel视图。如何在不创建相同视图的情况下解决此问题并重复每个视图中的代码?

先感谢您!

答案

你有几个选择:

  1. 重复自己,如果将来需要定制,你可以自定义1而不需要太多开销。缺点是,如果您需要检查通用行为,则需要更改它们。
  2. 创建表示VM表示状态的枚举。在这种情况下,您可以简单地创建一个能够捕获您需要处理的所有案例的vm。您可以使用视图上的依赖项属性并使用ViewToViewModelMapping自动将其映射到vm来解决此问题。这与您希望通过视图实现的代码重用最接近。它确实有点反对“关注点分离”,但由于它代表了相同类型的数据,我认为它仍然是一个好方法。

对于2,您需要执行以下操作:

1用SuggestEntityTypePlaceOfBirth等创建一个枚举Citizenship

2在vm上创建一个属性(此示例代码假设您正在使用Catel.Fody):

public SuggestedEntityType EntityType { get; set; }

3在视图上创建依赖项属性:

[ViewToViewModel(MappingType = ViewToViewModelMappingType.ViewToViewModel)]
public SuggestedEntityType EntityType
{
    get { return (SuggestedEntityType) GetValue(EntityTypeProperty); }
    set { SetValue(EntityTypeProperty, value); }
}

public static readonly DependencyProperty EntityTypeProperty = DependencyProperty.Register("EntityType", typeof (SuggestedEntityType),
    typeof (MyControl), new PropertyMetadata(null));

4您现在可以像这样使用用户控件:

<controls:MyView EntityType="Citizenship" />

有关更多信息,请参阅http://docs.catelproject.com/vnext/catel-mvvm/view-models/mapping-properties-from-view-to-view-model/

另一答案

例如,一种可能性是在UserControl代码隐藏中创建dependencyProperty

   #region Properties       

    public string Test
    {
        get { return (string)GetValue(TestProperty); }
        set { SetValue(TestProperty, value); }
    }

    #endregion Properties

    #region Dependency Properties

    public static readonly System.Windows.DependencyProperty TestProperty =
       System.Windows.DependencyProperty.Register("Test", typeof(string), typeof(YourUserControl), new System.Windows.FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });


    #endregion Dependency Properties

然后在您的xaml中,您可以将此属性绑定为:

<TextBlock Text="{Binding Test, RelativeSource={RelativeSource AncestorType={x:Type catel:UserControl}, Mode=FindAncestor}}">

然后在你的MainWindow中你可以写:

  <views:YourUserControlName Test="{Binding SomeTextPropertyFromMainWindowVM}"/>

因此,您将能够从windowVM中的属性SomeTextPropertyFromMainWindowVM绑定到userControl中的某个属性。

如果你在主窗口中有几个viewModels,你可以这样写:

<views:YourUserControlName Test="{Binding SomeViewModel.SomeTextProperty}"/>
<views:YourUserControlName Test="{Binding SomeOtherViewModel.SomeTextProperty}"/>

以上是关于Catel:具有一个View的多个ViewModel。可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Catel 创建多个 WPF 应用程序

Catel 中的 ViewModel 集合

viewmodel + Catel 上的验证

wpf MVVM Viewmodel之间传值

将 catel 与 3rd 方控件一起使用

DataWindowButton CanExecute 未触发,Catel 4.0