powered by simpleCommunicator - 2.0.18     © 2024 Programmizd 02
Map
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Зациклились зависимости Wpf Net Core
4 сообщений из 4, страница 1 из 1
Зациклились зависимости Wpf Net Core
    #39956151
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем привет, пишу Wpf-приложение на Core и столкнулся со следующей проблемой.
Я хочу в CompositionRoot приложения перехватывать создание View и ViewModel для них.
Все View-это UserControls, которые хостятся в Window.
В Window в качестве хоста для View использую компонент materialDesign:Transitioner из одноименного UI фреймворка. Он представляет собой ItemsControl, который принимает в качестве ItemSource коллекцию UserControl и при смене SelectedIndex анимированно меняет View.
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<Window x:Class="##PageTransitor.ControlHoster"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:##.PageTransitor"
        xmlns:views="clr-namespace:##.Views"
        mc:Ignorable="d"
        TextElement.Foreground="{DynamicResource MaterialDesignBody}"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        Background="{DynamicResource MaterialDesignPaper}">
    <Grid>
        <materialDesign:Transitioner 
            x:Name="TransitionHoster" 
             SelectedIndex="0"
             RenderOptions.BitmapScalingMode="HighQuality"
             AutoApplyTransitionOrigins="True">          
        </materialDesign:Transitioner>
    </Grid>
</Window>



CodeBehind ControlHoster
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
   public partial class ControlHoster : Window
    {
        /// <summary>
        /// Список страниц
        /// </summary>
        public ObservableCollection<BaseUserControl> Views { get; set; }

        private readonly IViewProvider _IViewProvider;
        public ControlHoster(IViewProvider iViewProvider)  
        {
            _IViewProvider = iViewProvider;
            _IViewProvider.RaiseNavigateToView += _navigationService_NavigateToView;           
            InitializeComponent();

            Views = new ObservableCollection<BaseUserControl> ();
            Views.Add(_IViewProvider.GetStartView);
            TransitionHoster.ItemsSource = Views;         
        }

        /// <summary>
        /// Анимация перехода между Views
        /// </summary>
        private void _navigationService_NavigateToView(BaseUserControl newView)
        {         
         var existedViews = TransitionHoster.ItemsSource.Cast<BaseUserControl>();
            var goalIndex = existedViews.ToList().FindIndex(t => string.Equals(t.Name, newView.Name));

            if (goalIndex==-1)
            {
                //View отсутствует
                Views.Add(newView);
                TransitionHoster.SelectedIndex= existedViews.Count()-1;
            }
            else
            {
                TransitionHoster.SelectedIndex = goalIndex;
            }
        }
    }



Что делает IViewProvider:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
 /// <summary>
    /// Провайдер View. Слушает INavigation и предоставляет соответствующий UserControl при событии смены View
    /// </summary>
    public  interface IViewProvider
    {
        /// <summary>
        /// Событие смены View
        /// </summary>

        public event Action<BaseUserControl> RaiseNavigateToView;

        /// <summary>
        /// Переход на другую страницу
        /// </summary>
        /// <param name="viewName"></param>
        public void NavigateTo(string viewName);

        /// <summary>
        /// Получить стартовую View
        /// </summary>
        public BaseUserControl GetStartView { get; }
    }


Конкретная реализация IViewProvider
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
 public class ViewProvider : IViewProvider
    {
        public BaseUserControl GetStartView => Views["EmptyLayout"];
    
        public event Action<BaseUserControl> RaiseNavigateToView;

        /// <summary>
        /// Событие смены View
        /// </summary>
        private readonly Dictionary<string, BaseUserControl> Views;

        public ViewProvider(IEnumerable<BaseUserControl> views)
        {
            Views = views.ToDictionary(t => t.Name, t => t);
        }

        /// <summary>
        /// Событие смены представления
        /// </summary>
        /// <param name="viewName"></param>
        public void NavigateTo(string viewName)
        {
            if (Views.TryGetValue(viewName, out BaseUserControl View))
            {
                RaiseNavigateToView?.Invoke(View);
            }
            else
                throw new Exception($"No registered view with name:{viewName}");
        }
    }


Сервис навигации определен с примитивными типами аргументов, потому что ViewModels находятся в другом проекте-библиотеке классов и я не могу сослаться на него из WPF-проекта, так как Wpf-проект уже имеет зависимость от ViewModels и соответственно срабатывает защита от зацикливания зависимостей.
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
    /// <summary>
    /// Сервис перехода между страницами приложения, вызываемый из ViewModels
    /// </summary>
    public  interface INavigation
    {
        /// <summary>
        /// Перейти на страницу
        /// </summary>
        /// <param name="viewName">Имя страницы, на которую переходим</param>
        public void NavigateTo(string viewName);
    }


Его имплементация
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
    public class NavigationService : INavigation
    {
        private readonly IViewProvider _viewProvider;
        public NavigationService(IViewProvider viewProvider)
        {
            _viewProvider = viewProvider;
        }
        /// <summary>
        /// Из ViewModel вызываем метод с указанием на которую страницу переходить
        /// </summary>
        /// <param name="viewName">Имя viewName, на которое нужно перейти</param>
        public void NavigateTo(string viewName) => _viewProvider.NavigateTo(viewName);   
    }


DataContext присваиваю для UserControl через конструктор
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
/// <summary>
/// Базовый класс представления
/// </summary>
public abstract class BaseUserControl:UserControl
    {
        public BaseUserControl(BaseViewModel viewModel=null)
        {
            Name = this.GetType().Name;
            this.DataContext = viewModel;           
        }
    }


, который раздаю через внедрение зависимостей в Composition Root

App.xaml.cs
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
 public partial class App : Application
    {
        private readonly IHost host;
        public App()
        {
            host = Host.CreateDefaultBuilder()
              .ConfigureServices((context, services) =>
              {
                  ConfigureServices(context.Configuration, services);
              })
              .Build();
        }

        public IServiceProvider ServiceProvider { get; private set; }

        public IConfiguration Configuration { get; private set; }
     
        protected override async void OnStartup(StartupEventArgs e)
        {  
            await host.StartAsync();

            var mainWindow = host.Services.GetRequiredService<ControlHoster>();    
            mainWindow.Show();
            var navigationService= host.Services.GetRequiredService<INavigation>();
            await Task.Delay(1500);
            navigationService.NavigateTo("Login");
            base.OnStartup(e);
        }

        protected override async void OnExit(ExitEventArgs e)
        {
            using (host)
            {
                await host.StopAsync(TimeSpan.FromSeconds(5));
            }
            base.OnExit(e);
        }

        
        private void ConfigureServices(
            IConfiguration configuration,
         IServiceCollection services)
        {   
            #region register ViewModels
            services.AddSingleton<LoginViewModel>();
            services.AddSingleton<FlightsDistributionViewModel>();
            #endregion

            #region register View

            services.AddSingleton(ctx =>
            {
                var loginViewModel = ctx.GetRequiredService<LoginViewModel>();
                return new Login(loginViewModel);
            });

            services.AddTransient<EmptyLayout>();

            services.AddSingleton(ctx =>
            {
                var DistributionViewModel = ctx.GetRequiredService<DistributionViewModel>();
                return new FlightsDistribution(DistributionViewModel);
            });
            #endregion

            services.AddSingleton<IViewProvider>(ctx =>
            {
                var viewList = new List<BaseUserControl>
            {
            ctx.GetRequiredService<EmptyLayout>(),
            ctx.GetRequiredService<Login>(),
            ctx.GetRequiredService<FlightsDistribution>()
            };
                return new ViewProvider.ViewProvider(viewList);
            });
            services.AddSingleton<ControlHoster>();
            services.AddSingleton<INavigation, NavigationService>();
        }
    }


Все работает ровно до этого момента. Но мне необходимо внедрить INavigation в те ViewModel, в которых происходит инициация смены View. Если я внедряю INavigation в LoginViewModel то я попадаю в Dependency Deadlock:LoginViewModel требует INavigation, а INavigation в конечном счете требует LoginViewModel. Подскажите плиз как можно передизайнить данную структуру зависимостей? Спасибо
...
Рейтинг: 0 / 0
Зациклились зависимости Wpf Net Core
    #40028188
Фотография pation
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub,

Неправильное использование DI
Зачем ты сразу создаёшь экземпляры всех объектов и используешь AddSingleton?
с таким подходом тебе совсем не нужен DI

Но я думаю что View и VM нужно создавать по мере необходимости выбрасывать если необходимость отпала.
...
Рейтинг: 0 / 0
Зациклились зависимости Wpf Net Core
    #40028229
vb_sub
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pation,
я не создаю объекты, а регистрирую их зависимость.
Singleton потому что в рамках wpf-приложения View и ViewModel должны быть по одному экземпляру на приложение.
Убивать VM нет смысла, потому что надо будет куда-то сохранять ее состояние, потом восстанавливать- проще держать ее в памяти.
...
Рейтинг: 0 / 0
Зациклились зависимости Wpf Net Core
    #40029185
Фотография skyANA
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vb_sub
Подскажите плиз как можно передизайнить данную структуру зависимостей?

vb_sub
Убивать VM нет смысла, потому что надо будет куда-то сохранять ее состояние, потом восстанавливать- проще держать ее в памяти.

Походу вам виднее и вы не нуждаетесь в подсказках :)
...
Рейтинг: 0 / 0
4 сообщений из 4, страница 1 из 1
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Зациклились зависимости Wpf Net Core
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали тему (1): Анонимы (1)
Читали форум (1): Анонимы (1)
Пользователи онлайн (11): Анонимы (9), Bing Bot, Yandex Bot
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]