WPF Caliburn.Micro和TabControl - 更改选项卡,而不是更改模型

时间:2017-07-30 21:43:57

标签: c# wpf mvvm tabcontrol caliburn.micro

我是WPF&MVVM&Caliburn的新手,所以我请你放纵:)

我在使用动态创建的模型绑定TabControl时遇到问题。 Tabcontrol正在正确创建,但更改选项卡不会切换用于绑定" view"的视图模型。 (我使用viewmodel第一种方法)

我已根据这个问题制定了解决方案:WPF Caliburn.Micro and TabControl with UserControls issue

这是我的模型定义:

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;
    }
}

以下是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>

我想要实现的目标是让TabControl具有多个选项卡。每个标签都有相同的&#34;视图&#34; (在DataTemplate中声明)但是要绑定此视图我想使用不同的viewModel(具体 - 相同的模型类[ViewTabModel]但具有不同的数据)

选项卡的大小将在运行时以及数据中声明,这些数据应该在ViewTabModel模型中。

在下面的示例中 - 我有两个选项卡,但更改它们不会更改标签(我有所有时间:&#34; Foo1&#34;标签,即使我点击&#34; Foo2&#34;标签)

我使用caliburn.micro作为框架 - 使用autofac bootstrap(如果重要的话) 我使用propertyChanged.Fody(https://github.com/Fody/PropertyChanged)来省略viewmodels中的所有propertychanged东西。

我做错了什么?

=== UPDATE ===

附加最小的复制解决方案:

https://wetransfer.com/downloads/0b909bfd31a588dda99655f366eddad420170801192103/1d094a

请尽快帮忙! :)

===更新2 ===

关于我的问题有什么不清楚的地方吗?:)仍然没有评论,没有关于它的赏金事件。

===更新3 ===

我已经发布了COMPLETE视图页面(xaml)和COMPLETE模型代码(这只是这个)

我还发布了AppBoostraper.cs和AppWindowManager.cs(但我认为这是无关紧要的)

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();
        }
    }
}

继承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
            };
        }
    }
}

===更新4 === 显然,改变控制:

  

CAL:Bind.Model =&#34; {结合}&#34; X:名称=&#34; DisplayName的&#34;

为:

  

内容=&#34; {Binding DisplayName}&#34;

做了什么工作。虽然我不太清楚为什么?

现在我想做同样的事情。只有这次我希望我的视图被绑定。所以ViewModel完全一样。但这一次:

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>

和ViewTab视图是:

<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>

===更新5 =&gt;快乐的决赛=== 我已经被吸食了,我应该坚持使用ViewModel的第一个约定(因为我宣称我正在使用),我的尝试首先是somhow视图。所以我把它改成了:

<ContentControl cal:View.Model="{Binding ActiveItem}" />

但是没有任何东西呈现

如果我这样声明:

<ContentControl cal:View.Model="{Binding}" />

只有消息说:&#34;找不到以下视图:[my_namspece] .ViewTabModel 这很奇怪,让我思考。也许我不坚持这个惯例。这是真的......

我的模型被称为:

  

ViewTabModel

它应该是:

  

ViewTabViewModel

与视图完全相同。它应该被称为:

  

ViewTabView.xaml

之后,这样的结构:

<ContentControl cal:View.Model="{Binding}" />

工作正常!!感谢arcticwhite和grek40带领我找到这个解决方案

2 个答案:

答案 0 :(得分:3)

Okay... I have already worked with Caliburn.Micro, so I can say I have some experience, not a pro, but I manage to make it working.

Your MainViewTestTabsViewModel.cs:

 public interface IMainScreenTabItem : IScreen
    {
    }

    public class MainViewTestTabsViewModel : Conductor<IMainScreenTabItem>.Collection.OneActive
    {
        public MainViewTestTabsViewModel(IEnumerable<IMainScreenTabItem> tabs)
        {

            Items.Add(new ViewTabModel() {DisplayName = "Test"});
            Items.Add(new ViewTabModel() { DisplayName = "Test2" });
            Items.Add(new ViewTabModel() { DisplayName = "Test3" });
            Items.AddRange(tabs);
        }
    }

    public class ViewTabModel : Screen, IMainScreenTabItem
    {
        public ViewTabModel()
        {

        }
    }

And your MainViewTestTabsView.xaml

    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.ViewModels"
    xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
   xmlns:viewModels="clr-namespace:TestWpfApp.Views"
    xmlns:cal="http://www.caliburnproject.org"
    mc:Ignorable="d" Width="500" Height="500">
    <Grid>
        <TabControl x:Name="Items" >
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Label cal:Bind.ModelWithoutContext="{Binding}" x:Name="DisplayName" Height="200" Width="200"/>
                    </StackPanel>
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
    </UserControl>

P.S. Why I removed your displayName variable in constructor... Because you don't need it, it's already in the Caliburn:Micro.Screen as a property.

Edit #2 The convention will work, just add cal:Bind.ModelWithoutContext="{Binding}" inside your Label (Edited the answer).

答案 1 :(得分:1)

现在我有时间测试您的示例项目......正如我评论的那样,您应该选择正确的绑定类型......

All About Actions开始,我猜会有其他文档来源,列出相同的基本信息:

  
      
  • Bind.Model - View-First - 将Action.Target和DataContext属性设置为指定的实例。适用约定   到了视野。字符串值用于解析来自的实例   IoC容器。 (在Window / UserControl / Page等根节点上使用。)
  •   
  • Bind.ModelWithoutContext - View-First - 将Action.Target设置为指定的实例。将约定应用于视图。 (使用   在DataTemplate中。)
  •   
  • View.Model - ViewModel-First - 找到指定VM实例的视图,并将其注入内容站点。设置VM   到Action.Target和DataContext。适用于的约定   图。
  •   

如上所述,我不是校准专家,所以我不得不尝试...第二个选项对我来说最好(“在DataTemplate中使用”),所以这里是工作结果:

<Label  cal:Bind.Model="{Binding}" x:Name="DisplayName" Height="200" Width="200" />

替换为

<Label cal:Bind.ModelWithoutContext="{Binding}" x:Name="DisplayName" Height="200" Width="200" />

及其工作。

实际上,我建议在周围的堆栈面板中引入模型(datatemplate root)

<StackPanel cal:Bind.ModelWithoutContext="{Binding}">
    <Label x:Name="DisplayName" Height="200" Width="200" />
</StackPanel>