Получение всех команд NET, часть 2

Продолжение части 1. Попробую поиграться с окнами и "прокидыванием" данных в них. Окна - на WPF (ну не люблю я WinForms, плюс MVC / MVP мне как-то не зашли).

Общая идея состоит в том, что окно само по себе ничего не делает - только отображает какие-то данные и посылает "команды" какому-то стороннему модулю. А уже этот модуль там чего-то с данными творит, модифицирует (при необходимости), записывает в базу и теде, и тепе.

Про MVVM в моем представлении
Т.е. приложение становится многоуровневым:
Model (“модель”) – хранит, предоставляет, удаляет, изменяет данные. Где и как хранятся эти данные – в идеале должна знать только Model. Уровню глубоко плевать, кто запрашивает и почему данные меняются именно так.
View (“представление”) – только показывает (причем не факт что пользователю) данные, откуда-то и кем-то полученные. Ни в коем случае не модифицирует их, не удаляет – тупо показывает, и на этом функции этого уровня заканчиваются. Уровень не имеет никакого представления о том, откуда берутся данные, и почему они меняются именно так. Имеет минимальную связь со следующим уровнем, но им толком не руководит.
View-Model (“модель представления”) – как раз занимается тем, чтобы получить данные с модели, предоставить эти данные во View, как-то их обработать, и потом отправить измененные (возможно) данные в Model. При этом по ходу дела как-то сообщает, что у нее там что-то поменялось. Имеет представление о Model (ну надо же откуда-то получать данные и кому-то давать команду на сохранение) и никакого представления о View.

Вот примерно таким образом я представляю себе паттерн (он же “шаблон”) MVVM.

Для упрощения понимания в дальнейшем буду использовать именно английские названия, т.к. сам несколько раз натыкался на статьи, где “модель представления” – это по смыслу был не один уровень, а два.

И вот теперь я на распутье: по хорошему надо делать 2 разные сборки - одна будет заведовать окнами, вторая - ViewModel'ями. С другой стороны, нередки ситуации (по крайней мере в моей практике такое встречается), когда одно окно вызывает второе, второе - третье, и по закрытию третьего какие-то данные надо "пробрасывать" в первое. Я не настолько крут в C#, чтоб это все реализовать, так что будет отдельная сборка KpblcCadInfrastructure.Core.NET (NET6), где будут и View, и ViewModel. Хотя, в этой сборке будет два подкаталога - Views и ViewModels, так что при необходимости "раскидать" по разным сборкам особого труда не должно составить ;)

Как бы мне ни хотелось сразу упасть в логику, но придется прописать пару окон. Одно будет показывать перечень загруженных сборок, второе - перечень объявленных команд. В темы оформления углубляться не буду, если интересно - могу потом попробовать пораспинываться на эту тему.

Начальные описания окон
Итак, отдельная сборка KpblcCadInfrastructure.Core (NET6), как я и грозился ;) И внутри – пара окошек WPF.

Окно по показу сборок становится примерно таким (в режиме разработки):
2025-01-01_20-29-45

И XAML-описание:

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
<Window x:Class="KpblcCadInfrastructure.Core.NET.Views.Windows.AssembliesWindow"
       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:KpblcCadInfrastructure.Core.Views.Windows"
       mc:Ignorable="d"
       Title="AssembliesWindow"
       Height="250"
       Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0"
                 Content="Показывать только внешние сборки" />
        <DataGrid Grid.Row="1"></DataGrid>
        <UniformGrid Grid.Row="2"
                    Rows="1"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="5">
            <Button Content="Закрыть"
                   IsCancel="True"></Button>
        </UniformGrid>
    </Grid>
</Window>

Второе окно (по поводу команд) очень сильно похоже на предыдущее. Вся разница будет в колонках DataGrid, которые пока не прописаны:
2025-01-01_20-32-50

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
<Window x:Class="KpblcCadInfrastructure.Core.NET.Views.Windows.CommandsWindow"
       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:KpblcCadInfrastructure.Core.NET.Views.Windows"
       mc:Ignorable="d"
       Title="CommandsWindow"
       Height="250"
       Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0"
                 Content="Показывать только внешние сборки" />
        <DataGrid Grid.Row="1"></DataGrid>
        <UniformGrid Grid.Row="2"
                    Rows="1"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="5">
            <Button Content="Закрыть"
                   IsCancel="True"></Button>
        </UniformGrid>
    </Grid>
</Window>
Вызов окон из команд nanoCAD
Так, чисто на поржать – прописываю вызов окна из команды. Понятно, что там не будет вообще ничего – но поглядеть будет интересно ;)

1
2
3
4
5
6
[CommandMethod("get-all-assemblies")]
public static void GetAllAssembliesDialogMode()
{
    AssembliesWindow win = new AssembliesWindow();
    Application.ShowModalWindow(win);
}

Здесь Application – это HostMgd.ApplicationServices.Application. Аналогичное действие прописываю и для получения всех команд:

1
2
3
4
5
6
[CommandMethod("get-all-commands")]
public static void GetAllCommandsDialogMode()
{
    CommandsWindow win = new CommandsWindow();
    Application.ShowModalWindow(win);
}

Если посмотреть на окна, видно, что у обоих есть флажок "показать только внешние сборки". Имеется в виду, что это сборки, которые подгружаются не из %ProgramFiles%. При этом получение команд зависит от перечня сборок. Как-то начинает свербеть чуть пониже спины, что такое получение "несистемных" сборок стоит прописывать только один раз. Так что дорабатываю интерфейс IAssemblyRepository и его реализацию

Дополнение интерфейса работы со сборками
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System.Collections.Generic;
using System.Reflection;

namespace KpblcCadInfrastructure.Abstractions.Interfaces
{
    public interface IAssemblyRepository
    {
        /// <summary>
        /// Получение списка загруженных сборок
        /// </summary>
        /// <returns></returns>
        IEnumerable<Assembly> Get();
        /// <summary>
        /// Получение списка загруженных сборок, кроме тех, что из %ProgramFiles%
        /// </summary>
        /// <returns></returns>
        IEnumerable<Assembly> GetCustomAssemblies();
    }
}

Очень хочется добавить в AssemblyRepository нечто типа

1
2
3
4
5
public IEnumerable<Assembly> GetCustomAssemblies()
{
    string programFiles = Environment.GetEnvironmentVariable("programfiles").ToUpper();
    return Get().Where(o => !o.Location.ToUpper().StartsWith(programFiles));
}

Но, покопавшись в глубинах, становится видно, что возможны варианты, когда Location сборки определить невозможно. Буду считать, что это все равно пользовательская сборка, так что код превращается в

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public IEnumerable<Assembly> GetCustomAssemblies()
{
    string programFiles = Environment.GetEnvironmentVariable("programfiles").ToUpper();
    return Get().Where(o =>
    {
        try
        {
            return !o.Location.ToUpper().StartsWith(programFiles);
        }
        catch
        {
            return true;
        }
    });
}

Да, я знаю, профи меня сейчас закидают тухлыми пидоморами. Буду уворачиваться ;)

Теперь можно и с ViewModel начинать ковыряться :)

Для начала в KpblcCadInfrastructure.Abstractions прописываю абстрактный класс ViewModel, от которого буду наследовать все ViewModel'и:

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
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace KpblcCadInfrastructure.Abstractions.ViewModels.Base
{
    public abstract class ViewModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(PropertyName));
            }
        }

        public virtual bool Set<T>(ref T Field, T value, [CallerMemberName] string PropertyName = null)
        {
            if (Equals(Field, value))
            {
                return false;
            }
            Field = value;
            OnPropertyChanged(PropertyName);
            return true;
        }

        public virtual string Title
        {
            get => _Title;
            set => Set(ref _Title, value);
        }
       
        private string _Title;
    }
}

Это изначальная ViewModel, которая тупо позволяет быстро и просто, с использованием метода Set, не только что-то чему-то назначить, но еще и про это проорать на всю Ивановскую. Поле Title понадобится для назначения заголовков окон.

Ну ок, теперь ползу в KpblcCadInfrastructure.Core, и там прописываю ViewModel для сборок (начну с них, поскольку тут будет проще слеганца). Для начала вспоминаю, что окно вызывается уже самим nanoCAD, так что остаются только такие задачи:

  • В окне вывести все сборки - полное имя, версия
  • Дать возможность закрыть окно

При этом закрытие окна в текущих реалиях можно вообще не заморачиваться, соответствующий код можно даже не писать. Назначение кнопке атрибут IsCancel="True" уже автоматом закроет окно при нажатии на кнопку или нажатии Esc. Так что задача остается только одна - показать информацию о сборках.

Так, что-то я увлекся и забыл про git...
[/su_spoiler]

Теперь пора прописывать ViewModel для получения сборок. Сначала поиграться - типа "Нажали на галочку, вывалилось сообщение":

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
using KpblcCadInfrastructure.Abstractions.ViewModels.Base;

namespace KpblcCadInfrastructure.Core.NET.ViewModels
{
    public class AssembliesViewModel : ViewModel
    {
        public AssembliesViewModel()
        {
            Title = "Показать сборки";
        }

        public bool ShowCustomAssemblies
        {
            get => _showCustomAssemblies;
            set
            {
                if (Set(ref _showCustomAssemblies, value))
                {
                    // ????????
                }
            }
        }

        private bool _showCustomAssemblies;
    }
}

И приводим в соответствие xaml-описание окна AssembliesWindow (прописывать буду по шагам, так что потерпите):

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
<Window x:Class="KpblcCadInfrastructure.Core.NET.Views.Windows.AssembliesWindow"
       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:KpblcCadInfrastructure.Core.NET.Views.Windows"
       xmlns:vm="clr-namespace:KpblcCadInfrastructure.Core.NET.ViewModels"
       mc:Ignorable="d"
       Title="AssembliesWindow"
       Height="250"
       Width="600"
       d:DataContext="{d:DesignInstance vm:AssembliesViewModel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0"
                 Content="Показывать только внешние сборки" />
        <DataGrid Grid.Row="1"></DataGrid>
        <UniformGrid Grid.Row="2"
                    Rows="1"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="5">
            <Button Content="Закрыть"
                   IsCancel="True"></Button>
        </UniformGrid>
    </Grid>
</Window>

Добавляются две строки в объявлении окна:

1
2
xmlns:vm="clr-namespace:KpblcCadInfrastructure.Core.ViewModels"
d:DataContext="{d:DesignInstance vm:AssembliesViewModel}"

Первая сообщает, что есть такое типа пространство имен, с заголовком vm, а вторая говорит, что для разработки надо взять определенный класс из vm, откуда уже "подсасывать" показываемые данные.

Теперь связываю Title и CheckBox с соответствующими полями ASsemblyViewModel:

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
<Window x:Class="KpblcCadInfrastructure.Core.NET.Views.Windows.AssembliesWindow"
       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:KpblcCadInfrastructure.Core.NET.Views.Windows"
       xmlns:vm="clr-namespace:KpblcCadInfrastructure.Core.NET.ViewModels"
       mc:Ignorable="d"
       Title="{Binding Title}"
       Height="250"
       Width="600"
       d:DataContext="{d:DesignInstance vm:AssembliesViewModel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0"
                 Content="Показывать только внешние сборки"
                 IsChecked="{Binding ShowCustomAssemblies}"/>
        <DataGrid Grid.Row="1"></DataGrid>
        <UniformGrid Grid.Row="2"
                    Rows="1"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="5">
            <Button Content="Закрыть"
                   IsCancel="True"></Button>
        </UniformGrid>
    </Grid>
</Window>

Ну и надо не забыть в вызывающей команде связать ViewModel и окно:

1
2
3
4
5
6
7
8
9
10
[CommandMethod("get-all-assemblies")]
public static void GetAllAssembliesDialogMode()
{
    AssembliesViewModel vm = new AssembliesViewModel();
    AssembliesWindow win = new AssembliesWindow()
    {
        DataContext = vm,
    };
    Application.ShowModalWindow(win);
}

Теперь, если запустить проект, и поставить точку останова на строке в ASsembliesViewModel if (Set(ref _showCustomAssemblies, value)), можно будет увидеть, что при клике на галочке выполнение как раз на этой строке и останавливается. Правда, ничего не происходит - но сейчас подправлю.

Чего я там хотел? Чтоб сообщение вываливалось? Ну так для этого есть сервис сообщений! Осталось его передать в ViewModel (ну и по ходу дела переделать вызов ViewModel):

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
using KpblcCadInfrastructure.Abstractions.Interfaces;
using KpblcCadInfrastructure.Abstractions.ViewModels.Base;

namespace KpblcCadInfrastructure.Core.NET.ViewModels
{
    public class AssembliesViewModel : ViewModel
    {
        public AssembliesViewModel(IMessageService MessageService)
        {
            _messageService = MessageService;
            Title = "Показать сборки";
        }

        public bool ShowCustomAssemblies
        {
            get => _showCustomAssemblies;
            set
            {
                if (Set(ref _showCustomAssemblies, value))
                {
                    _messageService.InfoMessage("Показывать " +
                                                (_showCustomAssemblies ? "только пользовательские" : "все") +
                                                " сборки", nameof(ShowCustomAssemblies));
                }
            }
        }

        private IMessageService _messageService;
        private bool _showCustomAssemblies;
    }
}

Ну и вызов:

1
2
3
4
5
6
7
8
9
10
11
[CommandMethod("get-all-assemblies")]
public static void GetAllAssembliesDialogMode()
{
    IMessageService messageService = new MessageService();
    AssembliesViewModel vm = new AssembliesViewModel(messageService);
    AssembliesWindow win = new AssembliesWindow()
    {
        DataContext = vm,
    };
    Application.ShowModalWindow(win);
}

Теперь, во-первых, заголовок окна уже более вменяем. И, во-вторых, нажатие на галочку будет выводить информационное сообщение. Вдобавок с разным сообщением. Немного кривенько и косенько (в идеале nameof(ShowCustomAssemblies) прописывать, конечно, не надо, но разбираться сейчас не буду - не до того).

Ну вот, поигрались - так что можно сносить упоминания про IMessageService и вместо него подсовывать IAssemblyRepository. Код не сильно поменяется:

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
using System.Reflection;
using KpblcCadInfrastructure.Abstractions.Interfaces;
using KpblcCadInfrastructure.Abstractions.ViewModels.Base;

namespace KpblcCadInfrastructure.Core.NET.ViewModels
{
    public class AssembliesViewModel : ViewModel
    {
        public AssembliesViewModel(IAssemblyRepository AssemblyRepository)
        {
            _assemblyRepository = AssemblyRepository;
            Title = "Показать сборки";
            Refresh();
        }

        public bool ShowCustomAssemblies
        {
            get => _showCustomAssemblies;
            set
            {
                if (Set(ref _showCustomAssemblies, value))
                {
                    Refresh();
                }
            }
        }

        public List<Assembly> AssembliesList
        {
            get => _assembliesList;
            private set => Set(ref _assembliesList, value);
        }

        private void Refresh()
        {
            if (ShowCustomAssemblies)
            {
                AssembliesList = _assemblyRepository.GetCustomAssemblies().ToList();
            }
            else
            {
                AssembliesList = _assemblyRepository.Get().ToList();
            }
        }

        private IAssemblyRepository _assemblyRepository;
        private bool _showCustomAssemblies;
        private List<Assembly> _assembliesList;
    }
}

И в команде тоже не такие уж категорические изменения:

1
2
3
4
5
6
7
8
9
10
11
[CommandMethod("get-all-assemblies")]
public static void GetAllAssembliesDialogMode()
{
    IAssemblyRepository assemblyRepository = new AssemblyRepository();
    AssembliesViewModel vm = new AssembliesViewModel(assemblyRepository);
    AssembliesWindow win = new AssembliesWindow()
    {
        DataContext = vm,
    };
    Application.ShowModalWindow(win);
}

Начинаю прописывать связку DataGrid и AssembliesList:

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
<Window x:Class="KpblcCadInfrastructure.Core.NET.Views.Windows.AssembliesWindow"
       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:KpblcCadInfrastructure.Core.NET.Views.Windows"
       xmlns:vm="clr-namespace:KpblcCadInfrastructure.Core.NET.ViewModels"
       mc:Ignorable="d"
       Title="{Binding Title}"
       Height="250"
       Width="600"
       d:DataContext="{d:DesignInstance vm:AssembliesViewModel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0"
                 Content="Показывать только внешние сборки"
                 IsChecked="{Binding ShowCustomAssemblies}"/>
        <DataGrid Grid.Row="1" ItemsSource="{Binding AssembliesList}">
        </DataGrid>
        <UniformGrid Grid.Row="2"
                    Rows="1"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Margin="5">
            <Button Content="Закрыть"
                   IsCancel="True"></Button>
        </UniformGrid>
    </Grid>
</Window>

И вот тут выясняется, что показать те же самые Location / FullName / etc напрямую невозможно! Точнее, не так: при текущих настройках показывается овердофига столбцов. Но мне-то нужны строго определенные! Так что вношу тьму изменений. Да, профи, я знаю, что вы про меня думаете. Да, я знаю, что так делать неправильно - но мне просто хочется показать, что сразу нарисовать идеальный софт лично у меня не получается никогда.

Так что добавляю отдельный класс AssemblyInfo (пока так; возможно, потом придется переделывать):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Reflection;

namespace KpblcCadInfrastructure.Abstractions.Entities
{
    public class AssemblyInfo
    {
        public AssemblyInfo(Assembly Assembly)
        {
            this.Assembly = Assembly;
            this.Location = Assembly.Location;
            this.Version = Assembly.GetName(false).Version;
        }

        public string Location { get; }
        public Version Version { get; }
        public Assembly Assembly { get; }
    }
}

Следом меняю IAssemblyRepository на то, чтоб он возвращал уже не сборки, а экземпляры AssemblyInfo. Потратив еще минуты 2-3, правлю все остальное, и запускаю команды - просто посмотреть, насколько верно выводятся данные. Вроде бы по первым прикидкам все правильно (окно специально не правлю).

То есть уже возникают смутные подозрения, что подобное разделение логики и ответственности способно хоть где-то да помочь.

А вот теперь попробую поменять собственно команду вывода сборок в ком.строку так, чтоб она тоже использовала ViewModel (использую подход, описанный в статье "ключевые слова на нескольких языках":

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
[CommandMethod("-get-all-assemblies")]
public static void GetAllAssembliesCommandLineMode()
{

    Document doc = Application.DocumentManager.MdiActiveDocument;
    if (doc == null)
    {
        return;
    }

    PromptKeywordOptions options = new PromptKeywordOptions("\nВыводить полный список [Да/Нет] <Да> : ");
    options.Keywords.Add("Да");
    options.Keywords.Add("Yes");
    options.Keywords.Add("Нет");
    options.Keywords.Add("No");
    options.AllowNone = true;
    options.AllowArbitraryInput = false;

    PromptResult res = doc.Editor.GetKeywords(options);

    if (res.Status == PromptStatus.Cancel)
    {
        return;
    }

    if (res.Status == PromptStatus.None)
    {
        res.StringResult = "Y";
    }

    bool showAllAssemblies = res.StringResult.StartsWith("Y") || res.StringResult.StartsWith("Д");

    IAssemblyInfoRepository rep = new AssemblyRepository();
    AssembliesViewModel vm = new AssembliesViewModel(rep)
    {
        ShowCustomAssemblies = showAllAssemblies,
    };

    IMessageService messageService = new MessageService();
    try
    {
        foreach (AssemblyInfo assembly in vm.AssembliesList.OrderBy(o => o.Location))
        {
            messageService.ConsoleMessage(assembly.Location);
        }
    }
    catch (Exception ex)
    {
        messageService.ExceptionMessage(ex);
    }
}

Как видно, команда nanoCAD сама определяет реализации интерфейсов и вызывает ViewModel. Та, в свою очередь, вообще никак не завязана на получение данных. И ей глубоко параллельно, куда обработанные ею данные пойдут. Лично мне такое нравится ;)

Исходный код по состоянию на текущий момент: https://github.com/kpblc2000/KpblcCadInfrastructure

Следующая часть : https://autolisp.ru/2025/01/08/get-all-commands-part3/

Размещено в .NET, MVVM, nanoCAD · Метки: , , , ,



Поделитесь своим мнением


Я не робот.