![](/img/trans.png)
[英]How to make TabControl with Caliburn.Micro and diffrent UserControls
[英]WPF Caliburn.Micro and TabControl with UserControls issue
我很確定這已經在某處得到了回答,但我似乎無法在我的生活中找到它。
我正在嘗試使用 TabControl 在 UserControls 之間切換(每個選項卡都不同,所以不使用 Items)
這是細分:我有我的主視圖和 3 個用戶控件。 Mainview 有一個選項卡控件 - 每個選項卡應顯示不同的用戶控件。
我可以輕松地將 tabcontrol contect 設置為 usercontrol 使用 But then it is not bound to the viewmodel, only the view.
所以我在我的 VM 和 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>
和視圖模型:
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
{
}
您可以確定您希望所有子模型都是Screen
(或者,如果是嵌套方案, Conductor
)。 它使它們具有完整的初始化/激活/停用循環。
然后,子視圖模型:
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完成所有繁重工作,而不是手動實例化它們。
但有一點是:選項卡的放置順序與注入類的順序相同。 如果您希望控制排序,可以通過傳遞自定義IComparer<IMainScreenTabItem>
或添加一些屬性OrderBy
或選擇IMainScreenTabItem
接口,在MainViewModel
構造函數中對它們進行排序。 默認選定的項目將是“ Items
列表中的第一個。
其他選項是使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);
}
}
請注意Configure
的viewModels
注冊。
只是為了補充 Patryk Ćwiek 的好答案!
如果您已經在使用 Caliburn Mirco 並且不想添加更多依賴項,您可以使用它們的SimpleContainer
代替 SimpleInjector 或 AutoFac。
只需像這樣注冊實現:
container.AllTypesOf<IMainScreenTabItem>(Assembly.GetExecutingAssembly());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.