I'm kind of newbie in WPF&MVVM&Caliburn so I beg your indulgence :)
I have a problem with binding TabControl with dynamically created models. Tabcontrol is being created correctly but changing tab is not switching viewmodel used to bind "view" (I'm using viewmodel first approach)
I've made my solution basing on this question: WPF Caliburn.Micro and TabControl with UserControls issue
This is my model definition:
public interface IMainScreenTabItem : IScreen
{
}
public class MainViewTestTabsViewModel : Conductor<IMainScreenTabItem>.Collection.OneActive
{
public MainViewTestTabsViewModel(IEnumerable<IMainScreenTabItem> tabs)
{
Items.Add(new ViewTabModel("Foo1"));
Items.Add(new ViewTabModel("Foo2"));
Items.AddRange(tabs);
}
}
public sealed class ViewTabModel : Screen, IMainScreenTabItem
{
public ViewTabModel(string displayName)
{
DisplayName = displayName;
}
}
And here is the view MainViewTestTabsView:
<UserControl x:Class="TestWpfApp.Views.MainViewTestTabsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApp.Views"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:viewModels="clr-namespace:TestWpfApp.ViewModels"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d" Width="500" Height="500">
<Grid>
<TabControl Name="Items">
<TabControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<Label cal:Bind.Model="{Binding}" x:Name="DisplayName" Height="200" Width="200" />
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
What I want to achieve - is to have TabControl with many tabs. Each tab has the same "view" (declared in DataTemplate) but to bind this view I want to use different viewModels (to be specific - the same model class [ViewTabModel ] but with different data)
The size of tabs is to be declared at runtime as well as the data, which should be in the ViewTabModel model.
In example below - I have two tabs, but changing them is not changing the Label (i have all the Time: "Foo1" Label, even if I click "Foo2" tab)
I use caliburn.micro as a framework - with autofac bootstrap (if it matters) And I use propertyChanged.Fody (https://github.com/Fody/PropertyChanged) to omit all the propertychanged stuff in viewmodels.
What am I doing wrong?
=== UPDATE ===
Attaching minimal reproduction solution:
https://wetransfer.com/downloads/0b909bfd31a588dda99655f366eddad420170801192103/1d094a
Plese, help! :)
=== UPDATE 2 ===
Is anything unclear about my question ?:) Still no comments, no anwsers event with bounty on it.
=== UPDATE 3 ===
I've already posted COMPLETE view page (xaml) and COMPLETE model code (this is only this)
I'm posting also AppBoostraper.cs and AppWindowManager.cs (but i suppose it is irrelevat here)
AppBoostrapper.cs
using Autofac;
using TestWpfApp.ViewModels;
namespace TestWpfApp {
using System;
using System.Collections.Generic;
using Caliburn.Micro;
public class AppBootstrapper : CaliburnMetroAutofacBootstrapper<MainViewTestTabsViewModel>
{
protected override void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<AppWindowManager>().As<IWindowManager>().SingleInstance();
var assembly = typeof(ShellViewModel).Assembly;
builder.RegisterAssemblyTypes(assembly)
.Where(item => item.Name.EndsWith("ViewModel") && item.IsAbstract == false)
.AsSelf()
.SingleInstance();
}
}
}
It is inheriting CaliburnMetroAutofacContainer (https://github.com/ziyasal/Caliburn.Metro)
AppWindowsManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Caliburn.Metro.Core;
using MahApps.Metro.Controls;
namespace TestWpfApp
{
public class AppWindowManager : MetroWindowManager
{
public override MetroWindow CreateCustomWindow(object view, bool windowIsView)
{
if (windowIsView)
{
return view as ShellView;
}
return new ShellView
{
Content = view
};
}
}
}
=== UPDATE 4 === Apprently, changing control from:
cal:Bind.Model="{Binding}" x:Name="DisplayName"
to:
Content="{Binding DisplayName}"
Did the work. Although i'm not quite sure why?
Now i want to do exactly the same. Only this time i want my view to be binded. So ViewModel is exactly the same. But this time:
MainViewTestTabsView
<UserControl x:Class="TestWpfApp.Views.MainViewTestTabsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApp.Views"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:viewModels="clr-namespace:TestWpfApp.ViewModels"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d" Width="500" Height="500">
<Grid>
<TabControl Name="Items">
<TabControl.ContentTemplate>
<DataTemplate>
<StackPanel>
<local:ViewTab cal:Bind.Model="{Binding}" />
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
and ViewTab view is :
<UserControl x:Class="TestWpfApp.Views.ViewTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfApp.Views"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:viewModels="clr-namespace:TestWpfApp.ViewModels"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d" Width="300" Height="300">
<Grid>
<StackPanel>
<Label x:Name="DisplayName"></Label>
</StackPanel>
</Grid>
=== Update 5 => Happy final === I've been sugested I should stick to the ViewModel first convention (as i declared i'm using) and my attempts was somhow view first. So i've changed it to:
<ContentControl cal:View.Model="{Binding ActiveItem}" />
But nothing is rendered then
If i declare it like this :
<ContentControl cal:View.Model="{Binding}" />
There is only message saying : "Cannot find view for : [my_namspece].ViewTabModel It was weird and get me thinking. Maybe i'm not sticking to the convention. And it was true...
My model was called:
ViewTabModel
Whereas it should be :
ViewTabViewModel
exactly the same thing with view. It should be called:
ViewTabView.xaml
After that, such construction:
<ContentControl cal:View.Model="{Binding}" />
Is working correctly!! Thank you arcticwhite and grek40 for leading me to this solution