As far as I can tell AvalonDock has two ways to add ViewModels. The AnchorablesSource and DocumentsSource properties. I simply want to be able to doc my current ViewModels in the way FileViewModels are in the LayoutDocumentPane without deriving from FileViewModel. The problem is if you don't derive from FileViewModel the window looses all of its labels. Also if you use the AnchorableSource instead of DocumentSource, when you add a new window it is added to the right side of the main and not as a tab to the documentspane. Does anyone know how to achieve this desired result?
Source code reference
http://avalondock.codeplex.com/
Here is the FileViewModel that comes with the AvalonDock test application
namespace AvalonDock.MVVMTestApp
{
class FileViewModel : PaneViewModel
{
static ImageSourceConverter ISC = new ImageSourceConverter();
public FileViewModel(string filePath)
{
FilePath = filePath;
Title = FileName;
//Set the icon only for open documents (just a test)
IconSource = ISC.ConvertFromInvariantString(@"pack://application:,,/Images/document.png") as ImageSource;
}
public FileViewModel()
{
IsDirty = true;
Title = FileName;
}
#region FilePath
private string _filePath = null;
public string FilePath
{
get { return _filePath; }
set
{
if (_filePath != value)
{
_filePath = value;
RaisePropertyChanged("FilePath");
RaisePropertyChanged("FileName");
RaisePropertyChanged("Title");
if (File.Exists(_filePath))
{
_textContent = File.ReadAllText(_filePath);
ContentId = _filePath;
}
}
}
}
#endregion
public string FileName
{
get
{
if (FilePath == null)
return "Noname" + (IsDirty ? "*" : "");
return System.IO.Path.GetFileName(FilePath) + (IsDirty ? "*" : "");
}
}
#region TextContent
private string _textContent = string.Empty;
public string TextContent
{
get { return _textContent; }
set
{
if (_textContent != value)
{
_textContent = value;
RaisePropertyChanged("TextContent");
IsDirty = true;
}
}
}
#endregion
#region IsDirty
private bool _isDirty = false;
public bool IsDirty
{
get { return _isDirty; }
set
{
if (_isDirty != value)
{
_isDirty = value;
RaisePropertyChanged("IsDirty");
RaisePropertyChanged("FileName");
}
}
}
#endregion
#region SaveCommand
RelayCommand _saveCommand = null;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand((p) => OnSave(p), (p) => CanSave(p));
}
return _saveCommand;
}
}
private bool CanSave(object parameter)
{
return IsDirty;
}
private void OnSave(object parameter)
{
Workspace.This.Save(this, false);
}
#endregion
#region SaveAsCommand
RelayCommand _saveAsCommand = null;
public ICommand SaveAsCommand
{
get
{
if (_saveAsCommand == null)
{
_saveAsCommand = new RelayCommand((p) => OnSaveAs(p), (p) => CanSaveAs(p));
}
return _saveAsCommand;
}
}
private bool CanSaveAs(object parameter)
{
return IsDirty;
}
private void OnSaveAs(object parameter)
{
Workspace.This.Save(this, true);
}
#endregion
#region CloseCommand
RelayCommand _closeCommand = null;
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
{
_closeCommand = new RelayCommand((p) => OnClose(), (p) => CanClose());
}
return _closeCommand;
}
}
private bool CanClose()
{
return true;
}
private void OnClose()
{
Workspace.This.Close(this);
}
#endregion
}
}
Here is the main window xaml from the test application
<Window x:Class="AvalonDock.MVVMTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonDock="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=AvalonDock"
xmlns:local="clr-namespace:AvalonDock.MVVMTestApp"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:sysWindows="clr-namespace:System.Windows;assembly=PresentationCore"
x:Name="mainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:ActiveDocumentConverter x:Key="ActiveDocumentConverter"/>
<avalonDock:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="File">
<MenuItem Header="New" Command="{Binding NewCommand}"/>
<MenuItem Header="Open" Command="{Binding OpenCommand}"/>
<Separator/>
<MenuItem Header="Save" Command="{Binding ActiveDocument.SaveCommand}"/>
<MenuItem Header="Save As..." Command="{Binding ActiveDocument.SaveAsCommand}"/>
<Separator/>
<MenuItem Header="Close" Command="{Binding ActiveDocument.CloseCommand}"/>
</MenuItem>
<MenuItem Header="Tools">
<MenuItem Header="Properties" IsChecked="{Binding FileStats.IsVisible, Mode=TwoWay}" IsCheckable="True"/>
</MenuItem>
<MenuItem Header="Layout">
<MenuItem Header="Load" Command="{Binding LoadLayoutCommand, ElementName=mainWindow}"/>
<MenuItem Header="Save" Command="{Binding SaveLayoutCommand, ElementName=mainWindow}"/>
<MenuItem Header="Dump to Console" Click="OnDumpToConsole"/>
</MenuItem>
</Menu><!--AnchorablesSource="{Binding Tools}" DocumentsSource="{Binding Files}"-->
<avalonDock:DockingManager x:Name="dockManager"
AnchorablesSource="{Binding Tools}"
DocumentsSource="{Binding Files}"
ActiveContent="{Binding ActiveDocument, Mode=TwoWay, Converter={StaticResource ActiveDocumentConverter}}"
Grid.Row="1">
<!--<avalonDock:DockingManager.Theme>
<avalonDock:AeroTheme/>
</avalonDock:DockingManager.Theme>-->
<avalonDock:DockingManager.LayoutItemTemplateSelector>
<local:PanesTemplateSelector>
<local:PanesTemplateSelector.FileViewTemplate>
<DataTemplate>
<TextBox Text="{Binding TextContent, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</local:PanesTemplateSelector.FileViewTemplate>
<local:PanesTemplateSelector.FileStatsViewTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding FileSize}"/>
<TextBlock Text="{Binding LastModified}"/>
<TextBox Text="test"/>
</StackPanel>
</DataTemplate>
</local:PanesTemplateSelector.FileStatsViewTemplate>
</local:PanesTemplateSelector>
</avalonDock:DockingManager.LayoutItemTemplateSelector>
<avalonDock:DockingManager.LayoutItemContainerStyleSelector>
<local:PanesStyleSelector>
<local:PanesStyleSelector.ToolStyle>
<Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
<!--<Setter Property="Title" Value="{Binding Model.Title}"/>-->
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
</Style>
</local:PanesStyleSelector.ToolStyle>
<local:PanesStyleSelector.FileStyle>
<Style TargetType="{x:Type avalonDock:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
<!--<Setter Property="ToolTip" Value="{Binding Model.FilePath}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"/>
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>-->
</Style>
</local:PanesStyleSelector.FileStyle>
</local:PanesStyleSelector>
</avalonDock:DockingManager.LayoutItemContainerStyleSelector>
<avalonDock:DockingManager.LayoutUpdateStrategy>
<local:LayoutInitializer/>
</avalonDock:DockingManager.LayoutUpdateStrategy>
<avalonDock:LayoutRoot>
<avalonDock:LayoutPanel Orientation="Vertical">
<avalonDock:LayoutDocumentPane/>
<avalonDock:LayoutAnchorablePane Name="ToolsPane" DockHeight="150">
</avalonDock:LayoutAnchorablePane>
</avalonDock:LayoutPanel>
</avalonDock:LayoutRoot>
</avalonDock:DockingManager>
</Grid>
Here is the ViewModel for the main window with edits I made it wont match the xaml posted here as that is it in the original form. I simply tried to replace FileViewModel with my own DockingViewModel.
class Workspace : ViewModelBase
{
protected Workspace()
{
}
static Workspace _this = new Workspace();
public static Workspace This
{
get { return _this; }
}
ObservableCollection<FileViewModel> _files = new ObservableCollection<FileViewModel>();
ReadOnlyObservableCollection<FileViewModel> _readonyFiles = null;
public ReadOnlyObservableCollection<FileViewModel> Files
{
get
{
if (_readonyFiles == null)
_readonyFiles = new ReadOnlyObservableCollection<FileViewModel>(_files);
return _readonyFiles;
}
}
//ToolViewModel[] _tools = null;
//public IEnumerable<ToolViewModel> Tools
//{
// get
// {
// if (_tools == null)
// _tools = new ToolViewModel[2];
// return _tools;
// }
//}
ObservableCollection<DockingViewModel> _tools = new ObservableCollection<DockingViewModel>();
ReadOnlyObservableCollection<DockingViewModel> _readonyTools = null;
public ReadOnlyObservableCollection<DockingViewModel> Tools
{
get
{
if (_readonyTools == null)
_readonyTools = new ReadOnlyObservableCollection<DockingViewModel>(_tools);
return _readonyTools;
}
}
FileStatsViewModel _fileStats = null;
public FileStatsViewModel FileStats
{
get
{
if (_fileStats == null)
_fileStats = new FileStatsViewModel();
return _fileStats;
}
}
#region OpenCommand
RelayCommand _openCommand = null;
public ICommand OpenCommand
{
get
{
if (_openCommand == null)
{
_openCommand = new RelayCommand((p) => OnOpen(p), (p) => CanOpen(p));
}
return _openCommand;
}
}
private bool CanOpen(object parameter)
{
return true;
}
private void OnOpen(object parameter)
{
//var dlg = new OpenFileDialog();
//if (dlg.ShowDialog().GetValueOrDefault())
//{
// var fileViewModel = Open(dlg.FileName);
// ActiveDocument = fileViewModel;
//}
}
public FileViewModel Open(string filepath)
{
var fileViewModel = _files.FirstOrDefault(fm => fm.FilePath == filepath);
if (fileViewModel != null)
return fileViewModel;
fileViewModel = new FileViewModel(filepath);
_files.Add(fileViewModel);
return fileViewModel;
}
#endregion
#region NewCommand
RelayCommand _newCommand = null;
public ICommand NewCommand
{
get
{
if (_newCommand == null)
{
_newCommand = new RelayCommand((p) => OnNew(p), (p) => CanNew(p));
}
return _newCommand;
}
}
private bool CanNew(object parameter)
{
return true;
}
private void OnNew(object parameter)
{
_tools.Add(new DockingViewModel("Hulk"));
//_files.Add(new FileViewModel());
//ActiveDocument = _files.Last();
ActiveDocument = _tools.Last();
}
#endregion
#region ActiveDocument
private DockingViewModel _activeDocument = null;
public DockingViewModel ActiveDocument
{
get { return _activeDocument; }
set
{
if (_activeDocument != value)
{
_activeDocument = value;
RaisePropertyChanged("ActiveDocument");
if (ActiveDocumentChanged != null)
ActiveDocumentChanged(this, EventArgs.Empty);
}
}
}
public event EventHandler ActiveDocumentChanged;
#endregion
internal void Close(FileViewModel fileToClose)
{
if (fileToClose.IsDirty)
{
var res = MessageBox.Show(string.Format("Save changes for file '{0}'?", fileToClose.FileName), "AvalonDock Test App", MessageBoxButton.YesNoCancel);
if (res == MessageBoxResult.Cancel)
return;
if (res == MessageBoxResult.Yes)
{
Save(fileToClose);
}
}
_files.Remove(fileToClose);
}
internal void Save(FileViewModel fileToSave, bool saveAsFlag = false)
{
if (fileToSave.FilePath == null || saveAsFlag)
{
var dlg = new SaveFileDialog();
if (dlg.ShowDialog().GetValueOrDefault())
fileToSave.FilePath = dlg.SafeFileName;
}
File.WriteAllText(fileToSave.FilePath, fileToSave.TextContent);
ActiveDocument.IsDirty = false;
}
}