Modal Popup and views communication under MVVM pattern on C#
Asked Answered
D

1

1

I have a large project coded with VB6 which I've been trying to upgrade to new technologies for a few months. My project consist on 6 administrative modules reunited under a client-server application. Coming from VB, my logical choice was to upgrade to .NET. After a lot of research I decide to use C#, WPF and the MVVM pattern (with Caliburn Micro).

At the beggining I had some problems, but I managed to resolve them. But now I've come to a point where I need (like every complex application) to communicate with different views and their corresponding viewModel through modal popups (or some other technique). And in this matter the MVVM pattern seems to be very restrictive or complex. A simple "Are you sure you want to delete this record (yes/no)" is a very complex task. So I'm looking for advice as how communicate views without complex artifacts as EventAgregators.

So far the only possible alternative I've found is to use the ModalContentPresenter class from this blog. The problems with this solution are:

  • I need to write the father view XAML and modal XAML on the same view.
  • I cannot have multiple popus from same view.

Some examples of where I'd like to use modal popups is:

  • Put a button on a view to select a Client. It should open a popup with all posible clients and let the user chose one.
  • Add a product popup to a customer order.

Any ideas or suggestions? Some sample code would be appreciated? Thanks!

Dent answered 12/9, 2014 at 19:44 Comment(7)
are you familiar with AngularJs I would personally have gone that route..Brunelleschi
No i'm not. What is used for? Maybe you can explain a little more about your comment.Dent
Do a google search on AngularJS with C# it's way too long to explain but it's easy to learn especially if you are familiar with MVCBrunelleschi
I already do that. From what i've read, AngularJs is a JavaScript Framework for HTML. My project is writted on WPF because is a desktop application, not HTML. Thanks for the advice.Dent
I am sure that you can mix and match but I don't do WPF anymore I thought that they deprecated that..LOLBrunelleschi
My initial idea was to use Windows Forms. But i almost get sacrified when i mention it because someone tell me that windows form whas from stone age. They suggest me to use WPF instead and thats the path I toke. I hope it wasn't a bad decision.Dent
You can never had multiple popups from the same view, if they are modal. You need to clarify more what you are trying to do. If it's just a matter of "Enter a number", then I don't usually go to all the bother of doing the MVVM route and binding to a custom class just for a simple edit. I will handle the value from the modal dialog once it is closed.Kenyettakenyon
M
1

I am the author of the linked ModalContentPresenter control so I will attempt to address some of your questions and concerns.

I need to write the father view XAML and modal XAML on the same view.

You can actually write both views in separate files. The views can then be loaded dynamically using DataTemplates which will depend on the ViewModel that is bound to either the Content or ModalContent properties.

See this which describes the general way in which this view switching can be achieved.

You could have a MainViewModel which has two properties, PrimaryViewModel and SecondaryViewModel which return appropriate view models which provide the properties and commands for the main and modal content.

You could have the following setup in XAML:

<DataTemplate DataType="{x:Type FooViewModel}">
    <Controls:FooView />
</DataTemplate>

<DataTemplate DataType="{x:Type BarViewModel}">
    <Controls:BarView />
</DataTemplate>

<controls:ModalContentPresenter 
              Name="modalPresenter"
              Content={Binding DataContext.PrimaryViewModel}
              ModalContent={Binding DataContext.SecondaryViewModel} />

When the IsModalproperty is false, only your PrimaryView will be displayed. As soon as you set the IsModal property to true the ModalContentPresenter will display your SecondaryView.

I cannot have multiple popus from same view.

I take it you mean you want to be able to display different modal content at different times from the same main view.

Using the above technique this is as simple as switching the ViewModel that is bound to the ModalContent property (before displaying it by setting IsModal to true). As long as you have a DataTemplate for the ViewModel that is bound (and your MainViewModel implements INotifyPropertyChanged correctly), the correct content will be displayed.

Some example on where i'd like to use modal popups is:

Put a button on a view to select a Client. It should open a popup with all possible clients and let the user chose one.

Add a product popup to a customer order.

Once you understand the technique described above you should be able to see that the as long as you have a View and ViewModel pair you can cover any scenario you can think of.

As an example, consider viewModels that have the following interfaces:

public interface SelectCustomerViewModel : INotifyPropertyChanged {
    event EventHandler CustomerSelected;        
    public ObservableCollection<Customer> Customers { get; }
    public Customer Customer { get; set; }
    public Command CustomerSelectedCommand { get; }
}

public interface MainViewModel : INotifyPropertyChanged {
    public SelectCustomerViewModel ModalContent { get; }
    public Command SelectCustomerCommand { get; }
    public bool IsSelectingCustomer { get; }
}

You could have XAML that looks something like this:

<Window x:Class="ModalContentTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Select a customer">

    <DataContext>
        <vm:MainViewModel />
    </DataContext>

    <DataTemplate DataType="{x:Type SelectCustomerViewModel}">
        <Controls:SelectCustomerView />
    </DataTemplate>

    <c:ModalContentPresenter Name="modalPresenter"
                             ModalContent={Binding ModalContent}
                             IsModal={Binding IsSelectingCustomer}>

        <!-- This is the primary content! -->
        <Grid>
            <Button Content="Select a customer"
                    Command={Binding SelectCustomerCommand} />
        </Grid>

    </c:ModalContentPresenter>

</Window>

Here's how it works:

  1. The IsSelectingCustomer property of the MainViewModel would start off as false.
  2. Clicking the button in the main view would invoke the SelectCustomerCommand object. The command would then tell the MainViewModel to change the IsSelectingCustomer property to true.
  3. The ModalContentPresenter would display the view specified by the data template. The user can now only interact with the 'select customer view'.
  4. Once a customer has been selected, a button can be clicked (which is bound to the CustomerSelectedCommand of the SelectCustomerViewModel) which in turn would raise the CustomerSelected event.
  5. The MainViewModel would have an event handler that would respond to the CustomerSelected event. The handler would then read the SelectedCustomer property from the SelectCustomerViewModel and finally, it would set the IsSelectingCustomer property back to false, causing the modal content to be closed.
Microsurgery answered 13/9, 2014 at 8:11 Comment(10)
Thank you very much! I will take my time to read your answser and completly understand it, but i have to say that your approach is the best i can fiend to resolve this problem. I'm new to all this technologies so is hard to me (at the moment) to fully undestand your answer but i will make all this test! Thank you again!Dent
In the proccess I will probably ask some more details about the code you post here, if you allow me of course. Thank you again for your timeDent
Glad I could help. If you have any further questions, please ask.Microsurgery
Hi. I'm tryng to understand the first part of your answer, that is, to have Content and Modelcontent in separated files (UserControls), but i cannot make it work. I tried this: Content="{Binding DataContext.OrderViewModel}" but it doesn't work. Do i need to declare OrderViewModel as DataContext source? how? thanks!Dent
@Dent you need to use data templates to tell WPF which user control to load for each viewModel. The link at the top of my post will take you to some articles which explain how this is done.Microsurgery
Hi. I'm re-tryng to solve this problem with yor help. I Manage to do what you suggest. I create a DataTemplate for then ModalContent. But becasuse i'm using two UserControls(or two views) both have different viewmodels. So how do I make modalcontent to change the IsModal Property of parent view? thanks!Dent
That's entirely up to you; the ModalContentPresenter allows you to use standard MVVM best practices to achieve your result.The example scenario above shows you how to achieve what you want using a parent-child viewModel(s) and communicates using an event (see point 5) but it doesn't have to be done this way. You could, for example, communicate via an event aggregator instead.Microsurgery
Thanks for the advice. I was thinking in something more simple like used a shared viewmodel between the two views, but suppose it is not posible. Also child view doesn't seem to link to it's viewmodel propeties, do you have any idea why? Thanks!Dent
I can't think of any reason why it wouldn't be possible to use a shared viewModel. You might need to use a DataTemplateSelector if you want to switch different views that share the same viewModel. If you're still struggling I would suggest you create a new question with some sample code.Microsurgery
Thnaks again. I follow your advice and post a new question. Can you give it a look? #29608383. Thanks!Dent

© 2022 - 2024 — McMap. All rights reserved.