WPF:如何在视图中创建ViewModel

时间:2014-12-16 20:17:52

标签: wpf xaml mvvm

基本上,我有一个标记问题。我想出了一些解决方案,但我不禁觉得这应该更简单。而不是引导你走上我错综复杂的道路,我想我会分享最简单的实现,并询问你将如何解决它。

MainPage.xaml中

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="6" />
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="6" />
        <ColumnDefinition />
        <!--Additional Columns-->
    </Grid.ColumnDefinitions>
    <!--Row Definitions-->
    <Label Grid.Row="0" Grid.Column="0" Content="Vin:" HorizontalAlignment="Right" />
    <ctrl:CommandTextBox Grid.Row="0" Grid.Column="2" Command="{Binding CreateVehicleCommand}" CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}" />
    <Label Grid.Row="0" Grid.Column="3" Content="Manufacturer:" HorizontalAlignment="Right" />
    <TextBox Grid.Row="0" Grid.Column="5" IsEnabled="False" Text="{Binding Vehicle.Manufacturer, Mode=OneWay}" />
    <!--Additional Read Only Values-->
</Grid>

鉴于上面的示例,在给定创建车辆的命令超出要创建的DataContext(车辆)的约束的情况下,如何将网格内容添加到视图中?

如果您希望查看我的具体尝试,那么问题就在UserControl's DependencyProperty is null when UserControl has a DataContext

2 个答案:

答案 0 :(得分:0)

  

如何在给定的视图中将网格的内容放入视图中   命令创建车辆的约束是在...之外   要创建的DataContext(Vehicle)?

这比MVVM问题更像是一种竞争条件。我将首先解决这个问题,但在之后提出辅助建议。

  1. 没有理由认为ViewModel不能包含另一个viewmodel作为引用,并且该引用必须使用INotifyPropertyChanged mechanisim。
  2. 或者您的xaml(view)页面包含对ViewModel的静态引用,其中页面(视图)不直接在其DataContext中使用,但某个控件无法绑定到包含数据上下文之外的那个静态控制。
  3. 无论哪种方式,都可以通过指向自身获取数据或提供获取数据的备用管道来提供访问权限(,如对您提供的其他帖子的响应中所述)。


    或者您可以展平视图模型以包含更多信息并处理此恕我直言竞争条件,以便不会出现这种情况,并且控件和网格可以正确访问信息格式。

    我无法完全解决问题,因为您更了解现在必须解决的设计目标和危害。

答案 1 :(得分:0)

我想出了一些东西,我对此感到满意。这使我无法创建100个复合ViewModel,虽然它确实引入了一些不必要的复杂性,但它确实大大减少了我需要编写的复制/粘贴代码量。

<强> VMFactoryViewModel.cs

public class CreatedViewModelEventArgs<T> : EventArgs where T : ViewModelBase
{
    public T ViewModel { get; private set; }

    public CreatedViewModelEventArgs(T viewModel)
    {
        ViewModel = viewModel;
    }
}

public class VMFactoryViewModel<T> : ViewModelBase where T : ViewModelBase
{
    private Func<string, T> _createViewModel;
    private RelayCommand<string> _createViewModelCommand;
    private readonly IDialogService _dialogService;

    /// <summary>
    /// Returns a command that creates the view model.
    /// </summary>
    public ICommand CreateViewModelCommand
    {
        get
        {
            if (_createViewModelCommand == null)
                _createViewModelCommand = new RelayCommand<string>(x => CreateViewModel(x));

            return _createViewModelCommand;
        }
    }

    public event EventHandler<CreatedViewModelEventArgs<T>> CreatedViewModel;

    private void OnCreatedViewModel(T viewModel)
    {
        var handler = CreatedViewModel;
        if (handler != null)
            handler(this, new CreatedViewModelEventArgs<T>(viewModel));
    }

    public VMFactoryViewModel(IDialogService dialogService, Func<string, T> createViewModel)
    {
        _dialogService = dialogService;
        _createViewModel = createViewModel;
    }

    private void CreateViewModel(string viewModelId)
    {
        try
        {
            OnCreatedViewModel(_createViewModel(viewModelId));
        }
        catch (Exception ex)
        {
            _dialogService.Show(ex.Message);
        }
    }
}

<强> VMFactoryUserControl.cs

public class VMFactoryUserControl<T> : UserControl where T : ViewModelBase
{
    public static readonly DependencyProperty VMFactoryProperty = DependencyProperty.Register("VMFactory", typeof(VMFactoryViewModel<T>), typeof(VMFactoryUserControl<T>));

    public VMFactoryViewModel<T> VMFactory
    {
        get { return (VMFactoryViewModel<T>)GetValue(VMFactoryProperty); }
        set { SetValue(VMFactoryProperty, value); }
    }
}

<强> GenericView.xaml

<ctrl:VMFactoryUserControl x:Class="GenericProject.View.GenericView"
                           x:TypeArguments="vm:GenericViewModel"
                           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                           xmlns:ctrl="clr-namespace:SomeProject.Controls;assembly=SomeProject.Controls"
                           xmlns:vm="clr-namespace:GenericProject.ViewModel">
    <Grid>
        <!-- Column Definitions -->
        <!-- Row Definitions -->
        <Label Grid.Row="0" Grid.Column="0" Content="Generic Id:" HorizontalAlignment="Right" />
        <ctrl:CommandTextBox Grid.Row="0" Grid.Column="2"
                             Command="{Binding VMFactory.CreateViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                             CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}" />
        <Label Grid.Row="0" Grid.Column="3" Content="Generic Property:" HorizontalAlignment="Right" />
        <TextBox Grid.Row="0" Grid.Column="5" IsEnabled="False" Text="{Binding GenericProperty, Mode=OneWay}" />
        <!--Additional Read Only Values-->
    </Grid>
</ctrl:VMFactoryUserControl>

<强> GenericView.xaml.cs

public partial class GenericView : VMFactoryUserControl<GenericViewModel>
{
    public GenericView()
    {
        InitializeComponent();
    }
}

<强> MainPageViewModel.cs

public class MainPageViewModel : ViewModelBase
{
    private readonly IDialogService _dialogService;
    private GenericViewModel _generic;
    private readonly VMFactoryViewModel<GenericViewModel> _genericFactory;

    public GenericViewModel Generic
    {
        get { return _generic; }
        private set
        {
            if (_generic != value)
            {
                _generic = value;
                base.OnPropertyChanged("Generic");
            } 
        }
    }

    public VMFactoryViewModel<GenericViewModel> GenericFactory
    {
        get { return _genericFactory; }
    }

    private void OnGenericFactoryCreatedViewModel(object sender, CreatedViewModelEventArgs<GenericViewModel> e)
    {
        Generic = e.ViewModel;
    }

    public MainPageViewModel(IDialogService dialogService)
    {
        _dialogService = dialogService;

        _genericFactory = new VMFactoryViewModel<GenericViewModel>(_dialogService, x => new GenericViewModel(_dialogService, GetGeneric(x)));
        _genericFactory.CreatedViewModel += OnGenericFactoryCreatedViewModel;
    }

    private Generic GetGeneric(string genericId)
    {
        // Return some Generic model.
    }
}

<强> MainPage.xaml中

<Page x:Class="GenericProject.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vw="clr-namespace:GenericProject.View">
    <StackPanel>
        <!-- Headers and Additional Content. -->
        <vw:EventView DataContext="{Binding Generic}"
                      VMFactory="{Binding DataContext.GenericFactory, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" />
    </StackPanel>
</Page>