Created
November 7, 2011 16:58
-
-
Save dbeattie71/1345512 to your computer and use it in GitHub Desktop.
Telerik RadDockingManager implementation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.IO.IsolatedStorage; | |
using System.Linq; | |
using System.Windows; | |
using System.Windows.Data; | |
using System.Windows.Media; | |
using Caliburn.Micro; | |
using Telerik.Windows.Controls; | |
using Telerik.Windows.Controls.Docking; | |
//http://caliburnmicro.codeplex.com/discussions/231809 | |
namespace ProShop.Client.Framework | |
{ | |
public interface IRadDockingManager | |
{ | |
void Init(IViewAware trackingShellViewModel); | |
void ShowWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null | |
); | |
void ShowDockedWindow(IViewAware viewModel, | |
object context, | |
DockPosition dockPosition = DockPosition.Left, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null | |
); | |
void ShowFloatingDockableWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null); | |
void ShowFloatingWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null); | |
void CloseAllWindows(); | |
void LoadLayout(); | |
string SaveLayout(); | |
} | |
public class RadPaneEx : RadPane | |
{ | |
public void Close() | |
{ | |
var stateChangeEventArgs = new StateChangeEventArgs(null, this, new List<RadPane> { this }); | |
OnClose(this, stateChangeEventArgs); | |
} | |
public event EventHandler<StateChangeEventArgs> OnClose = delegate { }; | |
} | |
public class RadDockingManager : IRadDockingManager | |
{ | |
public RadDocking RadDocking { get; set; } | |
public IScreen RootViewModel { get; set; } | |
public void Init(IViewAware parentViewModel) | |
{ | |
RadDocking = GetRadDocking(parentViewModel); | |
} | |
private static RadDocking GetRadDocking(IViewAware parentViewModel) | |
{ | |
//return Application.Current.RootVisual.FindChildByType<RadDocking>(); | |
return FindVisualChild<RadDocking>(parentViewModel.GetView() as DependencyObject); | |
} | |
public void ShowWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null) | |
{ | |
var radPane = CreateRadPane(viewModel, context); | |
if (configRadPane != null) configRadPane(radPane); | |
if (RadDocking.GetRadPane(radPane) != null) return; | |
AttachPane(radPane, configRadPaneGroup, configSplitContainer); | |
} | |
public void ShowDockedWindow(IViewAware viewModel, | |
object context, | |
DockPosition dockPosition = DockPosition.Left, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null) | |
{ | |
var radPane = CreateRadPane(viewModel, context); | |
if (configRadPane != null) configRadPane(radPane); | |
if (RadDocking.GetRadPane(radPane) != null) return; | |
AttachDockedPane(radPane, configRadPaneGroup, configSplitContainer, dockPosition); | |
} | |
public void ShowFloatingDockableWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null) | |
{ | |
var radPane = CreateRadPane(viewModel, context); | |
if (configRadPane != null) configRadPane(radPane); | |
if (RadDocking.GetRadPane(radPane) != null) return; | |
AttachFloatingDockablePane(radPane, configRadPaneGroup, configSplitContainer); | |
} | |
public void ShowFloatingWindow(IViewAware viewModel, | |
object context, | |
Action<RadPaneEx> configRadPane = null, | |
Action<RadPaneGroup> configRadPaneGroup = null, | |
Action<RadSplitContainer> configSplitContainer = null) | |
{ | |
var radPane = CreateRadPane(viewModel, context); | |
if (configRadPane != null) configRadPane(radPane); | |
if (RadDocking.GetRadPane(radPane) != null) return; | |
AttachFloatingPane(radPane, configRadPaneGroup, configSplitContainer); | |
} | |
public void CloseAllWindows() | |
{ | |
foreach (RadPaneEx radPane in RadDocking.Panes.ToList()) | |
radPane.Close(); | |
//RemovePane(radPane as RadPaneEx); | |
} | |
public void LoadLayout() | |
{ | |
// Load your layot from the isolated storage. | |
using (IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication()) | |
{ | |
using (var isoStream = storage.OpenFile("RadDocking_Layout.xml", FileMode.Open)) | |
{ | |
RadDocking.LoadLayout(isoStream); | |
} | |
} | |
} | |
public string SaveLayout() | |
{ | |
string xml; | |
// Save your layout for example in the isolated storage. | |
using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) | |
{ | |
using (var isoStream = storage.OpenFile("RadDocking_Layout.xml", FileMode.OpenOrCreate)) | |
{ | |
RadDocking.SaveLayout(isoStream); | |
isoStream.Seek(0, SeekOrigin.Begin); | |
var reader = new StreamReader(isoStream); | |
xml = reader.ReadToEnd(); | |
} | |
} | |
// Return the generated XML | |
return xml; | |
} | |
private RadPaneEx CreateRadPane(object viewModel, object context) | |
{ | |
var view = EnsureRadPane(viewModel, ViewLocator.LocateForModel(viewModel, null, context)); | |
ViewModelBinder.Bind(viewModel, view, context); | |
var haveDisplayName = viewModel as IHaveDisplayName; | |
if (haveDisplayName != null && !ConventionManager.HasBinding(view, RadPane.TitleProperty)) | |
{ | |
var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay }; | |
view.SetBinding(HeaderedContentControl.HeaderProperty, binding); | |
} | |
new DockableWindowConductor(viewModel, view, RadDocking); | |
return view; | |
} | |
private RadPaneEx EnsureRadPane(object viewModel, object view) | |
{ | |
var radPane = view as RadPaneEx; | |
if (radPane == null) | |
{ | |
radPane = new RadPaneEx { DataContext = viewModel, Content = view }; | |
radPane.SetValue(View.IsGeneratedProperty, true); | |
} | |
return radPane; | |
} | |
private RadSplitContainer GetRadSplitContainer(Action<RadSplitContainer> configSplitContainer) | |
{ | |
var radSplitContainer = RadDocking.DocumentHost as RadSplitContainer; | |
if (radSplitContainer == null) | |
{ | |
radSplitContainer = new RadSplitContainer(); | |
radSplitContainer.SetValue(View.IsGeneratedProperty, true); | |
RadDocking.DocumentHost = radSplitContainer; | |
} | |
else | |
{ | |
radSplitContainer = new RadSplitContainer(); // { InitialPosition = }; | |
if (configSplitContainer != null) configSplitContainer(radSplitContainer); | |
radSplitContainer.SetValue(View.IsGeneratedProperty, true); | |
RadDocking.Items.Add(radSplitContainer); | |
} | |
return radSplitContainer; | |
} | |
private RadPaneGroup GetRadPaneGroup(Action<RadPaneGroup> configRadPaneGroup) | |
{ | |
var radPaneGroup = new RadPaneGroup(); | |
if (configRadPaneGroup != null) configRadPaneGroup(radPaneGroup); | |
radPaneGroup.SetValue(View.IsGeneratedProperty, true); | |
return radPaneGroup; | |
} | |
private void AttachPane(RadPaneEx radPane, Action<RadPaneGroup> configRadPaneGroup, Action<RadSplitContainer> configSplitContainer) | |
{ | |
var radSplitContainer = GetRadSplitContainer(configSplitContainer); | |
var radPaneGroup = GetRadPaneGroup(configRadPaneGroup); | |
radSplitContainer.Items.Add(radPaneGroup); | |
radPaneGroup.Items.Add(radPane); | |
} | |
private void AttachDockedPane(RadPaneEx radPane, Action<RadPaneGroup> configRadPaneGroup, Action<RadSplitContainer> configSplitContainer, DockPosition dockPosition) | |
{ | |
var radSplitContainer = GetRadSplitContainer(configSplitContainer); | |
var radPaneGroup = GetRadPaneGroup(configRadPaneGroup); | |
radSplitContainer.Items.Add(radPaneGroup); | |
radPaneGroup.AddItem(radPane, dockPosition); | |
} | |
private void AttachFloatingDockablePane(RadPaneEx radPane, Action<RadPaneGroup> configRadPaneGroup, Action<RadSplitContainer> configSplitContainer) | |
{ | |
var radSplitContainer = GetRadSplitContainer(configSplitContainer); | |
var radPaneGroup = GetRadPaneGroup(configRadPaneGroup); | |
radSplitContainer.Items.Add(radPaneGroup); | |
radPaneGroup.Items.Add(radPane); | |
radPane.MakeFloatingDockable(); | |
} | |
private void AttachFloatingPane(RadPaneEx radPane, Action<RadPaneGroup> configRadPaneGroup, Action<RadSplitContainer> configSplitContainer) | |
{ | |
var radSplitContainer = GetRadSplitContainer(configSplitContainer); | |
var radPaneGroup = GetRadPaneGroup(configRadPaneGroup); | |
radSplitContainer.Items.Add(radPaneGroup); | |
radPaneGroup.Items.Add(radPane); | |
radPane.MakeFloatingOnly(); | |
} | |
private static TChildItem FindVisualChild<TChildItem>(DependencyObject obj) | |
where TChildItem : DependencyObject | |
{ | |
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) | |
{ | |
var child = VisualTreeHelper.GetChild(obj, i); | |
if (child != null && child is TChildItem) | |
return (TChildItem)child; | |
var childOfChild = FindVisualChild<TChildItem>(child); | |
if (childOfChild != null) | |
return childOfChild; | |
} | |
return null; | |
} | |
#region RadDocking events | |
//PaneStateChanged - This event is fired whenever the state of the RadPane is changed (e.g. pin, unpin, close, show, etc.). | |
//Pin\Unpin Events | |
//PreviewUnpin - Occurs before the RadPane is unpinned. The type of the passed event arguments is StateChangeEventArgs. | |
//Unpin - Occurs when the RadPane is unpinned. The type of the passed event arguments is StateChangeEventArgs. | |
//PreviewPin - Occurs before the RadPane is pinned. The type of the passed event arguments is StateChangeEventArgs. | |
//Pin - Occurs when the RadPane is pinned. The type of the passed event arguments is StateChangeEventArgs. | |
//Show\Hide Events | |
//PreviewShow - Occurs before the RadPane is shown. | |
//Show - Occurs when the RadPane is shown. | |
//PreviewClose - Occurs before the RadPane is closed. | |
//Close - Occurs when the RadPane is closed. | |
//PreviewWindowClose - Occurs before the ToolWindow is closed. | |
//WindowClose - Occurs when the ToolWindow is closed. | |
//The PreviewClose and Close events are fired, whenever you press the Close button. | |
//The PreviewShow and Show events are fired, when you invoke the ShowAllPanes method of the RadPaneGroup class. This method will show all hidden panes. | |
#endregion | |
private class DockableWindowConductor | |
{ | |
private static bool _eventsRegistered; | |
private RadDocking RadDocking { get; set; } | |
private object ViewModel { get; set; } | |
private RadPaneEx View { get; set; } | |
private bool IsClosing { get; set; } | |
private bool IsDeactivatingFromView { get; set; } | |
private bool IsDeactivatingFromViewModel { get; set; } | |
public DockableWindowConductor(object viewModel, RadPaneEx view, RadDocking radDocking) | |
{ | |
RadDocking = radDocking; | |
ViewModel = viewModel; | |
View = view; | |
var activatable = viewModel as IActivate; | |
if (activatable != null) | |
activatable.Activate(); | |
var deactivatable = viewModel as IDeactivate; | |
if (deactivatable != null) | |
{ | |
//only want these registered once | |
if (!_eventsRegistered) | |
{ | |
_eventsRegistered = true; | |
radDocking.PreviewClose += OnClosing; | |
radDocking.Close += OnClosed; | |
} | |
view.OnClose += OnClosed; | |
deactivatable.Deactivated += OnDeactivated; | |
} | |
var guard = viewModel as IGuardClose; | |
if (guard == null) | |
{ | |
//only want these registered once | |
if (!_eventsRegistered) | |
{ | |
_eventsRegistered = true; | |
radDocking.Close += OnClosed; | |
} | |
} | |
} | |
private void OnClosed(object sender, StateChangeEventArgs e) | |
{ | |
var tempRadPaneEx = sender as RadPaneEx; | |
//if RadPaneEx initiated the close, un-registered OnClosed | |
if (tempRadPaneEx != null) | |
View.OnClose -= OnClosed; | |
var radPane = e.Panes.FirstOrDefault(); | |
if (radPane == null) return; | |
var deactivatable = radPane.DataContext as IDeactivate; | |
if (deactivatable == null) return; | |
if (IsDeactivatingFromViewModel) | |
return; | |
IsDeactivatingFromView = true; | |
RemovePane(radPane); | |
deactivatable.Deactivate(true); | |
IsDeactivatingFromView = false; | |
} | |
private void OnDeactivated(object sender, DeactivationEventArgs e) | |
{ | |
((IDeactivate)ViewModel).Deactivated -= OnDeactivated; | |
if (!e.WasClosed) | |
return; | |
IsDeactivatingFromViewModel = true; | |
IsClosing = true; | |
RemovePane(FindPane(sender)); | |
IsClosing = false; | |
IsDeactivatingFromViewModel = false; | |
} | |
private void OnClosing(object sender, StateChangeEventArgs e) | |
{ | |
var radPane = e.Panes.FirstOrDefault(); | |
if (radPane == null) | |
return; | |
if (IsClosing) | |
{ | |
IsClosing = false; | |
return; | |
} | |
var guard = radPane.DataContext as IGuardClose; | |
if (guard == null) | |
return; | |
if (e.Handled) | |
return; | |
if (IsClosing) | |
{ | |
IsClosing = false; | |
return; | |
} | |
bool running = false, shouldEnd = false; | |
var tempRunning = running; | |
guard.CanClose(canClose => Execute.OnUIThread(() => | |
{ | |
if (tempRunning && canClose) | |
{ | |
IsClosing = true; | |
radPane.IsHidden = true; | |
} | |
else | |
e.Handled = !canClose; | |
shouldEnd = true; | |
})); | |
if (shouldEnd) | |
return; | |
running = e.Handled = true; | |
} | |
private RadPane FindPane(object viewModel) | |
{ | |
return RadDocking.Panes.FirstOrDefault(p => p.DataContext == viewModel); | |
} | |
private void RemovePane(RadPane pane) | |
{ | |
if (pane == null) | |
return; | |
var paneGroup = pane.PaneGroup; | |
pane.RemoveFromParent(); | |
if (paneGroup == null || paneGroup.HasItems) | |
return; | |
var splitContainer = paneGroup.ParentContainer; | |
paneGroup.RemoveFromParent(); | |
if (splitContainer == null || splitContainer.HasItems) | |
return; | |
if (splitContainer.IsInDocumentHost) | |
RadDocking.DocumentHost = null; | |
else | |
RadDocking.Items.Remove(splitContainer); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there an example of how to wire this up available somewhere?