WPFUIVisualizerService.Show sets DataContext to null

Aug 19, 2010 at 11:06 AM

Hi,

I want to show a window with the WPFUIVisualizerService.Show or ShowDialog method and inject the ViewModel via MEFedMVVM.

AccountingDocumentSelectionWindow.xaml:

xmlns:meffed="http:\\www.codeplex.com\MEFedMVVM"
meffed:ViewModelLocator.ViewModel="AccountingDocumentSelectionViewModel"

ViewModelXY.cs:

if (_uiVisualizerService.ShowDialog("AccountingDocumentSelectionWindow", null) == true)
{

When I call ShowDialog the DataContext of the Window is correctly set to the AccountingDocumentSelectionViewModel by MEF. But then its overwritten with null by the CreateWindow method!

Does this mean that I could not use MEFEDMVVM in combination with the WPFUIVisualizerService? Do I have to create the ViewModel by myself (also injecting all depending services to the constructor) and pass it to the ShowDialog method?

Thanks

Aug 19, 2010 at 11:56 AM

deadlock,

As far as I know, the answer is yes. You need to create the ViewModel in your code, passing all the necessary services to the constructor (meaning they should be present in your current ViewModel) and pass it to the Show or ShowDialog method of WPFUIVisualizerService. At least, that's the way, it is done in the demo. You can see it also in one of the Code Project articles: http://www.codeproject.com/KB/WPF/CinchV2_2.aspx#CoreServices.

Damien

Aug 19, 2010 at 12:04 PM
Edited Aug 19, 2010 at 2:53 PM

@anolae

Thanks for your reply.

Aug 19, 2010 at 2:19 PM
Edited Aug 19, 2010 at 2:33 PM

deadlock/anolae

 

It is possibly to use Meffed with the WPFUIVisualizerService so you could do what Analae suggested which is what I do in demo apps.

But there is another way that does not rely on the parent ViewModel having to have all services that child ViewModel may need.

 

All you need to do on your popup ViewModel is change how Meffed deals with it, so you would do this, here is a demo ViewModel

IMPORTANT NOTE : The [ExportViewModel("PopupWindowViewModel", true)] line below, this tell Meffed to resolve the Imports, but is also tells Meffed that this ViewModel will be constructed by someone else, in our case we would New up a PopupWindowViewModel in the MainWindowViewModel and pass it as the State using the WPFUIVisualizerService. Also notice that the PopupViewModel uses a default constructor but specifies [Import] using property ImportAttribute. This is how Meffed will fill the Imports, and the PopupViewModel SHOULD be being created by some parent ViewModel, but Meffed will still work, as it hooks into the DataContextChanged event on the popup that will occur using the WPFUIVisualizerService, when you pass it a PopupViewModel as the State parameter to the Show() / ShowDialog() methods.

Its really cool man

 

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using Cinch;
using MEFedMVVM.Common;
using MEFedMVVM.Services;
using MEFedMVVM.ViewModelLocator;

namespace CinchV2ImportSample.ViewModels
{
    [ExportViewModel("PopupWindowViewModel", true)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class PopupWindowViewModel : ViewModelBase
    {
        #region Services

        [Import]
        public IMessageBoxService _messageBoxService { get; set; }

        #endregion

        #region Private Properties

        private string _greetingName;
        private SimpleCommand<object, object> _showGreetingCommand;

        #endregion

        #region Constructor

        public PopupWindowViewModel(string greetingName)
        {
            _greetingName = greetingName;
            _showGreetingCommand = new SimpleCommand<object, object>(ShowGreetingCommandExecute);
        }

        #endregion

        #region Public Properties

        public SimpleCommand<object, object> ShowGreetingCommand
        {
            get
            {
                return _showGreetingCommand;
            }
        }

        #endregion

        private void ShowGreetingCommandExecute(object parameter)
        {
            _messageBoxService.ShowInformation("Hello " + _greetingName);
        }
    }
}

And here is the XAML for the popup code behind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Cinch;

namespace CinchV2ImportSample.Views
{
    /// <summary>
    /// Interaction logic for PopupWindowView.xaml
    /// </summary>

    
    [PopupNameToViewLookupKeyMetadata("PopupWindow", typeof(PopupWindowView))] //Use this to preregister with the IUIVisualizerService
    public partial class PopupWindowView : Window
    {
        public PopupWindowView()
        {
            InitializeComponent();
        }
    }
}

And here is the popups XAML

<Window x:Class="CinchV2ImportSample.Views.PopupWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:cinch="clr-namespace:Cinch;assembly=Cinch.WPF"
        xmlns:meffed="http:\\www.codeplex.com\MEFedMVVM"
        meffed:ViewModelLocator.ViewModel="PopupWindowViewModel"
        Title="Popup Window"
        Height="200"
        Width="200"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterOwner"
        WindowStyle="ThreeDBorderWindow"
        ShowInTaskbar="False">
    <Grid>
        <Button Command="{Binding ShowGreetingCommand}"
                Padding="5,2,5,2"
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
            Show Greeting
        </Button>
    </Grid>
</Window>

And this is how you would use the VisualiserService in conjunction with the popup.

IMPORTANT : You should NEVER be passing NULL to the VisualiserService, THAT IS NOT HOW IT SHOULD WORK, you will need the altered state from the popup, so you should be passing in a ViewModel from Parent ViewModel. See below

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using Cinch;
using MEFedMVVM.Common;
using MEFedMVVM.Services;
using MEFedMVVM.ViewModelLocator;

namespace CinchV2ImportSample.ViewModels
{
    [ExportViewModel("MainWindowViewModel")]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class MainWindowViewModel : ViewModelBase
    {
        #region Services

        private IUIVisualizerService _uiVisualizerService;
        private IMessageBoxService _messageBoxService;

        #endregion

        #region Private Properties

        private SimpleCommand<object, object> _showPopupCommand;
        private string _greetingName;

        #endregion

        #region Constructor

        [ImportingConstructor]
        public MainWindowViewModel(IUIVisualizerService uiVisualizerService, IMessageBoxService messageBoxService)
        {
            _uiVisualizerService = uiVisualizerService;
            _messageBoxService = messageBoxService;

            _showPopupCommand = new SimpleCommand<object,object>(ShowPopupCommandExecute);
        }

        #endregion

        #region Public Properties

        public SimpleCommand<object, object> ShowPopupCommand
        {
            get
            {
                return _showPopupCommand;
            }
        }

        public String GreetingName
        {
            get
            {
                return _greetingName;
            }
            set
            {
                _greetingName = value;
                NotifyPropertyChanged("GreetingName");
            }
        }

        #endregion

        private void ShowPopupCommandExecute(object parameter)
        {
            if (String.IsNullOrWhiteSpace(GreetingName))
            {
                _messageBoxService.ShowWarning("Please enter a name before showing the popup");
            }
            else
            {
                PopupWindowViewModel popupWindowViewModel = new PopupWindowViewModel(GreetingName.Trim());
                bool? result = _uiVisualizerService.ShowDialog("PopupWindow", popupWindowViewModel);
                if (result.HasValue && result.Value)
                {
                    CloseActivePopUpCommand.Execute(true);
                }
            }
        }
    }
}

And that is how you should be doing it, or like I do in the demo apps, where Parent ViewModel passes in PopupViewModels required services. Probably this approach is better. 
I have a working sample of this I can send you, which I just knocked up to create this reply, if you reply with your email here, I can send it to you.

This is also dicussed at a Cinch V2 article : http://www.codeproject.com/Messages/3533572/Question-about-ViewModel-constructors-with-MEF.aspx  or http://www.codeproject.com/KB/WPF/CinchV2_1.aspx?msg=3533572#xx3533572xx

Aug 19, 2010 at 3:37 PM

hey man I did answer you, see above, But I just have to say that you acting all surprised that WPFUIVisualizerService.Show sets DataContext to null , when it is you that is setting the context to null when you wrote the line

 

if (_uiVisualizerService.ShowDialog("AccountingDocumentSelectionWindow", null) == true)

 

The last parameter is used as DataContext to the popup, so it was you all along. Tee Hee.

Still I answered you I hope

Aug 19, 2010 at 3:38 PM

sacha,

thanks for your detailed explanation, now it makes sense to me.

My email address is maima@gmx.at.

The reason why I did pass a NULL to the VisualizerService is that the ViewModel that shows the popup window does nothing check on the ViewModel of the popup after it was closed.

There is no dependency between them, I do everything in the ViewModel of the popup window.

Aug 19, 2010 at 3:53 PM

sacha wrotes : "The last parameter is used as DataContext to the popup, so it was you all along. Tee Hee."

I know that now. Let me ask my question different. Why I cannot call ShowDialog without setting the DataContext (ViewModelLocator does it for me). That's why I tried with passing a NULL.

Aug 19, 2010 at 4:39 PM
Edited Aug 19, 2010 at 4:42 PM

When you say ViewModelLocator you are talking Meffed right? There is a difference actually, when you specify ExportViewModel("SomePopup", true) MeffedMVVM WILL NOT Create DataContext for the View that has the Meffed attached DP, as it is expecting DataContext to be set by other means. In the case where you have xportViewModel("SomePopup", true) , MeffedMVVM simply listens to the DataCOntextChanged event (which will happen if you use the Cinch VisualizerService, as it sets DataContext on popup), and will just try and satisfy the ViewModels Imports.

Anyway doesnt matter, Cinch uses Meffed for sure, but if you want to make use of Cinch VisualizerService, you need to play by its rules. Which means supplying the Object state paramater. In fact I missed a trick there,I should throw Exception if that is null.

But as you say you don't care about the results, but to work with the Cinch VisualizerService, you still need to provide it as Object state paramater, as more people will want to use results from popup in parent ViewModel I feel.

 

Anyway I think you get it now, I'll send you that demo

Aug 19, 2010 at 4:54 PM

Thanks.

Aug 19, 2010 at 5:17 PM

No problem, I am actually pretty proud of Cinch V2, and think it actually works very well, I have not found anything I can't do with it (yet?), so obviously I like it when people use it, and with that comes questions some times. I don't mind that, as long as the question is valid, and not something

I have clearly already covered in the articles.

 

This one from you was not covered that well in articles, so fair enough for you to ask. So I answered you, hope it helps you.