Представление разрешения SimpleIoc MVVM (UserControl)

Надеюсь, кто-то может просветить меня о лучшей практике mvvm с помощью service locator.
Основные принципы ясны. У меня есть свои представления с соответствующей моделью представления, все работает на нее должно.

Приведем простой пример. У меня есть одно главное окно и 2 пользовательских элемента управления UCA и UCB. Каждая из них имеет модель вида, зарегистрированную в классе локатора.

Использование этого шаблона IoC как можно отобразить UCA и UCB в главном окне с помощью одного элемента управления содержимым и привязки через модель представления главного окна? Чтобы быть точным, я хочу показать только один элемент управления в то же время. Я не могу связать модель представления UCA или UCB, потому что это первый подход представления, представление не разрешается автоматически.

Каков правильный подход для этого?

Спасибо

3 ответа

  1. Можно создать отдельную службу для запуска представлений в виде диалогового окна, чтобы ее можно было использовать в общем виде в приложении. И введет эту услугу в конструктор ViewModelvia, который хочет запустить любое диалоговое окно.

    public interface IDialogService<T>
    {
        void Show();
        void ShowDialog();
    }
    
    public class DialogService<T> : IDialogService<T> where T : Window
    {
        public void Show()
        {
            container.Resolve<T>().Show();
        }
    
        public void ShowDialog()
        {
            container.Resolve<T>().ShowDialog();
        }
    }
    

    Теперь просто впрысните эту услугу соответствующим viewmodel.

    public class YourViewModel
    {
        //commands
        public ICommand someCommand { get; set; }
    
        private IDialogService<BookingView> _dialogService;
        public YourViewModel(IDialogService<YourView > dialogService)
        {
            _dialogService = dialogService
            someCommand = new RelayCommand(someCommandDoJob, () => true);
        }
    
        public void someCommandDoJob(object obj)
        {
            //Since you want to launch this view as dialog you can set its datacontext in its own constructor.    
            _dialogService.ShowDialog();
        }
    }
    
  2. Одним из решений может быть использование ContentTemplateсвойства вместе с DataTriggerвыборочно показать UCA или UCB (см. пример ниже). UCA также может быть установлен в качестве представления по умолчанию в установщике стилей. В этом случае нужен только одинDataTriggers.

    Другим решением может быть использование ContentTemplateSelectorсвойства и реализации a DataTemplateSelector.

    См.: ContentControl.ContentTemplateSelector (MSDN)

    <!--content control in main window-->
    <ContentControl>
        <ContentControl.Style>
            <Style TargetType="ContentControl">
                <Style.Triggers>
                    <!--suppose main window view model has a bool property UseUCA-->
                    <!--if UseUCA is true render UCA view-->
                    <DataTrigger Binding="{Binding UseUCA}" Value="True">
                        <DataTrigger.Setters>
                            <!--by setting ContentTemplate property you control what is rendered inside of the content control-->
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <!--render UCA view-->
                                        <local:UCA />
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger.Setters>
                    </DataTrigger>
                    <!--if UseUCA is false render UCB view-->
                    <DataTrigger Binding="{Binding UseUCA}" Value="False">
                        <DataTrigger.Setters>
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <!--render UCB view-->
                                        <local:UCB />
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger.Setters>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
    </ContentControl>
    
  3. ОК… вот мысль… вы можете отслеживать «currentview» в MainViewModel и позволить UI показать правильный элемент управления на основе типа. Вот краткий пример. Я использую кнопку для переключения видов… в идеале это может быть сделано с помощью любой логики, которая соответствует вашим требованиям.

    Главное окно:

    <Window x_Class="WpfApplication4.MainWindow"
            
            
            
            
            
            mc_Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            DataContext="{Binding Main, Source={StaticResource Locator}}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="30" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Button Content="Switch Views" Command="{Binding SwitchViewsCommand}" />
            <ContentControl Grid.Row="1" Content="{Binding CurrentControl}">
    
            </ContentControl>
        </Grid>
    </Window>
    

    UCA и UCB — это просто пользовательские элементы управления с различным текстом в них:

    <UserControl x_Class="WpfApplication4.UserControls.UCA"
                 
                 
                  
                  
                 
                 mc_Ignorable="d" 
                 d_DesignHeight="300" d_DesignWidth="300">
        <Grid>
            <TextBlock Text="This is user control A" />
        </Grid>
    </UserControl>
    

    UCAViewModel и UCBViewModel являются пустыми viewmodels в моем примере, которые наследуют от ViewModelBase

    namespace WpfApplication4.ViewModel
    {
        public class UCAViewModel : ViewModelBase
        {
        }
    }
    

    MainViewModel обрабатывает текущее представление на основе его viewmodel

    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    using Microsoft.Practices.ServiceLocation;
    
    namespace WpfApplication4.ViewModel
    {
        public class MainViewModel : ViewModelBase
        {
            public MainViewModel()
            {
                RegisterCommands();
                SwitchView();
            }
    
            private void SwitchView()
            {
                if(CurrentControl == null)
                {
                    CurrentControl = ServiceLocator.Current.GetInstance<UCAViewModel>();
                }
                else
                {
                    if(CurrentControl is UCAViewModel)
                        CurrentControl = ServiceLocator.Current.GetInstance<UCBViewModel>();
                    else
                        CurrentControl = ServiceLocator.Current.GetInstance<UCAViewModel>();
    
    
                }
            }
    
            private ViewModelBase _currentControl;
            public ViewModelBase CurrentControl
            {
                get { return _currentControl; }
                set
                {
                    if (_currentControl != value)
                    {
                        _currentControl = value;
                        RaisePropertyChanged("CurrentControl");
                    }
                }
            }
    
            private void RegisterCommands()
            {
                SwitchViewsCommand = new RelayCommand(SwitchView);
            }
    
            public RelayCommand SwitchViewsCommand { get; private set; }
        }
    }
    

    ViewModelLocator хранит экземпляры

    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Ioc;
    using Microsoft.Practices.ServiceLocation;
    
    namespace WpfApplication4.ViewModel
    {
        /// <summary>
        /// This class contains static references to all the view models in the
        /// application and provides an entry point for the bindings.
        /// </summary>
        public class ViewModelLocator
        {
            /// <summary>
            /// Initializes a new instance of the ViewModelLocator class.
            /// </summary>
            public ViewModelLocator()
            {
                ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    
                SimpleIoc.Default.Register<MainViewModel>();
                SimpleIoc.Default.Register<UCAViewModel>();
                SimpleIoc.Default.Register<UCBViewModel>();
            }
    
            public MainViewModel Main
            {
                get
                {
                    return ServiceLocator.Current.GetInstance<MainViewModel>();
                }
            }
    
            public static void Cleanup()
            {
                // TODO Clear the ViewModels
            }
        }
    }
    

    И, наконец, клей, который делает правильный вид шоу делается в приложении.файл XAML:

    <Application x_Class="WpfApplication4.App"    StartupUri="MainWindow.xaml"  d1p1_Ignorable="d" 
                 
                 >
        <Application.Resources>
            <vm:ViewModelLocator x_Key="Locator" d_IsDataSource="True" />
            <DataTemplate DataType="{x:Type vm:UCAViewModel}">
                <localUC:UCA />
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:UCBViewModel}">
                <localUC:UCB />
            </DataTemplate>
        </Application.Resources>
    </Application>