5

I have a window which hosts various UserControl's as pages. Is it possible to close the window which I have no reference to from within the usercontrol's datacontext? Simplified details:

SetupWindow

    public SetupWindow()
    {
        InitializeComponent();
        Switcher.SetupWindow = this;
        Switcher.Switch(new SetupStart());  
    }

    public void Navigate(UserControl nextPage)
    {
        this.Content = nextPage;
    }

SetupStart UserControl

<UserControl x:Class="...">
 <UserControl.DataContext>
    <local:SetupStartViewModel/>
 </UserControl.DataContext>
 <Grid>
    <Button Content="Continue" Command="{Binding ContinueCommand}"/>
 </Grid>
</UserControl>

SetupStartViewModel

    public SetupStartViewModel()
    {
    }

    private bool canContinueCommandExecute() { return true; }

    private void continueCommandExectue()
    {
        Switcher.Switch(new SetupFinish());
    }

    public ICommand ContinueCommand
    {
        get { return new RelayCommand(continueCommandExectue, canContinueCommandExecute); }
    }
user13070
  • 511
  • 1
  • 5
  • 14
  • Potential duplicate of question http://stackoverflow.com/questions/1484233/wpf-mvvm-closing-a-view-from-viewmodel. –  Jun 15 '12 at 10:24

3 Answers3

6

I managed to find a solution from an answer here: How to bind Close command to a button

View-Model:

public ICommand CloseCommand
{
    get { return new RelayCommand<object>((o) => ((Window)o).Close(), (o) => true); }
}

View:

<Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Content="Close"/>
Community
  • 1
  • 1
user13070
  • 511
  • 1
  • 5
  • 14
5

I do this by having a RequestClose event in my ViewModel that it can raise when it wants the view to close.

This is then hooked up to the window's Close() command by the code that creates the window. eg

var window    = new Window();
var viewModel = new MyViewModel();

window.Content = viewModel;
viewModel.RequestClose += window.Close;

window.Show()

This way all the stuff to do with window creation is handled in one place. Neither the view, or the viewmodel know about windows.

GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103
  • my window isnt created programatically and I have no reference to it as i mentioned – user13070 Jun 18 '12 at 10:06
  • Something must be creating the window though. That is was should be controlling window lifetime. Your UserControl should not know about windows, it may not even be hosted in a window. – GazTheDestroyer Jun 18 '12 at 10:20
  • yeah i got that wrong, not sure what i meant. but yes i can add this event but how do i trigger that event from within my view model (which is behind the user control) – user13070 Jun 18 '12 at 10:45
  • Same as raising any other event, just call the event delegate: RequestClose(); – GazTheDestroyer Jun 20 '12 at 12:10
  • that wont work. im trying to explain that i have a window that does this.Content = page; where page is a UserControl. the user control has a viewmodel but there's no reference or access to it from within my window. – user13070 Jun 20 '12 at 13:29
3

Inside your user control you can find a reference to the window that's hosting it with a static method on the Window class.

var targetWindow = Window.GetWindow(this);
targetWindow.Close();

Edit:

If you have no reference to the user control that the data context is being used in you don't have a huge amount of options, if there is just 1 application window you can get away with

Application.Current.MainWindow.Close()

If there are many windows in your application and the one you want to close is in focus you could find that with something like

public Window GetFocusWindow()
{
    Window results = null;
    for (int i = 0; i < Application.Current.Windows.Count; i ++)
        if (Application.Current.Windows[i].IsFocused)
        {
            results = Application.Current.Windows[i];
            break;
        }
    return results;
}

Finally I guess (though this is pretty out there) you could loop through the applications window classes, checking the data context of every object in the visual tree until you find the reference you're after, the window can be closed from there.

Andy
  • 6,366
  • 1
  • 32
  • 37
  • 1
    I usually end up writing my own event in the view model, `RequestClose` or something similar, which the user control listens to. Then it closes the window on the event. Just to avoid passing the user control/window references to the view model. :) – Patryk Ćwiek Jun 15 '12 at 09:35
  • 1
    that's not an answer to my question, I know how to do it in code-behind – user13070 Jun 15 '12 at 09:37
  • @user13070 Then create the event and raise it when you need to close the window. Then close it from code-behind in the method listening to said event. :) Of course that would require you to wire up the events, preferably in the control's constructor. I have no better idea though. – Patryk Ćwiek Jun 15 '12 at 09:40
  • I guess I didn't understand your question properly then, are you saying that the datacontext object needs to be able to close the window but doesn't have a reference to either the window or the user control that it's being used in? – Andy Jun 15 '12 at 09:42