Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM way to close document with possibility to cancel out

I'm using Avalondock 2.x for one of my open source projects, if a document is dirty when you close it you should be able to cancel the close.

I am using Caliburn Micro and Coroutine, only way I have been able to solve it is to use C.M to attach to the event

<i:EventTrigger EventName="DocumentClosing">
    <cal:ActionMessage MethodName="DocumentClosing">
        <cal:Parameter Value="$documentcontext" />
        <cal:Parameter Value="$eventArgs" />
    </cal:ActionMessage>
</i:EventTrigger>

The event arg has a cancel property. Problem with this approuch is thats its not very MVVM friendly, I have created a little helper method to Coroutinify this like

public IEnumerable<IResult> Coroutinify(IEnumerable<IResult> results, System.Action cancelCallback)
{
    return results.Select(r =>
        {
            if (r is CancelResult)
                cancelCallback();

            return r;
        });
}

Used like

public IEnumerable<IResult> DocumentClosing(ScriptEditorViewModel document, DocumentClosingEventArgs e)
{
    return Result.Coroutinify(HandleScriptClosing(document), () => e.Cancel = true);
}

This works but it's a bit clumsy etc, is there a more MVVM way of closing documents in Avalondock with cancel ability?

edit: source code

https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.GUI/Shells/MainShellView.xaml#L29

https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.GUI/Shells/MainShellViewModel.cs#L110

https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.GUI/Result/ResultFactory.cs#L49

like image 556
Anders Avatar asked Oct 03 '22 18:10

Anders


1 Answers

The way I've accomplished this is by binding to the CloseCommand property of an AvalonDock LayoutItem. When this binding is associated, it overrides the default behavior of closing a document ('X' button, right click close / close all). You then are fully responsible for removing (closing) the document if desired.

The way I set it up was to have a DocumentManagerVM which contains an ObservableCollection of DocumentVMs. Each DocumentVM has an ICommand called RequestCloseCommand, which can close the document by removing itself from the collection of DocumentVMs it's owning DocumentManagerVM.

Specifically, in my DocumentVM viewmodel, there's an ICommand (I'm using mvvmLight RelayCommand) to perform the closing logic:

public RelayCommand RequestCloseCommand { get; private set; }
void RequestClose()
{
    // if you want to prevent the document closing, just return from this function
    // otherwise, close it by removing it from the collection of DocumentVMs
    this.DocumentManagerVM.DocumentVMs.Remove(this);
}

In your view, set up your binding in the LayoutItemContainerStyle or LayoutItemContainerStyleSelector.

<ad:DockingManager
    DataContext="{Binding DocumentManagerVM}"
    DocumentsSource="{Binding DocumentVMs}">

    <ad:DockingManager.LayoutItemContainerStyle>
        <Style TargetType="{x:Type ad:LayoutItem}">
            <Setter Property="Title" Value="{Binding Model.Header}"/>
            <Setter Property="CloseCommand" Value="{Binding Model.RequestCloseCommand}"/>
        </Style>
    </ad:DockingManager.LayoutItemContainerStyle>

</ad:DockingManager>
like image 80
sfm Avatar answered Oct 13 '22 11:10

sfm