Вопрос по caliburn.micro, wpf, tabcontrol, mvvm, c# – WPF Caliburn.Micro и TabControl с проблемой UserControls

12

Я почти уверен, что где-то ответили, но я могуКажется, он нашел это для меня. Я

я пытаюсь использовать TabControl для переключения между UserControls (каждая вкладка отличается, поэтому не с использованием элементов)

Вот'Разбивка: у меня есть основное и 3 пользовательских элемента управления. Mainview имеет элемент управления вкладками - каждая вкладка должна отображать разные пользовательские элементы управления.

Я мог бы просто установить для контекста tabcontrol пользовательский контроль, используя Но, но это не так.T привязан к модели представления, только к представлению.

Так что я'Я использую Conductor в моей виртуальной машине и ActivateItem. Вот's, где это начинает становиться странным / расстраивающим. Приложение запускается с выбранным Tab0, но с содержимым Tab2 (последняя вкладка). Нажмите на любую другую вкладку, загрузит правильный ViewModel для этой вкладки. Нажмите обратно на Tab0, там же будет загружен правильный контент.

Как мне заставить это остановиться? Также я'мне бы очень понравилось, если переключение вкладок не• повторно инициализировать модель представления, очистив поля, которые уже были введены.

В любом случае, здесьнекоторые из моего источника, яЯ собираюсь просто бросить это здесь и поработать над чем-то еще, прежде чем я сломаю свою мышь.

Посмотреть:


        
            
                
            
        
        
            
                
            
        
        
            
                
            
        

    

и ViewModel:

class MainViewModel : Conductor
{
    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);
    }
}

Ваш Ответ

1   ответ
27

Это'Это то, что я успешно делал в сценариях мастер-детали. Позволять'скажем, у вас есть коллекция моделей дочернего вида. Я'Я подготовлю интерфейс маркера для всех этих элементов, конечно, вы можете добавить свойства / методы, которые вы считаете подходящими, если есть такие методы, которые охватывают все модели дочерних представлений:

public interface IMainScreenTabItem : IScreen
{
}

Вы можете быть совершенно уверены, что хотите, чтобы все модели вашего ребенка былиScreens (или, в случае вложенных сценариев,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);
    }
}
</imainscreentabitem></imainscreentabitem>

ГдеMainView.xaml просто:

<tabcontrol name="Items">
</tabcontrol>

И это просто работает. Это'такжеочень Хорошее и удобное решение, если ваши дочерние модели представления принимают несколько зависимостей (например, доступ к базе данных, регистратор, механизм проверки и т. д.), теперь вы можете заставить IoC выполнять всю тяжелую работу, а не создавать их вручную.

Здесь есть одна вещь: вкладки будут расположены в том же порядке, в котором классы вводятся. Если вы хотите контролировать заказ, вы можете заказать их вMainViewModel конструктор, передавая пользовательскийIComparer или добавив некоторые свойства, которые вы можетеOrderBy или выберитеIMainScreenTabItem интерфейс. Выбранный по умолчанию элемент будет первым в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).bool параметр вOnDeactivate указывает, является ли модель представления просто деактивированной или полностью 'закрыто' (например, когда вы закрываете диалоговое окно и удаляетесь). Если вы полностью закроете модель представления, она будет повторно инициализирована в следующий раз.показано.

Это означает, что любые связанные данные будут сохранены междуOnActivate звонки и тыЯ должен четко очистить его вOnDeactivate, Какие'Более того, если вы будете строго ссылаться на модели вашего ребенка, то даже после того, как вы позвонитеOnDeactivate(true), данные все еще будут там при следующей инициализации - этоs потому что IoC внедряет модели представленияодин раз (если вы не введете фабричную функцию в видеFunc), а затем инициализируется / активируется / деактивируется по требованию.

РЕДАКТИРОВАТЬ

О загрузчике, яЯ не совсем уверен, какой контейнер 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 name="key">The key to locate.
    /// <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);
    }
}
<p>Обратите внимание<code>viewModels</code> регистрация в.<code>Configure</code></p></object></ieventaggregator,></iwindowmanager,></mainviewmodel>
Это хороший подход. Однако после того, как я реализовал это в своем приложении, я понял, что невозможно установить горячую клавишу для элементов вкладки. Я'V ~ 20 вкладок и нужно быстро перемещаться между ними с помощью горячих клавиш. IgorStack
Очень хорошая рецензия, серьезно. Я'Я собираюсь реализовать это сейчас, и ядам вам знать, как это получается. Para
Этот SimpleInjector работал. Я'Я должен буду прочитать об этом - у меня нетне использовал MEF раньше, и не могКажется, мне это нравится. Серьезно, спасибо за помощь. Раньше приложение было функциональным, но мне это не нравилось, и я хотел научиться делать это правильно. Как я уже сказал, все еще новичок в MVVM (намеревался выучить его долгое время), так что это действительно помогло мне, так как загрузчик - большой шаг. Para
Ну яя все еще учусь калибра / mvvm, поэтому яЯ в недоумении - мне нужно что-то сделать в моем загрузчике, но я не уверен, что именно. Можете ли вы указать мне правильное направление? Я пытался следоватьcaliburnmicro.codeplex.com/... но не повезло. Это заставляет меня чувствовать себя полным нубом LOL. Para

Похожие вопросы