1. Is this feature planned for future releases of the Telerik controls?
2. If not, is there any code, either released or not, that can be used as a workaround ? One could imagine a PaneFactory that would track INotifycollectionChanges in a collection and feed the Items property of a RadPaneGroup.
3. Finally, if there is no such support code, we will have to implement it ourselves. Any gotchas that we should be aware of before starting to work on it?
Thanks in advance. I would appreciate if you could provide some answers as soon as possible.
29 Answers, 1 is accepted
Thank you for contacting us.
Unfortunately, RadDocking control does not support such a feature out of the box with its current implementation. In case of growing client interest , we will consider future development options. However, you could achieve the same functionality using some extensions (attached properties). We have a basic example that shows how to use the RadDocking control with the MVVM pattern. Attached you can find the sample.
I hope this suits your needs. Please do not hesitate to contact us if you require any further information.
George
the Telerik team

This does not solve the problem of RadDocking not supporting MVVM directly, but it is very useful to come up with a workaround that suits our needs. A more direct solution would still be much preferred though, so please register our interest in that.

Just to let you know that we also are very interested in that.
When implementing MVVM support for the controls, please keep two things in mind: no code-behind need and no UI controls being instantiated in our viewmodels.
Binding + DataTemplates is the key!
Cheers,
André Carlucci

I wrote another implementation for a MVVM friendly RadPaneGroup, where you can create your RadPanes using DataTemplates in a completely customizable way.
You can also use a DataTemplateSelector if you want to attach RadPanes with different look and feel/content at runtime.
I want to post the code in the code library, but there is no section for RadDock.
How to proceed?
Cheers,
André Carlucci
Thank you for contacting us.
I would suggest you to post the code in the General and Integration Projects section.
George
the Telerik team
Thank you for sending your feedback.
All the best,
Yana
the Telerik team

Would you please be able to add your MVVM friendly RadPaneGroup to the code library? It would really be useful for me.
Much appreciated,
John


I'll upload it somewhere, give me a day.
Cheers,
André

There's no link to Andre's code here and that would be helpful so we could all locate it. It was suggested that this location may be appropriate. It's not there. Perhaps it really should be found under Docking. But it's not there either.
Driving an observable collection from the view-model is the way to go. Please add me to those requesting this feature.
Thanks

Long time since I don't touch this code, but I uploaded it to bitbucket so you can check it out.
I hope it helps.
https://bitbucket.org/andrecarlucci/telerikcontrib/src
Cheers,
André Carlucci

I broke the standard pattern and made my view's code-behind aware of the backing view-model. The two elements that the view needs to see in the view model are:
public
BindingList<DataTableViewModel> DataTableViewModelList
{
get
{
return
_dataTableViewModelList; }
}
public
DataTableViewModel CurrentDataTableViewModel
{
get
{
return
_currentDataTableViewModel; }
set
{
SetFieldValueAndRaisePropertyChangedNotification<DataTableViewModel>(
ref
_currentDataTableViewModel, value, () => CurrentDataTableViewModel);
OnCurrentDataTableViewModelChanged();
}
}
The BindingList contains the view models that are to be associated with RadDocumentPanes.
The view watches this list and executes the following when it changes:
private
void
RadPaneDocumentList_ListChanged(
object
sender, System.ComponentModel.ListChangedEventArgs e)
{
// look for view models that have been added
foreach
(DataTableViewModel dataTableViewModel
in
_datagridViewModel.DataTableViewModelList)
{
if
(_paneTrackingDataByPane.TryFindPaneForViewModel(dataTableViewModel) ==
null
)
{
System.Diagnostics.Trace.WriteLine(
string
.Format(
"RadPaneDocumentList_ListChanged: Adding {0}"
, dataTableViewModel.ControlName));
DataTableView dataTableView =
new
DataTableView();
dataTableView.DataContext = dataTableViewModel;
RadDocumentPane radDocumentPane =
new
RadDocumentPane();
radDocumentPane.Content = dataTableView;
radDocumentPane.Name = dataTableViewModel.ControlName;
radDocumentPane.Title = dataTableViewModel.Title;
// our tracking
PaneTrackingData paneTrackingData =
new
DatagridView.PaneTrackingData(dataTableView, dataTableViewModel);
_paneTrackingDataByPane.Add(radDocumentPane, paneTrackingData);
// start monitoring and then add so we see all relevant messages
RadDocumentPane_StartMonitoringChanges(radDocumentPane);
_rpanegroupDocuments.Items.Add(radDocumentPane);
}
}
... removal code omitted
for
brevity
}
_rpanegroupDocuments is in the xaml:
<
telerik:RadDocking.DocumentHost
>
<
telerik:RadSplitContainer
>
<
telerik:RadPaneGroup
Name
=
"_rpanegroupDocuments"
/>
</
telerik:RadSplitContainer
>
</
telerik:RadDocking.DocumentHost
>
I don't feel too bad about what I had to do in the code behind - it's all view related. The view model remains clean and it drives the creation/deletion of the elements being displayed.
I'll add just a bit more in case anybody is interested in handling the RadDocumentPane being closed by the user. Handle the Close event from the xaml:
<
telerik:RadDocking
Close
=
"RadDocking_OnClose"
>
...
The method looks like this:
private
void
RadDocking_OnClose(
object
sender, StateChangeEventArgs e)
{
foreach
(RadDocumentPane radDocumentPane
in
e.Panes)
{
System.Diagnostics.Trace.WriteLine(
string
.Format(
"RadDocking_OnClose: {0}"
, radDocumentPane.Name));
// we must know this pane
LLDiagnostics.IsInCollectionOrThrow(_paneTrackingDataByPane.ContainsKey(radDocumentPane),
"radDocumentPane not known"
);
PaneTrackingData paneTrackingData = _paneTrackingDataByPane[radDocumentPane];
// stop monitoring before removing - this is the last event we want to know about for this pane
RadDocumentPane_StopMonitoringChanges(radDocumentPane);
// inform the view model that the active selection has been reset,
// the rad framework switches active tabs before we get this message but we still have to handle
// the case where this is the last tab being closed
if
(paneTrackingData.DataTableViewModel == _datagridViewModel.CurrentDataTableViewModel)
{
SetViewModelCurrentDocumentPane(
null
);
}
_paneTrackingDataByPane.Remove(radDocumentPane);
_datagridViewModel.DataTableViewModelList.Remove(paneTrackingData.DataTableViewModel);
}
}
The view model is watching its DataTableViewModelList and is notified of the change. It has a shadow copy of the list so it can compare the two to determine which element was removed and do the teardown for it before it is disposed.
While I'm on a roll, I'll include how I'm handling notifying the view model of the currently selected item. When a pane is created, the following method is called:
private
void
RadDocumentPane_StartMonitoringChanges(RadDocumentPane radDocumentPane)
{
...
System.ComponentModel.DependencyPropertyDescriptor
dependencyPropertyDescriptor
= System.ComponentModel.DependencyPropertyDescriptor.FromProperty(RadDocumentPane.IsActiveProperty,
typeof
(RadDocumentPane));
dependencyPropertyDescriptor.AddValueChanged(radDocumentPane, RadDocumentPane_IsActiveEventHandler);
}
When the IsActive state changes for the pane, the following is called:
private
void
RadDocumentPane_IsActiveEventHandler(
object
sender, System.EventArgs args)
{
RadDocumentPane radDocumentPane = sender
as
RadDocumentPane;
LLDiagnostics.IsInCollectionOrThrow(_paneTrackingDataByPane.ContainsKey(radDocumentPane),
"radDocumentPane not known"
);
PaneTrackingData paneTrackingData = _paneTrackingDataByPane[radDocumentPane];
if
(radDocumentPane.IsActive && !paneTrackingData.IsActive)
{
System.Diagnostics.Trace.WriteLine(
string
.Format(
"RadDocumentPane_IsActiveEventHandler: {0} -- Current"
, radDocumentPane.Name));
paneTrackingData.IsActive =
true
;
SetViewModelCurrentDocumentPane(radDocumentPane);
}
else
if
(!radDocumentPane.IsActive && paneTrackingData.IsActive)
{
System.Diagnostics.Trace.WriteLine(
string
.Format(
"RadDocumentPane_IsActiveEventHandler: {0} -- No longer current"
, radDocumentPane.Name));
paneTrackingData.IsActive =
false
;
}
}
At first, I didn't think that the IsActive property was being changed correctly. When I detached two panes and clicked back and forth between the header of their tool windows, the IsActive state did not change. If I clicked on a control in each pane then the toggling would work. A couple cups of coffee later, I realized that I was setting the focus and it must be related to the control going active! So, when the content (a user control) is loaded, it programmatically sets the focus to one of its controls. Now, when torn off, the user can toggle between the panes and the IsActive state is fired and the view model gets notification of its currently selected element.
And, for completeness:
private
void
SetViewModelCurrentDocumentPane(RadDocumentPane radDocumentPane)
{
DataTableViewModel dataTableViewModel = (radDocumentPane !=
null
) ? _paneTrackingDataByPane[radDocumentPane].DataTableViewModel :
null
;
if
(dataTableViewModel != _datagridViewModel.CurrentDataTableViewModel)
{
_isInternalCurrentRadDocumentPaneChange =
true
;
_datagridViewModel.CurrentDataTableViewModel = dataTableViewModel;
_isInternalCurrentRadDocumentPaneChange =
false
;
}
}
I'm sure that many recognize the litte true/false dance around the change of the view model's property. The view is watching this property so it can bring the appropriate RadDocumentPane to the foreground when changed by the view model. The change is echoed from the view model - the flag allows the view to determine where the change originated.
Hope this helps someone else get started.

<
telerik:RadDocking
Grid.Row
=
"1"
Grid.Column
=
"0"
>
<
telerik:RadDocking.DocumentHost
>
<
telerik:RadSplitContainer
>
<
local:MvvmRadPaneGroup
PanesSource
=
"{Binding WhiteboardItems}"
PanesTemplate
=
"{StaticResource PaneItemTemplate}"
PanesTemplateSelector
=
"{StaticResource WIContentSelector}"
/>
</
telerik:RadSplitContainer
>
</
telerik:RadDocking.DocumentHost
>
</
telerik:RadDocking
>

when I run the project that George provided and unpin the panes in the left hand pane group I get an error when the last pane is unpinned. I'm seeing hte same error in my own project using RadDocking. I'm using version 2012.2.912.40.
System.Argument.NulException
{"Value cannot be null.\r\nParameter name: collection"}
Stack Trace:
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at Telerik.Windows.Controls.RadTabControlAutomationPeer.GetChildrenCore() in c:\TB\135\WPF_Scrum\Release_WPF\Sources\Development\Controls\Navigation\TabControl\RadTabControlAutomationPeer.cs:line 196
at System.Windows.Automation.Peers.AutomationPeer.EnsureChildren()
at System.Windows.Automation.Peers.AutomationPeer.UpdateChildrenInternal(Int32 invalidateLimit)
at System.Windows.Automation.Peers.ItemsControlAutomationPeer.UpdateChildren()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.ContextLayoutManager.fireAutomationEvents()
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run()
at DockingAndMVVM.App.Main() in c:\Users\Mark\Documents\Development\Helpers\Telerik Docking and MVVM\obj\Debug\App.g.cs:line 0
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Thanks for bringing this issue to our attention. We recently found it and it is already fixed. The fix should be introduced in our next internal build.
Kind regards,Stefan
the Telerik team
Time to cast your vote for Telerik! Tell DevPro Connections and Windows IT Pro why Telerik is your choice. Telerik is nominated in a total of 25 categories.

If you use the MVVMLight Framework (and probably some others too) there is an elegant way to work around this. You can use Laurents EventToCommand trigger and hook e.g. the PreviewClose event. EventToCommand also allows you to pass the EventArgs from the event - and from that you should be able to identify the pane and then remove it from your ObservableCollection in your view model.
NOTE : This will handle someone clicking the Close (X) icon - IT WILL NOT WORK IF YOU DRAG - I am looking at what other events need to be handled to support that , so this might not be a viable solution for all scenarios, but maybe someone else out there has used this method and can add more info ?
eg
<
telerik:RadDocking
Grid.Row
=
"1"
>
<
i:Interaction.Triggers
>
<
i:EventTrigger
EventName
=
"PreviewClose"
>
<
cmd:EventToCommand
Command
=
"{Binding PreviewCloseCommand, Mode= OneWay}"
MustToggleIsEnabledValue
=
"True"
PassEventArgsToCommand
=
"True"
/>
</
i:EventTrigger
>
</
i:Interaction.Triggers
>
</
telerik:RadPaneGroup
>
Remember to bind the ItemsSource in your RadPaneGroup to an ObservableCollection in your ViewModel
e.g.
<
telerik:RadDocking.DocumentHost
>
<
telerik:RadSplitContainer
x:Name
=
"radSplitContainer"
>
<
telerik:RadPaneGroup
x:Name
=
"radPaneGroup"
ItemsSource
=
"{Binding DockingPanes, Mode=TwoWay}"
/>
</
telerik:RadSplitContainer
>
</
telerik:RadDocking.DocumentHost
>
In you ViewModel code :
public
ObservableCollection<RadPane> DockingPanes
{
get
;
set
;
}
In the constructor of your view model remember to setup the relay command
eg
PreviewCloseCommand =
new
RelayCommand<RoutedEventArgs>((e) => PreviwClose(e));
and finally a method to locate the Pane and remove it from the collection - this is a VERY CRUDE example !
public
void
PreviwClose(RoutedEventArgs e)
{
RadDocking dock = (RadDocking)e.Source;
var header = dock.ActivePane.Header;
List<RadPane> toremove =
new
List<RadPane>();
foreach
(var p
in
DockingPanes)
{
if
(p.Header == header)
toremove.Add(p);
}
foreach
(var p
in
toremove)
{
DockingPanes.Remove(p);
}
e.Handled =
true
;
}
Remember to set the event to Handled to prevent the event continuing to bubble up the stack !
Hope that helps, I spent quite some time banging my head on the table trying to solve this.
The real solution would be for Telerik to add some addition Commands to the Docking library.
I noticed that DevExpress are using Commands in their docking library to provide MVVM support. Binding is the way to go !


This is very close to working, however if you close a RadPane off manually by clicking on the 'x' then attempt to add another it throws an ArgumentOutOfRangeException and it appears that any changes made by the user are not reflected in the ViewModel.
How can this be achieved.
thanks
Duncan

RadDocking seems useless for me if I can't use ItemsSource and ItemContentTemplateSelector
Please let me know if you plan to implement something
With the Q3 2013 SP release RadDocking has a new mechanism which helps binding it to a collection of objects. Please, refer to our online documentation here and let me know if you have further questions - http://www.telerik.com/help/wpf/raddocking-features-panes-panesource.html
Hope this helps.
Regards,
George
Telerik
Learn what features your users use (or don't use) in your application. Know your audience. Target it better. Develop wisely.
Sign up for Free application insights >>

I've enhanced the MVVM docking example to work properly with floating and to feed back to the original source collection when panes are closed. Also added ability to bind more RadPane properties from the model
Hope someone finds this useful :)
Stevo
(Note that you'll need to remove the dummy .jpg extension to unzip)
Hello Stevo,
Thank you for sharing the sample project. Please, note that we have a build-in functionality in RadDocking control which helps implementing the MVVM scenarios. You can find more information in the following links:
- online documentation - http://www.telerik.com/help/wpf/raddocking-features-panes-panesource.html
- blogpost - http://blogs.telerik.com/blogs/13-12-09/building-ide-with-raddocking-for-wpf-silverlight
Regards,
Telerik

yes thanks I've seen that.
To be honest I don't think it's done very well, since you are putting UI controls (RadPanes) into ViewModels. That will lead many people to add UI specific code/controls into their view models.
I think the original example binding models is much better design, that's why I have enhanced it adding more features, fixed some bugs and kept the ViewModels nice and clean.
Best Regards,
Stevo
In the blogpost example the ViewModel misses the Views. The only place where the views are used is in the DockingPanesFactory. The PaneViewModel has a property ContentType which is used in CustomDockingPanesFactory for generating pane's content, but it can be done with string or even attributes (MEF approach).
However, the provided sample could be helpful to the community as well, but it should be sent as CodeLibrary. Note that in the forums it is allowed sending only image files due to security reasons. Please, send the sample project as a CodeLibrary and if it's approved, we will reward you with additional Telerik points.
Regards,
George
Telerik

I am reading this and it's somewhat troubling to me that Telerik does not know how to do Binding and DataTemplates.
I'll be the first to admit I do not mind writing some code behind once in awhile in a WPF MVVM architecture. An event handler there, extending my View Model there. But the key to MVVM is that the parts are separate from one another, very loosely coupled, very testable in their own right.
As has been mentioned:
- This is a key WPF building block.
- Your (Telerik) competitors know how to do it.
data-templates-and-mvvm-support
I'd like to make as informed a vendor decision as possible where component vendors are concerned, so that we can defer that expertise to the vendor, while focusing on business goals. This is a big one, Telerik.
Where does Telerik stand with this functionality today? In the trial I installed, I am under the impression this has gone unsupported, still?
Thank ye...

I have had some levels of success using the 'visual studio' example in their github repo.
https://github.com/telerik/xaml-sdk/tree/master/Docking/VisualStudioDocking
This still comes with a couple of issues, but should get you a long way.
Duncan
Your remarks are correct - in WPF world using DataTemplates is a key building block, but RadDocking control is from the family of layout controls. The difficulty of implementing it with MVVM is its complex nature and various scenarios with the UI and the visual tree. The recommended way to go with MVVM is the DockingPanesFactory. It is similar to the ItemContainerGenerator in ItemsControls and allows to have full control over panes, including setting a position for the new items.
Duncan,
We are glad to hear that you found the provided example helpful. Can you please point us the issues you faced with it? We appreciate your feedback and we'll improve the example.
Regards,
George
Telerik
Build cross-platform mobile apps using Visual Studio and .NET. Register for the online webinar on 03/27/2014, 11:00AM US ET.. Seats are limited.

To anyone looking for a solution, I recommend avalondock. It's open source and natively set up to work in an mvvm architecture.