C# WPF Caliburn.Micro和TabControl与UserControls问题
我很确定这个问题在什么地方得到了答案,但我似乎一辈子都找不到答案 我尝试使用TabControl在UserControls之间切换(每个选项卡都不同,所以不使用项) 以下是分类: 我有我的主视图和3个用户控件。Mainview有一个选项卡控件-每个选项卡应显示不同的用户控件 我可以使用 但是它不绑定到viewmodel,只绑定到视图 所以我在我的虚拟机中使用导体,并激活它们。这就是它开始变得奇怪/令人沮丧的地方。应用程序开始时选择了Tab0,但选择了Tab2(最后一个选项卡)内容。单击任何其他选项卡,为该选项卡加载正确的ViewModel。单击返回到选项卡0,并在那里加载正确的内容 我怎样才能阻止这一切?另外,如果切换选项卡不重新初始化viewmodel,清除已经输入的字段,我真的很希望这样 不管怎么说,这是我的一些资料,在我弄坏我的鼠标之前,我将把它放在这里,做一些其他的事情 视图: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(最后一个选项卡)内容。单击任何其他选
以及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的实现。