C# WPF Caliburn.Micro和TabControl与UserControls问题

C# WPF Caliburn.Micro和TabControl与UserControls问题,c#,wpf,mvvm,tabcontrol,caliburn.micro,C#,Wpf,Mvvm,Tabcontrol,Caliburn.micro,我很确定这个问题在什么地方得到了答案,但我似乎一辈子都找不到答案 我尝试使用TabControl在UserControls之间切换(每个选项卡都不同,所以不使用项) 以下是分类: 我有我的主视图和3个用户控件。Mainview有一个选项卡控件-每个选项卡应显示不同的用户控件 我可以使用 但是它不绑定到viewmodel,只绑定到视图 所以我在我的虚拟机中使用导体,并激活它们。这就是它开始变得奇怪/令人沮丧的地方。应用程序开始时选择了Tab0,但选择了Tab2(最后一个选项卡)内容。单击任何其他选

我很确定这个问题在什么地方得到了答案,但我似乎一辈子都找不到答案

我尝试使用TabControl在UserControls之间切换(每个选项卡都不同,所以不使用项)

以下是分类: 我有我的主视图和3个用户控件。Mainview有一个选项卡控件-每个选项卡应显示不同的用户控件

我可以使用 但是它不绑定到viewmodel,只绑定到视图

所以我在我的虚拟机中使用导体,并激活它们。这就是它开始变得奇怪/令人沮丧的地方。应用程序开始时选择了Tab0,但选择了Tab2(最后一个选项卡)内容。单击任何其他选项卡,为该选项卡加载正确的ViewModel。单击返回到选项卡0,并在那里加载正确的内容

我怎样才能阻止这一切?另外,如果切换选项卡不重新初始化viewmodel,清除已经输入的字段,我真的很希望这样

不管怎么说,这是我的一些资料,在我弄坏我的鼠标之前,我将把它放在这里,做一些其他的事情

视图:


以及ViewModel:

class MainViewModel : Conductor<object>
{
    RemoteInfoViewModel remoteInfo = new RemoteInfoViewModel();
    RemoteToolsViewModel remoteTools = new RemoteToolsViewModel();
    CHRemoteViewModel chRemote = new CHRemoteViewModel();

    public MainViewModel()
    {
        ActivateItem(remoteInfo);
    }

    public void LoadRemoteInfo()
    {
        ActivateItem(remoteInfo);
    }

    public void LoadRemoteTools()
    {
        ActivateItem(remoteTools);
    }

    public void LoadCHRemote()
    {
        ActivateItem(chRemote);
    }
}
class MainView模型:导体
{
RemoteInfoViewModel remoteInfo=新的RemoteInfoViewModel();
RemoteToolsViewModel remoteTools=新的RemoteToolsViewModel();
CHRemoteViewModel chRemote=新的CHRemoteViewModel();
公共主视图模型()
{
激活项(远程信息);
}
public void LoadRemoteInfo()
{
激活项(远程信息);
}
公共void LoadRemoteTools()
{
ActivateItem(远程工具);
}
公共void LoadCHRemote()
{
激活肽(chRemote);
}
}

我可以建议一条不同的路线吗

这是我在master details场景中成功完成的事情。假设您有一组子视图模型。我将为所有这些项目准备一个标记接口,当然,如果存在跨越所有子视图模型的此类方法,您可以添加您认为合适的属性/方法:

public interface IMainScreenTabItem : IScreen
{
}
public sealed class ChRemoteViewModel : Screen, IMainScreenTabItem
{
    public ChRemoteViewModel()
    {
        DisplayName = "CH Remote";
    }
}

public sealed class PcInfoViewModel : Screen, IMainScreenTabItem
{
    public PcInfoViewModel()
    {
        DisplayName = "PC Info";
    }
}

public sealed class RemoteToolsViewModel : Screen, IMainScreenTabItem
{
    public RemoteToolsViewModel()
    {
        DisplayName = "Remote Tools";
    }
}
您可以非常确定希望所有子模型都是
Screen
s(或者,在嵌套场景中是
Conductor
s)。它使它们具有完整的初始化/激活/停用周期

然后,子视图模型:

public interface IMainScreenTabItem : IScreen
{
}
public sealed class ChRemoteViewModel : Screen, IMainScreenTabItem
{
    public ChRemoteViewModel()
    {
        DisplayName = "CH Remote";
    }
}

public sealed class PcInfoViewModel : Screen, IMainScreenTabItem
{
    public PcInfoViewModel()
    {
        DisplayName = "PC Info";
    }
}

public sealed class RemoteToolsViewModel : Screen, IMainScreenTabItem
{
    public RemoteToolsViewModel()
    {
        DisplayName = "Remote Tools";
    }
}
DisplayName
将显示为标题文本。将这些类密封是一个很好的做法,因为
DisplayName
是一个虚拟属性,在未密封的类的构造函数中调用虚拟方法是一个很大的禁忌

然后,您可以添加相应的视图并设置所选的IoC容器注册-您必须将所有子视图模型注册为实现
IMainScreenTabItem
的类,然后:

public class MainViewModel : Conductor<IMainScreenTabItem>.Collection.OneActive
{
    public MainViewModel(IEnumerable<IMainScreenTabItem> tabs)
    {
        Items.AddRange(tabs);
    }
}
而且它很有效。如果您的子视图模型具有多个依赖项(例如数据库访问、记录器、验证机制等),这也是一个非常好且方便的解决方案,现在您可以让IoC完成所有繁重的工作,而不是手动实例化它们

不过,这里有一件事:选项卡将按照注入类的相同顺序放置。如果您想控制排序,可以在
MainViewModel
构造函数中排序,方法是传递自定义
IComparer
或添加一些属性,您可以
OrderBy
或选择
IMainScreenTabItem
界面。默认选中的项目将是
项目
列表中的第一个项目

另一个选项是使
MainViewModel
采用三个参数:

public MainViewModel(ChRemoteViewModel chRemoteViewModel, PcInfoViewModel pcInfo, RemoteToolsViewModel remoteTools)
{
    // Add the view models above to the `Items` collection in any order you see fit
}
虽然当你有2-3个以上的子视图模型时(你可以很容易地得到更多),它会很快变得混乱

关于“清理”部分。IoC创建的视图模型符合常规生命周期:它们最多初始化一次(
OnInitialize
),然后在每次导航到
OnDeactivate(bool)
时停用,并在导航到(
OnActivate
)时激活。
OnDeactivate
中的
bool
参数指示视图模型是刚刚停用还是完全“关闭”(例如,当您关闭对话框窗口并导航离开时)。如果完全关闭视图模型,它将在下次显示时重新初始化

这意味着任何绑定的数据都将在激活的
调用之间保留,您必须在
OnDeactivate
中明确清除它。更重要的是,如果您保持对子视图模型的强引用,那么即使在调用
OnDeactivate(true)
,数据在下次初始化时仍将在那里-这是因为IoC注入的视图模型只创建一次(除非您以
Func
的形式注入工厂函数),然后根据需要初始化/激活/停用


编辑 关于引导程序,我不太确定您使用的是哪种IoC容器。我的示例使用,但您也可以同样轻松地使用Autofac:

public class AppBootstrapper : Bootstrapper<MainViewModel>
{
    private Container container;

    /// <summary>
    /// Override to configure the framework and setup your IoC container.
    /// </summary>
    protected override void Configure()
    {
        container = new Container();
        container.Register<IWindowManager, WindowManager>();
        container.Register<IEventAggregator, EventAggregator>();
        var viewModels =
            Assembly.GetExecutingAssembly()
                .DefinedTypes.Where(x => x.GetInterface(typeof(IMainScreenTabItem).Name) != null && !x.IsAbstract && x.IsClass);
        container.RegisterAll(typeof(IMainScreenTabItem), viewModels);
        container.Verify();
    }

    /// <summary>
    /// Override this to provide an IoC specific implementation.
    /// </summary>
    /// <param name="service">The service to locate.</param><param name="key">The key to locate.</param>
    /// <returns>
    /// The located service.
    /// </returns>
    protected override object GetInstance(Type service, string key)
    {
        if (service == null)
        {
            var typeName = Assembly.GetExecutingAssembly().DefinedTypes.Where(x => x.Name.Contains(key)).Select(x => x.AssemblyQualifiedName).Single();

            service = Type.GetType(typeName);
        }
        return container.GetInstance(service);
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return container.GetAllInstances(service);
    }

    protected override void BuildUp(object instance)
    {
        container.InjectProperties(instance);
    }
}
公共类AppBootstrapper:Bootstrapper
{
私人货柜;
/// 
///重写以配置框架并设置IoC容器。
/// 
受保护的覆盖无效配置()
{
容器=新容器();
container.Register();
container.Register();
var视图模型=
Assembly.getExecutionGassembly()
.DefinedTypes.Where(x=>x.GetInterface(typeof(IMainScreenTabItem.Name)!=null&&!x.isastract&&x.IsClass);
container.registeral(typeof(IMainScreenTabItem),viewModels);
container.Verify();
}
/// 
///覆盖此选项以提供特定于IoC的实现。