0

A newbie question, but I can't get it to work. In trying to use the answer to SO "How Should the View Model Close the form" , I have a UserControl defined with:

<UserControl 
         .....
         h:DialogCloser.DialogResult="{Binding DialogResult}"
         >

which while designing shows:

        Property 'DialogResult' is not attachable to elements of type 'UserControl'.

The DialogCloser is defined as:

         public static class DialogCloser
{
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window != null)
            window.DialogResult = e.NewValue as bool?;
    }
    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}

The UserControl is opened by:

         var win = new WindowDialog();
         win.Title = title;
         win.DataContext = datacontext;

        return win.ShowDialog();

In the View Model for the UserControl, I have:

public new void DoExit()
    {
        DialogResult = true;
    }

    private bool? dialogresult;
    public bool? DialogResult
    {
        get
        {
            return dialogresult;
        }
        set
        {
            if (dialogresult != value)
            {
                dialogresult = value;
                OnPropertyChanged("DialogResult");
            }
        }
    }

When using DoExit(), the modal dialog, my UserControl, does not close. What's wrong? And how do I get the designer (VS 2010) to not throw error?

Thanks is advance for any help.

Addendum:

 public class ModalDialogService : IModalDialogService
{
     public bool? ShowDialog(string title, object datacontext)
    {
        var win = new WindowDialog();

        win.Title = title;
        win.DataContext = datacontext;

        return win.ShowDialog();
    }
}

Note: If the "UserControl" is rather made as a "Window", the designer is happy, but then the error is:

"Window must be the root of the tree. Cannot add Window as a child of Visual."

Help somebody?

Community
  • 1
  • 1
Alan Wayne
  • 5,122
  • 10
  • 52
  • 95
  • Where is this code from?: `var win = new WindowDialog();` – Sheridan Sep 19 '14 at 13:27
  • @Sheridan In another view model (of another usercontrol). – Alan Wayne Sep 19 '14 at 13:28
  • If it has that code in it, then it's not a view model. View models shouldn't know *anything* about `Window`s and views. In this case, as your 'view model' is corrupted anyway, just handle the `Window.Close` normally: `if (win.ShowDialog() == DialogResult.OK) DoSomething();` – Sheridan Sep 19 '14 at 13:32
  • @Sheridan Oops...It is called by a DialogService (which is used by another viewmodal). Please see addendum. Thanks. – Alan Wayne Sep 19 '14 at 13:46

1 Answers1

2

I am new to this, but putting everything together, I would suggest this as a possible answer for newbies like myself.

Does this comply with best MVVM practices?

First, as the designer stated "Property 'DialogResult' is not attachable to elements of type 'UserControl'.

My translation, a "control" is not a window--so it can't be closed. Therefore,

h:DialogCloser.DialogResult="{Binding DialogResult}"

is wrong. Remove it.

Second, my usercontrol is being displayed as the sole element of an otherwise plain window as

 <Window x:Class="Nova5.UI.Views.Ink.WindowDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WindowDialog" 
    WindowStyle="SingleBorderWindow" 
    WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight">

    <ContentPresenter x:Name="DialogPresenter" Content="{Binding .}"/>

 </Window>

Now, the whole thing is initiated when the top most view model does

   var dialog = new WindowDialog
            {
                Title = "It's me Margaret",
                ShowInTaskbar = false,               
                Topmost = true,                      
                ResizeMode = ResizeMode.NoResize    
            };
            dialog.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);

            ModalDialogService dialogService = new ModalDialogService();

            dialogService.ShowDialog<PrescriptionWriterViewModel>(dialog,
                new PrescriptionWriterViewModel(),
                returnedViewModelInstance =>                                
                    {
                            if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
                                {
                                 }
                     }
                );
        };

When Exit is clicked on the usercontrol, it performs by way of a DelegateCommand in its view model

 public new void DoExit()
    {
        OnRequestClose();
    }

    public event EventHandler RequestClose;

    void OnRequestClose()
    {
        EventHandler handler = this.RequestClose;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }

This request to close is then handled in the initiating dialog service,

public void ShowDialog<TDialogViewModel>(IModalWindow view, TDialogViewModel viewModel, Action<TDialogViewModel> onDialogClose)
    {
        view.DataContext = viewModel;

        IDialogViewModel vm = viewModel as IDialogViewModel;
        if (vm !=null)
            vm.RequestClose += (s,e)=> view.Close();        //RequestClose is the event in the ViewModel that the view will listen for.

        if (onDialogClose != null)
        {
            view.Closed += (sender, e) => onDialogClose(viewModel);
        }               
        view.Show();
    }

When the view is closed, the onDialogClose(viewModel), then calls back to the topmost view model and completes the action specified with

  returnedViewModelInstance =>                              
                    {
                            if (dialog.DialogResult.HasValue && dialog.DialogResult.Value)
                                {
                                 }
                     }

So completes several intense hours of learning. I hope it is of use to somebody.

Alan Wayne
  • 5,122
  • 10
  • 52
  • 95