SimpleChildWindows binded to View Model

2019-09-04 17:27发布

问题:

I'm using MVVM Light, MahApps and SimpleChildWindows.

I want to be able to create a CRUD Form in a modal popup.

This CRUD form must be binded to its own ViewModel and called by a command in another ViewModel.

I don't succeed to do this with SimpleChildWindows...

So... Is it possible?

回答1:

How I solved similar problem for myself, taking advantage of Dependency Injection:

I used Unity and registered dependency to 'Func':


public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        IUnityContainer container = new UnityContainer();
        container.RegisterType<EntityCRUDWindowViewModel>();
        container.RegisterType<ConsumerViewModel>();
        container.RegisterInstance<Func<Entity, EntityCRUDWindow>>(entity =>  new EntityCRUDWindow(){DataContext=container.Resolve<EntityCRUDWindowViewModel>(new ParameterOverride("entity", new InjectionParameter<Entity>(entity))));
        /* whatever goes here */
    }
}

ViewModel for the CRUD window looks like


public class EntityCRUDWindowViewModel
{
    private readonly Entity entity;

    public EntityCRUDWindowViewModel(Entity entity)
    {
        this.entity = entity;
    }
}

and you can get the instance of window EntityCRUDWindow and use it in ConsumerViewModel or any other ViewModel by simply declaring in constructor parameter


public class ConsumerViewModel
{
    public ConsumerViewModel(Func<Entity, EntityCRUDWindow> entityCrudWindowFactory)
    {
       this.WhateverCommand = new DelegateCommand(
           () =>
           {
               Entity someEntity = null; //or whatever
               entityCrudWindowFactory(someEntity).ShowDialog();
           });
    }

    public ICommand WhateverCommand { get; }
}

Thus, you can put any dependency you need in constructor parameters of both viewmodels, just keeping in mind that Entity entity parameter must be present in EntityCRUDWindowViewModel.



回答2:

I would probably add DataTemplate to resources and that will render whenever you bind Content to some CrudViewModelType instance



    <Grid>
        <Grid.Resources>
            <DataTemplate DataType="{x:Type whateverNs:MyCrudViewModelType}">
                <UserControl Content="{Binding}"/>
            </DataTemplate>
        </Grid.Resources>
        <ContentControl Content={Binding CurrentCrudViewModel}></ContentControl>
        <simpleChildWindow:ChildWindow IsOpen="{Binding OpenChildWindows}" Content="{Binding MyCrudViewModel}"/>
    </Grid>



回答3:

Ok sometimes the solution is under my nose but I struggle to see it...

So here is my solution:

 <Grid>
    <Grid >
        <UserControl Content="{Binding Path=CurrentViewModel}"/>
    </Grid>
    <simpleChildWindow:ChildWindow IsOpen="{Binding OpenChildWindows}"
                                   Title="{Binding TitleChildWindows}"
                                   Content="{Binding CurrentWindowsViewModel }"/>
</Grid>

The CurrentWindowsViewModel is binded to a UserControl which fits perfectly in the Content property of my SimpleChildWindows.

However I must Bind all the ChildWindows properties to the Viewmodel which "Host" my childwindows ("Title", "IsOpen" etc).

All the ChildWindows ViewModel inherit from a class Template (which obviously inherits from ViewModelBase) with a Title and maybe some others properties specific to ChildWindows.

I communicate through the MVVM light Messenger to tell the "Host" which ViewModel to display by its name and then I read its"Title" and open the Windows by the "IsOpen" propertie:

        private void ReceiveMessage(EnumViewModelNames viewName)
        {
        var selectedViewModel = ViewModelList.Where(x => x.ViewModelName == viewName).SingleOrDefault();
        if (selectedViewModel is TemplateWindowsViewModel)
        {
            TitleChildWindows = (selectedViewModel as TemplateWindowsViewModel).Title;
            OpenChildWindows = true;
            CurrentWindowsViewModel = (selectedViewModel as TemplateWindowsViewModel);
        }
        else if (selectedViewModel != null)
        {
            CurrentViewModel = selectedViewModel;
        }
        Messenger.Default.Unregister<EnumViewModelNames>(this, (action) => ReceiveMessage(action));
        }

This way, I'm able to open the childWindows from the "CurrentViewModel", or the "MainViewModel".

It works pretty well and the view is simply a standard Usercontrol binded to a DataContext in the ViewModelLocator.

Sorry Heorhiy Pavlovych, I've seen that you were trying hard, but we misunderstood...