我很确定这已经在某个地方得到了解答,但我似乎无法在我的生活中找到它。
我正在尝试使用TabControl在UserControls之间切换(每个选项卡都不同,因此不使用Items)
这是细分: 我有我的主视图和3个用户控件。 Mainview有一个选项卡控件 - 每个选项卡应显示不同的用户控件。
我可以轻松地将tabcontrol对象设置为usercontrol 但是它没有绑定到viewmodel,只绑定了视图。
所以我在我的VM中使用Conductor,以及ActivateItem。这是它开始变得奇怪/令人沮丧的地方。应用程序从选中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);
}
}
答案 0 :(得分:29)
我可以建议一条不同的路线吗?
这是我在主要细节场景中成功完成的事情。假设您有一组子视图模型。我将为所有这些项目准备一个标记界面,当然,如果有这样的方法跨越所有子视图模型,您可以添加您认为合适的属性/方法:
public interface IMainScreenTabItem : IScreen
{
}
您可以确定您希望所有子模型都是Screen
(或者,如果是嵌套方案,Conductor
s)。这使得它们具有完整的初始化/激活/停用循环。
然后,子视图模型:
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);
}
}
MainView.xaml
只是:
<TabControl Name="Items"/>
它只是有效。如果您的子视图模型具有多个依赖关系(例如数据库访问,记录器,验证机制等),它也是非常非常好的方便解决方案,现在您可以让IoC完成所有繁重工作,而不是通过实例化它们手。
这里有一件事:选项卡将按照注入类的顺序放置。如果您希望控制排序,可以通过传递自定义MainViewModel
或添加某些属性IComparer<IMainScreenTabItem>
或选择{{1}来在OrderBy
构造函数中对它们进行排序接口。默认选定项目将是IMainScreenTabItem
列表中的第一个。
其他选项是让Items
采用三个参数:
MainViewModel
虽然当你有超过2-3个子视图模型(并且你可以轻松获得更多)时,它会很快变得混乱。
关于'清算'部分。 IoC创建的视图模型与常规生命周期相对应:它们最多初始化一次(public MainViewModel(ChRemoteViewModel chRemoteViewModel, PcInfoViewModel pcInfo, RemoteToolsViewModel remoteTools)
{
// Add the view models above to the `Items` collection in any order you see fit
}
),然后在每次导航离开OnInitialize
时停用,并在导航时激活到(OnDeactivate(bool)
)。 OnActivate
中的bool
参数表示视图模型是刚刚停用还是完全“关闭”(例如,当您关闭对话框窗口并离开时)。如果完全关闭视图模型,它将在下次显示时重新初始化。
这意味着任何绑定数据都会在OnDeactivate
次调用之间保留,您必须在OnActivate
中明确清除它。更重要的是,如果你保留对子视图模型的强引用,那么即使在你调用OnDeactivate
之后,数据仍将在下次初始化时出现 - 这是因为IoC注入视图模型是创建的> em>(除非您以OnDeactivate(true)
的形式注入工厂函数),然后根据需要初始化/激活/停用。
关于bootstrapper,我不太确定你正在使用什么样的IoC容器。我的示例使用SimpleInjector,但您也可以使用{{3}}轻松完成Autofac:
Func<YourViewModel>
请注意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
注册。