加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – WPF Caliburn.Micro和TabControl与UserControls问题

发布时间:2020-12-15 06:53:50 所属栏目:百科 来源:网络整理
导读:我很确定这已经在某个地方得到回答,但我似乎找不到我的生活. 我试图使用TabControl在UserControls之间切换(每个选项卡是不同的,所以不使用Items) 以下是故障: 我有我的主视图和3个用户控件. Mainview有一个选项卡控件 – 每个选项卡应显示不同的用户控件.
我很确定这已经在某个地方得到回答,但我似乎找不到我的生活.

我试图使用TabControl在UserControls之间切换(每个选项卡是不同的,所以不使用Items)

以下是故障:
我有我的主视图和3个用户控件. Mainview有一个选项卡控件 – 每个选项卡应显示不同的用户控件.

我可以很容易地将tabcontrol contect设置为用户控件使用

但是它不是绑定到viewmodel,只是视图.

所以我在我的VM和ActivateItem中使用Conductor.这是开始变得奇怪/令人沮丧的地方.应用程序从Tab0选择开始,但是Tab2(最后一个选项卡)的内容.单击任何其他选项卡,为该选项卡加载正确的ViewModel.点击回到Tab0,加载正确的内容.

如何让它停止?另外,如果切换选项卡不会再次重新初始化视图模型,清除已经输入的字段,我真的很喜欢它.

无论如何,这里是我的一些资料来源,我打算把这个放在这里,然后再打破我的鼠标,再尝试其他的东西.

视图:

<TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row ="1">
        <TabItem Header="PC Information">
            <Grid>
                <ContentControl x:Name="LoadRemoteInfo" cal:View.Model="{Binding ActiveItem}"/>
            </Grid>
        </TabItem>
        <TabItem Header="Remote Tools">
            <Grid>
                <ContentControl x:Name="LoadRemoteTools" cal:View.Model="{Binding ActiveItem}"/>
            </Grid>
        </TabItem>
        <TabItem Header="CHRemote">
            <Grid>
                <ContentControl x:Name="LoadCHRemote" cal:View.Model="{Binding ActiveItem}"/>
            </Grid>
        </TabItem>

    </TabControl>

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

解决方法

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

这是我在大师细节场景中成功的事情.假设你有一个子视图模型的集合.我将为所有这些项目准备一个标记界面,当然,如果有这样的方法跨越所有子视图模型,您可以添加您认为合适的属性/方法:

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是一个虚拟属性,并且在没有密封的类的构造函数中调用虚拟方法是一个很大的no-no.

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

public class MainViewModel : Conductor<IMainScreenTabItem>.Collection.OneActive
{
    public MainViewModel(IEnumerable<IMainScreenTabItem> tabs)
    {
        Items.AddRange(tabs);
    }
}

MainView.xaml只是:

<TabControl Name="Items"/>

它只是工作.如果您的子视图模型具有多个依赖关系(例如数据库访问,记录器,验证机制等),那么它也是非常好的和方便的解决方案,现在您可以让IoC做所有的重大工作,而不是手动实例化.

有一件事情在这里:标签将按照注册类的顺序放置.如果您想要控制订单,可以通过传递自定义IComparer< IMainScreenTabItem>在MainViewModel构造函数中对它们进行排序.或添加一些属性您可以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参数指示视图模型是否刚刚停用或完全“关闭”(例如,当您关闭对话窗口并导航时).如果您完全关闭视图模型,则会在下一次显示时重新初始化.

这意味着在OnActivate调用之间保留任何绑定的数据,您必须在OnDeactivate中显式清除它.此外,如果您保持对子视图模型的强烈引用,则即使在调用OnDeactivate(true)之后,数据仍将在下次初始化时存在 – 这是因为IoC注入视图模型已创建一次(除非您注入工厂以Func< YourViewModel>形式的功能),然后根据需要初始化/激活/停用.

编辑

关于bootstrapper,我不太清楚你正在使用什么样的IoC容器.我的样品使用了SimpleInjector,但是你可以像这样简单地做同样的事情. 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);
    }
}

注意配置中的viewModels注册.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读