4
\$\begingroup\$

Sometimes when developing a WPF application, I need to access a certain Window (View) from the corresponding ViewModel, to perform or fire a method like this for example:

//In the MainWindowViewModel
MainWindowView.Hide(); 

But that's not really easy, and even it violates MVVM because the ViewModel shouldn't know anything about the View, so I started doing this instead:

//In the MainWindowViewModel
Application.Current.MainWindow.Hide();

This becomes impractical when dealing with other Windows (Views):

//In the AnotherWindowView
Application.Current.Windows.OfType<AnotherWindowView>().First().Hide();

So I decided to make a solution, a helper class, in which, using a method, I give the ViewModel and I get back the corresponding View, using the DataContext of the View, the solution looks like this:

The Class:

public static class ViewsAccessibility
{
    public static Window GetCorresponingWindow(ViewModelBase viewModel)
    {
        var windowAccessibility = new WindowAccessibility(viewModel);
        return windowAccessibility.CorrespondanteWindow;
    }

    private class WindowAccessibility 
    {
        public Window CorrespondanteWindow { get; private set; }

        public WindowAccessibility(ViewModelBase viewModel)
        {
            var windows = Application.Current.Windows.OfType<Window>();
            CorrespondanteWindow = (from window in windows
                                      where window.DataContext.Equals(viewModel)
                                      select window).First();
        }
    }
}

The use:

//In the MainWindowViewModel
ViewsAccessibility.GetCorresponingWindow(this).Hide();

Is this a good practice and does it violate MVVM or not?

\$\endgroup\$
3
\$\begingroup\$

You have multiple options

a. Use an event Aggregator where publish the message from the viewmodel and subscribe from view. see prism documentation for event aggregators:here is sample code

 //view will subscribe from this message 
_aggregator.GetEvent<CloseEvent>().Subscribe(CloseAction);

 private void CloseAction(string s) { //do your logic }
 //view model will pubilsh this request 
 _aggregator.GetEvent<CloseEvent>().Publish(output);

b. create an interface named as IView and implement it in your view , pass the instance of IView to data context of the view

public partial class Shell : Window ,IView
{
    public Shell()
    {
        InitializeComponent();
        this.DataContext.View = this;
    }

    public void CloseShell()
    {

    }
}

public interface IView
{
    void CloseShell();
}

by using this you have only a reference of interface will be there in viewmodel . but not the whole view itself

\$\endgroup\$
2
  • \$\begingroup\$ I used the first options already, but thx for reminding, but I didn't get clearly the second option, the DataContext should contain the ViewModel I guess, and what should the IView interface have ? \$\endgroup\$ – AymenDaoudi Jul 28 '14 at 7:48
  • \$\begingroup\$ Okay so basically viewmodel will contain a property which will a reference of IView and IView is basically referring to your view so you can call the method of interface , so no direct reference of view directly.this is one of link "visualstudiomagazine.com/articles/2011/10/01/…" \$\endgroup\$ – Paritosh Jul 28 '14 at 8:14
1
\$\begingroup\$

what I mostly do when i need to fire an event is this:

I create a Boolean property in my viewmodel and bind this property to the view like this: (Put this in the content of your element)

<Element.Style>
 <Style TargetType="Element">
      <Style.Triggers>
         <DataTrigger Binding="{Binding IsHidden}" Value="True">
            <Setter Property="Visibility" Value="Hidden"/>
         </DataTrigger>
         <DataTrigger Binding="{Binding IsHidden}" Value="False">
            <Setter Property="Visibility" Value="Visible"/>
         </DataTrigger>
      </Style.Triggers>
 </Style>
</Element.Style>

If you want it to be animated you can set the datatrigger.enteractions with a beginstoryboard and in this item your animation. Here are some great examples: http://www.wpf-tutorial.com/styles/trigger-datatrigger-event-trigger/

Also don't forget to put RaisePropertyChanged('NameProperty') in the setter of your property else it won't be triggered when the property is changed at runtime.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.