4

I'm using a Messenger class in order to send data between view models. There is an AppView that hosts two main views in a content control, and up until now have had no issue with sending/receiving data this way.

Issue:

Now I added a ProductView that shows a separate dialog to the AppView. But when I call Messenger.Default.Send<ProductModel>(SelectedProduct); after calling .ShowDetailDialog() this blocks the Send code call, until the dialog is closed.

I tried the other way around, calling the Send code first, then opening the dialog. But this means that the message handler in the receiving VM doesn't register in time before the message is sent.

Does anyone know of a solution, to prevent the dialog from blocking the send call? Or alternatively register the ProductVM message handler prior to sending message and showing dialog?

Below is a summary of the related classes:

CustomerOrdersVM (sending code):

    private void EditOrder(object obj)
    {
        _dialogService.ShowDetailDialog();    
        Messenger.Default.Send<ProductModel>(SelectedProduct);            
    }

ProductVM (receiving code):

    public ProductViewModel()
    {
        Messenger.Default.Register<ProductModel>(this, OnSelectedProductReceived);              
    }

DialogService:

class DialogService : IDialogService
{

    Window productView = null;

    public DialogService()
    {

    }


    public void ShowDetailDialog()
    {
         productView = new ProductView();
        productView.ShowDialog();
    }
}

AppVM (Main VM's are registered, ProductVM is independent of this VM):

    public ApplicationViewModel()
    {
        // Add available pages
        PageViewModels.Add(new CustomerDetailsViewModel(customerDataService, countryDataService, dialogService));
        PageViewModels.Add(new CustomerOrdersViewModel(orderDataService, dialogService));
        PageViewModels.Add(new OrderStatisticsViewModel());

        // Set starting page
        CurrentPageViewModel = PageViewModels[0];  
    }

AppView: (holds the AppVM views):

<Window x:Class="MongoDBApp.Views.ApplicationView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:views="clr-namespace:MongoDBApp.Views"
        xmlns:vm="clr-namespace:MongoDBApp.ViewModels">


    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:CustomerDetailsViewModel}">
            <views:CustomerDetailsView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:CustomerOrdersViewModel}">
            <views:CustomerOrdersView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:OrderStatisticsViewModel}">
            <views:OrderStatisticsView />
        </DataTemplate>
    </Window.Resources>

    <Window.DataContext>
        <vm:ApplicationViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height=".07*" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>


        <TabControl Grid.Row="1"
                    ItemsSource="{Binding PageViewModels}"
                    SelectedItem="{Binding CurrentPageViewModel}"
                    TabStripPlacement="Top">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ItemContainerStyle>
                <Style TargetType="{x:Type TabItem}">
                    <Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>
    </Grid>
</Window>
Brian Var
  • 6,029
  • 25
  • 114
  • 212

1 Answers1

2

You could solve the problem a few ways:

  1. Don't use ShowDialog(). Use Show(), and make the dialog window TopMost and parented to the main window.
  2. Register the ProductView in the constructor of the DialogService (do you really need a new ProductView each time anyway?)

  3. Make the DialogService (or a utility class inside of it) register at construction time for the message, and then pass the message on to any displayed ProductViews

Personally I like #2- since you are using ShowDialog, it implies that only one ProductView is ever needed at a time. For example:

class DialogService : IDialogService
{
    Window productView = null;
    ProductView _productView;

    public DialogService()
    {
         _productView = new ProductView();
    }

    public void ShowDetailDialog()
    {
        _productView.ShowDialog();
    }
}
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • Okay in #2, by register ProductView, do you mean pass it in the ctor like this: Public DialogService(ProductView view){ this._view = view } Then calling Also could you provide a snippet on #1 solution , making the dialog parented to appView? – Brian Var Dec 18 '15 at 01:58
  • I've given an example of #2 in an edit. For #1, see https://msdn.microsoft.com/en-us/library/system.windows.window.topmost(v=vs.110).aspx and http://stackoverflow.com/questions/27462682/wpf-set-parent-window – Chris Shain Dec 18 '15 at 02:11
  • I implemented #2, registering the ProductView in the DialogService(). So.. _productView.ShowDialog(); didn't work, but for some reason _productView.Show(); did work. Any ideas why that is? – Brian Var Dec 18 '15 at 12:31
  • Because you send the message after you show the product view. Try reversing those calls in addition to implementing #2, and then ShowDialog should work. Remember that ShowDialog blocks, while Show doesn't. – Chris Shain Dec 18 '15 at 12:35
  • ok, so what is the difference in using Show(), or ShowDialog() is this case? out of interest. – Brian Var Dec 18 '15 at 12:38
  • ShowDialog blocks the caller until the dialog is closed. Show doesn't. – Chris Shain Dec 18 '15 at 12:40
  • I tried implementing .Close() from the dialog service, after I click save in the ProductView. When I step through, the Close() is stepped into, and close is called, but my ProductView doesn't close. Any ideas why that could be? I called close before the message is sent back, so that the handler is registered before the message is sent: https://gist.github.com/BrianJVarley/17a7e5369a942b8fa2b3 – Brian Var Dec 18 '15 at 14:20