Reputation: 509
first attempt at MVVM and WPF (steep learning curve). In my ViewModel I want to run the following code to add a "layoutDocument" which is an AvalonDock layout into my Mainform UI.
ViewModel class:
LayoutDocument layoutDocument = new LayoutDocument { Title = "Plan Layout" };
Window mainWindow = Application.Current.Windows.OfType<Window>().Where(x => x.Name == "MainWindow").FirstOrDefault();
if (mainWindow != null)
{
mainWindow.mainPanel.Children.Add(layoutDocument);
}
The above code gives me the following error:
"'Window' does not contain definition for 'mainPanel' and no extension method for 'mainPanel'".
Note in my XAML below that "LayoutDocumentPane" does contain a name "mainPanel".
I have tried adding the above code directly into my MainForm View Class (excluding the Application.Current.Windows.OfType and If statement bit) and just including the: mainPanel.Children.Add(layoutDocument); And it works fine (a new layout is created in my MainForm when I click the button).
However, as I want to stickto MVVM this is not a suitable solution.
How can I add "layoutDocument" to MainWindow from ViewModel? Thanks in advance.
An extract of my XAML looks like this:
<Window x:Class="LiveExplorer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LiveExplorer"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:vm="clr-namespace:WpfApp1.ViewModel">
<Grid> etc etc etc here---
<xcad:LayoutDocumentPaneGroup>
<xcad:LayoutDocumentPane x:Name="mainPanel">
<xcad:LayoutDocument ContentId="document1" Title="Document 1" >
<Button Content="Document 1 Content" HorizontalAlignment="Center" VerticalAlignment="Center"
Command="{Binding NewPlanCommand, Source={StaticResource viewModel}}"
/>
</xcad:LayoutDocument>
<xcad:LayoutDocument ContentId="document2" Title="Document 2">
<TextBox Text="Document 2 Content" AcceptsReturn="True"/>
</xcad:LayoutDocument>
</xcad:LayoutDocumentPane>
</xcad:LayoutDocumentPaneGroup >
Whilst the accepted answer does not answer the question in terms of MMVM, it does correct the coding error.
Upvotes: 0
Views: 2632
Reputation: 169200
This is not related to MVVM but to be able access the mainPanel
you need to cast the returned Window
to a MainWindow
:
MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
{
mainWindow.mainPanel.Children.Add(layoutDocument);
}
A view model shouldn't access any window directly though. This breaks the MVVM pattern.
Upvotes: 0
Reputation: 6458
What you've tried to implement does not follow the MVVM pattern. You need to take care of 3 things to get started:
ViewModels:
Create a viewmodel that will be binded to your MainWindow and create an observable collection inside that MainWindowViewModel that contains a list of object that will contain data that can be used in the UI:
public ObservableCollection<LayoutDocumentViewModel> LayoutDocument {get;set;}
Make sure that both the MainWindowViewModel and the LayoutDocumentViewModel inherits from INotifyPropertyChanged(Implement Property Change Notification) or if you use MVVMLight (or similar) from ViewModelBase.
The LayoutDocumentViewModel is just a ViewModel that will be used to store information about your layout document and that can be binded to the UI.
public LayoutDocumentViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName]
string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
}
I would strongly recommend that you use MVVMLight (or similar) or put the INotifyPropertyChange code into a base class i.e. ViewModelBase for example.
For simplicity sake in this example, I'm initializing the observable collection and creating a couple of document layouts objects directly in the MainWindowViewModel but you'll need to research this further and find out where it is appropriate for you to initialize and/or create these.
public MainPageViewModel() { DocumentLayouts = new ObservableCollection(); DocumentLayouts.Add(new DocumentLayout {Name="Layout1"}); DocumentLayouts.Add(new DocumentLayout {Name="Layout2"}); }
The above takes care of creating your MainWindowViewModel and layout documents.
Initializing MainViewModel (and binded to the MainWindow.xaml). Note this is a quick and dirty way to get you started and you should really look into IoC containers.
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
Finally, bind your ViewModel & UI
XAML:
<Grid>
<ItemsControl ItemsSource="{Binding LayoutDocuments}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Note: Just replace the Label by your LayoutDocument control and bind it to the relevant element properties you have declared in LayoutDocumentViewModel.
Hope this helps get you started.
Upvotes: 3