WPF + Caliburn Micro + MVVM:TabItem 处理
WPF + Caliburn Micro + MVVM: TabItem handling
我正在尝试制作一个弹出窗口 window,其中包含使用 WPF、Caliburn Micro 和 MVVM 模式的 tabcontrol,在这种情况下无需使用代码隐藏。 tabcontrol 包含超过 1 个 tabitem。在 SO 中挖掘一些线程一段时间后,我结合找到的解决方案并可以创建弹出窗口 window 并用 tabcontrol 及其 tabitems 填充它(I take it from this thread)。
问题: tabitem 显示来自视图模型的内容(文本)但不显示来自视图的内容。请看这里附上的代码。
预期 我希望看到文本 "Tab Item 1" 作为 TabItem1 header 和文本 "Selection One" 作为 TabItem1 中的内容。现在 header 和 TabItems 的内容都包含相同的文本 "Tab Item 1"。
我错过了什么吗?我在这里附上代码。请随时更改代码。非常感谢任何提示。
代码序列:
- TabItem1、TabItem2 视图和视图模型
- ITabItem
- PopUp window 视图和视图模型
- AppBootstrapper,Shell 视图和视图模型
TabItem1ViewModel(TabItem2ViewModel 具有相同的内容)
public class TabItem1ViewModel : Screen, ITabItem
{
public TabItem1ViewModel() => DisplayName = "Tab Item 1";
}
注意: 在下面的 TabItem 视图中,我使用 Label 来显示文本 "Selection One",但该文本根本没有出现。只有视图模型中定义的显示名称 "Tab Item 1" 显示为 TabItem1
的内容
TabItem1View(TabItem2View 内容相同)
<UserControl
x:Class="CmTabControl.Views.TabItem1View"
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"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<TabItem x:Name="TabItem1" Header="{Binding Path=DisplayName}">
<Grid x:Name="TabItem1ContentGrid">
<Label HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Selection One" />
</Grid>
</TabItem>
</Grid>
</UserControl>
ITabItem(没错,就是空界面)
public interface ITabItem : IScreen
{
}
PopUpViewModel
public class PopUpViewModel : Screen
{
public PopUpViewModel()
{
TabItems.Add(new TabItem1ViewModel());
TabItems.Add(new TabItem2ViewModel());
}
private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
public BindableCollection<ITabItem> TabItems
{
get => _tabItems;
set
{
if (_tabItems == null)
{
return;
}
_tabItems.Clear();
_tabItems.AddRange(value);
NotifyOfPropertyChange(() => TabItems);
}
}
}
PopUpView
<Window
x:Class="CmTabControl.Views.PopUpView"
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:local="clr-namespace:CmTabControl.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="PopUpView"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid Margin="3,8,3,3" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabControl x:Name="TabItems" />
</Grid>
</Window>
AppBootstrapper
public class AppBootstrapper : BootstrapperBase
{
private readonly SimpleContainer _container = new SimpleContainer();
public AppBootstrapper() => Initialize();
protected override object GetInstance(Type serviceType, string key) => _container.GetInstance(serviceType, key);
protected override IEnumerable<object> GetAllInstances(Type serviceType) => _container.GetAllInstances(serviceType);
protected override void BuildUp(object instance) => _container.BuildUp(instance);
protected override void Configure()
{
base.Configure();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<ShellViewModel>();
_container.PerRequest<PopUpViewModel>(); // Or Singleton if there'll only ever be one
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
}
ShellViewModel
public class ShellViewModel : Conductor<object>.Collection.AllActive
{
private IWindowManager _windowManager;
public ShellViewModel(PopUpViewModel popUpVm)
{
DisplayName = "Shell Window";
PopUpViewModel = popUpVm;
}
public PopUpViewModel PopUpViewModel { get; set; }
public sealed override void ActivateItem(object item) => base.ActivateItem(item);
public void OpenPopUp()
{
ActivateItem(PopUpViewModel);
if (_windowManager == null) _windowManager = new WindowManager();
_windowManager.ShowDialog(PopUpViewModel, null, null);
}
public sealed override string DisplayName { get; set; }
}
Shell查看
<UserControl
x:Class="CmTabControl.Views.ShellView"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Width="300" Height="300"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="OpenPopUp" Width="100" Height="35"
Content="Open Popup" />
</Grid>
</UserControl>
已添加: 实时可视化树的屏幕截图。
我找到了一个使用模板的解决方案:
PopUpViewModel
添加 SelectedTab
:
public sealed class PopUpViewModel : Screen
{
private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
private IScreen _selectedTab;
public PopUpViewModel()
{
DisplayName = "Popup";
TabItems.Add(new TabItem1ViewModel());
TabItems.Add(new TabItem2ViewModel());
SelectedTab = TabItems.FirstOrDefault();
}
public BindableCollection<ITabItem> TabItems
{
get => _tabItems;
set
{
if(_tabItems == null)
return;
_tabItems.Clear();
_tabItems.AddRange(value);
NotifyOfPropertyChange(() => TabItems);
}
}
public IScreen SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
NotifyOfPropertyChange();
}
}
}
PopUpView
:
<Grid Margin="3, 8, 3, 3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabControl ItemsSource="{Binding TabItems}"
SelectedItem="{Binding SelectedTab,
UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
现在您只需将 TabItem 内容添加到您的页面,TabItem1View
:
<UserControl x:Class="WpfTestApp.Views.Tabs.TabItem1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfTestApp.Views.Tabs"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid x:Name="TabItem1ContentGrid">
<Label HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Selection One" />
</Grid>
</UserControl>
编辑:
SelectedTab 就在那里,因此默认选择第一个选项卡。
我正在尝试制作一个弹出窗口 window,其中包含使用 WPF、Caliburn Micro 和 MVVM 模式的 tabcontrol,在这种情况下无需使用代码隐藏。 tabcontrol 包含超过 1 个 tabitem。在 SO 中挖掘一些线程一段时间后,我结合找到的解决方案并可以创建弹出窗口 window 并用 tabcontrol 及其 tabitems 填充它(I take it from this thread)。
问题: tabitem 显示来自视图模型的内容(文本)但不显示来自视图的内容。请看这里附上的代码。
预期 我希望看到文本 "Tab Item 1" 作为 TabItem1 header 和文本 "Selection One" 作为 TabItem1 中的内容。现在 header 和 TabItems 的内容都包含相同的文本 "Tab Item 1"。
我错过了什么吗?我在这里附上代码。请随时更改代码。非常感谢任何提示。
代码序列:
- TabItem1、TabItem2 视图和视图模型
- ITabItem
- PopUp window 视图和视图模型
- AppBootstrapper,Shell 视图和视图模型
TabItem1ViewModel(TabItem2ViewModel 具有相同的内容)
public class TabItem1ViewModel : Screen, ITabItem
{
public TabItem1ViewModel() => DisplayName = "Tab Item 1";
}
注意: 在下面的 TabItem 视图中,我使用 Label 来显示文本 "Selection One",但该文本根本没有出现。只有视图模型中定义的显示名称 "Tab Item 1" 显示为 TabItem1
的内容TabItem1View(TabItem2View 内容相同)
<UserControl
x:Class="CmTabControl.Views.TabItem1View"
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"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<TabItem x:Name="TabItem1" Header="{Binding Path=DisplayName}">
<Grid x:Name="TabItem1ContentGrid">
<Label HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Selection One" />
</Grid>
</TabItem>
</Grid>
</UserControl>
ITabItem(没错,就是空界面)
public interface ITabItem : IScreen
{
}
PopUpViewModel
public class PopUpViewModel : Screen
{
public PopUpViewModel()
{
TabItems.Add(new TabItem1ViewModel());
TabItems.Add(new TabItem2ViewModel());
}
private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
public BindableCollection<ITabItem> TabItems
{
get => _tabItems;
set
{
if (_tabItems == null)
{
return;
}
_tabItems.Clear();
_tabItems.AddRange(value);
NotifyOfPropertyChange(() => TabItems);
}
}
}
PopUpView
<Window
x:Class="CmTabControl.Views.PopUpView"
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:local="clr-namespace:CmTabControl.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="PopUpView"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid Margin="3,8,3,3" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabControl x:Name="TabItems" />
</Grid>
</Window>
AppBootstrapper
public class AppBootstrapper : BootstrapperBase
{
private readonly SimpleContainer _container = new SimpleContainer();
public AppBootstrapper() => Initialize();
protected override object GetInstance(Type serviceType, string key) => _container.GetInstance(serviceType, key);
protected override IEnumerable<object> GetAllInstances(Type serviceType) => _container.GetAllInstances(serviceType);
protected override void BuildUp(object instance) => _container.BuildUp(instance);
protected override void Configure()
{
base.Configure();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<ShellViewModel>();
_container.PerRequest<PopUpViewModel>(); // Or Singleton if there'll only ever be one
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
}
ShellViewModel
public class ShellViewModel : Conductor<object>.Collection.AllActive
{
private IWindowManager _windowManager;
public ShellViewModel(PopUpViewModel popUpVm)
{
DisplayName = "Shell Window";
PopUpViewModel = popUpVm;
}
public PopUpViewModel PopUpViewModel { get; set; }
public sealed override void ActivateItem(object item) => base.ActivateItem(item);
public void OpenPopUp()
{
ActivateItem(PopUpViewModel);
if (_windowManager == null) _windowManager = new WindowManager();
_windowManager.ShowDialog(PopUpViewModel, null, null);
}
public sealed override string DisplayName { get; set; }
}
Shell查看
<UserControl
x:Class="CmTabControl.Views.ShellView"
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"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid Width="300" Height="300"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="OpenPopUp" Width="100" Height="35"
Content="Open Popup" />
</Grid>
</UserControl>
已添加: 实时可视化树的屏幕截图。
我找到了一个使用模板的解决方案:
PopUpViewModel
添加 SelectedTab
:
public sealed class PopUpViewModel : Screen
{
private readonly BindableCollection<ITabItem> _tabItems = new BindableCollection<ITabItem>();
private IScreen _selectedTab;
public PopUpViewModel()
{
DisplayName = "Popup";
TabItems.Add(new TabItem1ViewModel());
TabItems.Add(new TabItem2ViewModel());
SelectedTab = TabItems.FirstOrDefault();
}
public BindableCollection<ITabItem> TabItems
{
get => _tabItems;
set
{
if(_tabItems == null)
return;
_tabItems.Clear();
_tabItems.AddRange(value);
NotifyOfPropertyChange(() => TabItems);
}
}
public IScreen SelectedTab
{
get => _selectedTab;
set
{
_selectedTab = value;
NotifyOfPropertyChange();
}
}
}
PopUpView
:
<Grid Margin="3, 8, 3, 3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TabControl ItemsSource="{Binding TabItems}"
SelectedItem="{Binding SelectedTab,
UpdateSourceTrigger=PropertyChanged}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
现在您只需将 TabItem 内容添加到您的页面,TabItem1View
:
<UserControl x:Class="WpfTestApp.Views.Tabs.TabItem1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfTestApp.Views.Tabs"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid x:Name="TabItem1ContentGrid">
<Label HorizontalAlignment="Left"
VerticalAlignment="Top"
Content="Selection One" />
</Grid>
</UserControl>
编辑:
SelectedTab 就在那里,因此默认选择第一个选项卡。