For this project I'm targeting .NET 4.6 with Prism 6 as a toolkit to implement correct MVVM patterns.
The code below allows me to raise a dialog box with Yes, No, Cancel buttons. If the user clicks 'Yes' the content is saved then the tab item closed. If 'No' the tab item is only closed, and if 'Cancelled' then return, do not close anything.
However the dialog is only displayed if the content is dirty. If not just close it.
I feel like in its current sate it's very messy and could be cleaned up. Take a look below and tell me what you think.
internal class CloseTabAction : TriggerAction<Button>
{
protected override void Invoke(object parameter)
{
var args = parameter as RoutedEventArgs;
if (args == null) return;
var tabItem = FindParent<TabItem>(args.OriginalSource as DependencyObject);
if (tabItem == null) return;
var tabControl = FindParent<TabControl>(tabItem);
if (tabControl == null) return;
var region = RegionManager.GetObservableRegion(tabControl).Value;
if (region == null) return;
var context = new NavigationContext(region.NavigationService, null);
if (Implements<IRequestTabItemClose>(tabItem.Content, r => r.IsContentDirty(isDirty =>
{
if (isDirty)
{
if (Implements<IRequestTabItemClose>(tabItem.Content,
i => i.ConfirmCloseRequest((canSave, cancel) =>
{
if (cancel) return;
if (!region.Views.Contains(tabItem.Content)) return;
if (canSave)
{
i.SaveContent();
region.Remove(tabItem.Content);
return;
}
region.Remove(tabItem.Content);
})))
return;
}
region.Remove(tabItem.Content);
}))) return;
}
private static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
while (true)
{
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
var parent = parentObject as T;
if (parent != null) return parent;
child = parentObject;
}
}
private static T Implementor<T>(object content) where T : class
{
T implementor = content as T;
if (implementor != null) return implementor;
var element = content as FrameworkElement;
if (element != null) implementor = element.DataContext as T;
return implementor;
}
private static bool Implements<T>(object content, Action<T> action) where T : class
{
T target = Implementor<T>(content);
if (target == null) return false;
action(target);
return true;
}
}
Here are my custom interfaces:
public interface IRequestTabItemClose
{
void ConfirmCloseRequest(Action<bool, bool> callback);
void SaveContent();
void IsContentDirty(Action<bool> callBack);
}
public interface ICloseConfirmation : INotification
{
bool Confirmed { get; set; }
bool Cancel { get; set; }
}
ViewModel:
public bool IsDirty { get; set; }
public virtual void ConfirmCloseRequest(Action<bool, bool> callback)
{
ClosePopupRequest.Raise(
new CloseConfirmation {Title = "Confirm", Content = "Do you want to save this view before closing?"},
r => { callback(r.Confirmed, r.Cancel); });
}
public virtual void SaveContent()
{
// Save stuff
}
public void IsContentDirty(Action<bool> callBack)
{
callBack(IsDirty);
}