Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
WPF

Как внедрить модель во viewModel?

26.12.2024, 11:19. Показов 25445. Ответов 295
Метки mvvm (Все метки)

Студворк — интернет-сервис помощи студентам
День добрый. Есть у меня метод в проекте, который вызывается из конструктора. Метод по типу "Получить все из бд и вывести на экран".

Смущает меня то, как именно я это делаю. Когда происходит загрузка, UI блокируется. Да и в целом все очень медленно и дальше будет только хуже с добавлением данных.

Теперь код.

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
public class BirdsViewModel : ObservableObject
{
    // [B]Конструктор[/B]
    public BirdsViewModel(IBirdRepository birdRepository, Func<BirdEntity, BirdViewModel> birdView) // [B]Прошу обратить 
//внимание на делегат. Его, думаю, и нужно заменять.[/B]
 
    {
        Birds = [];
        _birdView = birdView;
        _birdRepository = birdRepository;
        LoadBirds();
    }
 
// [B]Проблемный метод[/B]
private async void LoadBirds()
{
    var birds = await _birdRepository.GetAllAsync(); // [B]Получение данных из БД[/B]
    switch (Name)
    {
        default:
            foreach (var bird in birds)
                Birds.Add(_birdView(bird)); // [B]С помощью делегата я во ViewModel передаю Entity, 
//с которой та и работает. Есть ли другие методы подобной реализации, чтобы у каждой ViewModel был свой Entity?[/B]
            break;
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
26.12.2024, 11:19
Ответы с готовыми решениями:

Как из ViewModel UserControl'a изменить свойство в ViewModel главного окна?
Недавно начал попытки создавать приложение WPF с применением MVVM, и у меня возник вопрос, который не даёт мне продвигаться в разработке...

Как зарегистрировать ViewModel через Autofac, создав между ViewModel и нужным View DataContext связь?
Доброго времени суток. Есть желание при регистрации ViewModel'и сразу же &quot;подключиться&quot; к DataContext'у у нужной View, как например: ...

WPF получить доступ из одной viewmodel к другой viewmodel
Собственно есть у меня две viewmodel, mainviewmodel и settingsviewmodel, в mainviewmodel есть вот такая штука private Visibility...

295
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 16:31  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А поподробнее... Только код текстом, а не картинками.
У кнопки есть свойство Visibility="{Binding Visibility}". Привязано к свойству ниже.

C#
1
2
3
4
public string Visibility
{
    get { return bird.Departure != "" ? "Hidden" : "Visible"; }
}
Когда я кликаю по кнопке, то все, что есть в том TextBox попадает в bird.Departure и это свойство становится не пустым. От этого и меняется видимость кнопки.

Добавлено через 1 минуту
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Ещё пример Разделение функционала между View и ViewModel
Буду сидеть, ознакомляться.

Скажите, а как можно из конструктора awaitить метод, если конструктор нельзя сделать async?
0
 Аватар для Andrey-MSK
3388 / 2274 / 388
Регистрация: 14.08.2018
Сообщений: 7,695
Записей в блоге: 4
27.12.2024, 16:36
Цитата Сообщение от Vissmuy Посмотреть сообщение
Скажите, а как можно из конструктора awaitить метод, если конструктор нельзя сделать async?
Написать сервис очереди асинхронных методов. А лучше не пихать их в конструктор.
Цитата Сообщение от Vissmuy Посмотреть сообщение
Когда я кликаю по кнопке, то все, что есть в том TextBox попадает в bird.Departure и это свойство становится не пустым. От этого и меняется видимость кнопки.
Можно рассказать всю логику - начиная от БД и заканчивая UI. Если честно - нифига не понятно, кто за кем стоял...
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 16:54  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Написать сервис очереди асинхронных методов.
А есть ли пример такого? Только не сильно комплексный, мне так, пожевать немного и зубы не сломать.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А лучше не пихать их в конструктор.
Так нужно, чтобы при загрузке такой метод вызывался.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Можно рассказать всю логику
Хорошо, постараюсь подробней.

Видишь кнопочку "Удалить"? Под ней есть поле ввода, я туда дату ввожу.


Когда нажимаю на кнопку, происходит следующее.
C#
1
2
3
4
5
6
7
public async Task DeleteBird()
{
    BirdDeparture = Departure; //
    bird.IsAlive = false;
    await birdRepository.UpdateAsync(bird);
    navigationService.NavigateTo(App.ServiceProvider!.GetRequiredService<BirdsViewModel>());
}
1) BirdDeparture - это свойство, которое изменяет свойство у Entity, а именно bird.Departure
2) Departure - тоже свойство, к которому идет привязка из View. Тот самый TexBox с датой под кнопкой "Удалить".
2) bird.IsAlive = false; Меняем одно из свойств Entity.
3) await birdRepository.UpdateAsync(bird); Сохраняем в бд эту обновленную Entity.
4) navigationService.NavigateTo(App.Service Provider!.GetRequiredService<BirdsViewMo del>()); Обновляем UI с новыми данными.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 16:58
Цитата Сообщение от Vissmuy Посмотреть сообщение
Скажите, а как можно из конструктора awaitить метод, если конструктор нельзя сделать async?
А зачем?
Функция конструктора, по большому счёту, получить в памяти место для экземпляра и предоставить ссылку на него.
Код инициализация памяти (полей) выполняется между двумя этими действиями.
Как сюда "всунуть" асинхронность и для чего?
Если это запуск какой-то асинхронной службы, то для этого async не нужен.

Если же нужен результат выполнения, то надо для этого создать метод, а не конструктора вызывать.
Метод же вызывать из какого-то синхронного события.
0
 Аватар для Andrey-MSK
3388 / 2274 / 388
Регистрация: 14.08.2018
Сообщений: 7,695
Записей в блоге: 4
27.12.2024, 16:59
Цитата Сообщение от Vissmuy Посмотреть сообщение
А есть ли пример такого? Только не сильно комплексный, мне так, пожевать немного и зубы не сломать.
C#
1
2
3
4
5
6
7
8
9
10
11
12
using System;
using System.Threading.Tasks;
 
namespace Services.Interfaces
{
    public interface IBackgroundTaskQueue
    {
        void QueueBackgroundWorkItem(Func<Task> workItem);
        void Start();
        void Stop();
    }
}
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
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
 
using Services.Interfaces;
 
namespace Services
{
    public class BackgroundTaskQueue : IBackgroundTaskQueue
    {
        private readonly ConcurrentQueue<Func<Task>> _workItems = new ConcurrentQueue<Func<Task>>();
        private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
        private readonly CancellationTokenSource _stopTokenSource = new CancellationTokenSource();
        private Task _processTask;
 
        private async Task Process()
        {
            CancellationToken stopToken = _stopTokenSource.Token;
 
            while (!stopToken.IsCancellationRequested)
            {
                try
                {
                    await _signal.WaitAsync(stopToken);
                    _workItems.TryDequeue(out var task);
                    await await Task.Factory.StartNew(task);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex.Message);
                }
            }
        }
 
        public void QueueBackgroundWorkItem(Func<Task> workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException(nameof(workItem));
            }
 
            _workItems.Enqueue(workItem);
            _signal.Release();
        }
 
        public void Start()
        {
            _processTask = Task.Run(Process);
        }
 
        public void Stop()
        {
            _stopTokenSource.Cancel();
            _processTask?.GetAwaiter().GetResult();
        }
    }
}
Запуск в DI
C#
1
.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>()
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 17:05  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А зачем?
Мне нужно вызвать метод при загрузке приложения, то есть в конструкторе. Хочу, чтобы метод работал асинхронно. Ну чтобы UI не блокировать, пока данные капают из БД.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 17:05
Цитата Сообщение от Vissmuy Посмотреть сообщение
Когда нажимаю на кнопку, происходит следующее.
Нужна команда с параметром DeleteBird(Bird bird).
Команду уровня коллекции.
Передача элемента в праметр CommandParameter="{Binding}".

Цитата Сообщение от Vissmuy Посмотреть сообщение
Обновляем UI с новыми данными.
Вот этого точно не должно быть в команде.
Обновление состояния Модели должно отслеживать через события Модели. В данном случае через Наблюдаемую Коллекцию отражающую локальный кеш Модели.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 17:13  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
отражающую локальный кеш Модели.
Да с этим еще надо разобраться. Что это за кеш такой и с чем его едят.

Могу ли я тут задать пару вопросов не по самой теме, но также из wpf mvvm? Или лучше также новую создать, там, думаю, без пивка тоже не обойдется.

Добавлено через 4 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Команду уровня коллекции.
XML
1
2
3
4
5
6
7
<ItemsControl ItemsSource="{Binding Birds}"
              Margin="10 20 20 0">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <view:BirdView Margin="50 0 0 30" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
То есть мне тут нужна кнопка, у которой будет CommandParameter="{Binding}". Правильно ли я понял?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 17:16
Цитата Сообщение от Vissmuy Посмотреть сообщение
Мне нужно вызвать метод при загрузке приложения, то есть в конструкторе.
Ну, так вызывайте, но не конструктор, а метод.
Чтобы дальше код продолжал работать, надо же предоставить ссылку на экземпляр. А для этого нужно завершить конструктор.

Вместо этого:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SomeItem
{
    public string SomeProperty ....
 
    public SomeItem()
    {
        SomeLongMethod();
    }
 
    private void SomeLongMethod()
    {
        Thread.Sleep(5000); // Имитация долго выполнения.
 
        SomeProperty = result;
    }
}
C#
1
2
3
4
5
6
7
  .... void OnStartup(.....)
  {
      // чё-то там
 
      wind.DataContext = new SomeItem();
      Wind.Show();
  }
Нужно:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SomeItem
{
    public string SomeProperty ....
 
    public SomeItem()
    {
        // SomeLongMethod();
    }
 
    public async Task SomeLongMethodAsync() => await Task.Run(SomeLongMethod);
    private void SomeLongMethod()
    {
        Thread.Sleep(5000); // Имитация долго выполнения.
 
        SomeProperty = result;
    }
}
C#
1
2
3
4
5
6
7
8
9
10
  ....async void OnStartup(.....)
  {
      // чё-то там
 
      SomeItem item = new();    // Получили ссылку на экземпляр
      wind.DataContext = new SomeItem(); // Предоставили ссылку на экземпляр
      Wind.Show();                 // Показали Окно
 
      await item.SomeLongMethodAsync(); // Запустили инициализацию
  }
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 17:16  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Если же нужен результат выполнения, то надо для этого создать метод, а не конструктора вызывать.
Есть метод LoadBirds, в нем происходит получение данных из бд и их последующее добавление в наблюдаемую коллекцию.
Можно ли сделать это все так, чтобы я видел как элементы добавляются в реальном времени? То есть они добавляются, я могу там тыкнуть чего-нибудь, перенести окно, свернуть и тд.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 17:19
Цитата Сообщение от Vissmuy Посмотреть сообщение
То есть мне тут нужна кнопка, у которой будет CommandParameter="{Binding}". Правильно ли я понял?
Да.
Тогда команда получит текущий контекст данных, то есть, в данном случае, элемент коллекции с которым нужно работать.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 17:24  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
async void OnStartup(.....)
Так, окей. Это при загрузке приложения. Полезно знать.

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


Те две команды привязаны к первым двум кнопкам (на кнопку Import не смотрим, ее нет).
Название: изображение_2024-12-27_232143147.png
Просмотров: 59

Размер: 4.8 Кб

Соответственно, когда я нажимаю на Birds, происходит загрузка ViewModel, соответственно мне нужен такой же метод OnSturtup, только для обычной ViewModel.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 17:25  [ТС]
Код
C#
1
2
3
4
5
6
7
8
public RelayCommand ToABViewCommand => new RelayCommand
    (
        execute => Navigation.NavigateTo(App.ServiceProvider!.GetRequiredService<ABViewModel>()
    ));
public RelayCommand ToBirdsViewCommand => new RelayCommand
    (
        execute => Navigation.NavigateTo(App.ServiceProvider!.GetRequiredService<BirdsViewModel>()
    ));
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 17:31
Цитата Сообщение от Vissmuy Посмотреть сообщение
Что это за кеш такой и с чем его едят.
В вашем случае, вам нужен ToObservableCollection.
Но чтобы его использовать нужен постоянный контекст БД на весь сеанс приложения.

Добавлено через 1 минуту
Цитата Сообщение от Vissmuy Посмотреть сообщение
Это при загрузке приложения. Полезно знать.
Это пример. Можно так же вызывать из события инициализации или загрузки Окна.

Но обычно все слои, связи между ними, их инициализация и пр. - производzтся в App, а не в Окнах.

Добавлено через 3 минуты
Цитата Сообщение от Vissmuy Посмотреть сообщение
Соответственно, когда я нажимаю на Birds, происходит загрузка ViewModel,
Загрузка или создание? Или и то и другое вместе?

Если создание, то надо предоставить ссылку на экземпляр ViewModel. Кому предоставить?
А у же после этого загружать данные в неё.

"нажимаю" - это тоже событие. И в него нет проблем вставить выполнение асинхронного метода.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
27.12.2024, 18:00  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
нажимаю" - это тоже событие.
Так разве в MVVM есть события?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Загрузка или создание? Или и то и другое вместе?
При вызове конструктора у ViewModel - это что? Создание?

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Кому предоставить?
Хороший вопрос. Ссылку по идее можно через ServiceProvider взять. Только я не догоняю зачем.

Конструктор вызывается при создании класса. Мне как раз таки нужно, чтобы при создании класса был вызван метод асинхронный с загрузкой данных из бд без блока интерфейса.

Вы подсказали про OnStatrup из класса App, это да, но у меня эта модель не сразу создается при запуске приложения, а только по нажатию кнопки в самом приложении.

Добавлено через 1 минуту
Можно было бы взять событие ViewModel_Loaded и там такое проворачивать, но это секция CodeBehind, а у нас тут mvvm.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
27.12.2024, 18:33
Цитата Сообщение от Vissmuy Посмотреть сообщение
Так разве в MVVM есть события?
Конечно.

INPC (INotifyPropertyChanged) и получение данных из Модели [WPF, Элд Хасп]

Цитата Сообщение от Vissmuy Посмотреть сообщение
При вызове конструктора у ViewModel - это что? Создание?
Конструктор - это создание: выделение памяти, её инициализация и возврат ссылки на неё.

Загрузка - это часть инициализации.

В общем случае, важно понимание что нужно для работы дальнейшему коду.
Если ему нужна только ссылка на экземпляр, и его состоянием позволяет сделать отложенную загрузку (для View - это почти всегда так), то в конструкторе размещается только "быстрый" код. Если требуется долгая инициализация, то она выводится из конструктора в отдельный метод, который вызывается асинхронно тем или иным способом. В случае WPF для нормальной работы механизма извещения об исключениях, необходимо вызывать весь код из синхронного в UI потоке. Проще всего это сделать в событиях. События позволяют подключать асинхронные обработчики. Поэтому наиболее частая практика, это вызов загрузки (долгой) объекта в каком-нибудь "удобном" событии.

Добавлено через 4 минуты
Цитата Сообщение от Vissmuy Посмотреть сообщение
Ссылку по идее можно через ServiceProvider взять. Только я не догоняю зачем.
Конструктор вызывается при создании класса. Мне как раз таки нужно, чтобы при создании класса был вызван метод асинхронный с загрузкой данных из бд без блока интерфейса.
Такое ощущение что вы "залипли" на текущую реализацию и мыслите её категориями.
Скорее всего, вам нужно слегка поменять архитектуру данных, взаимных вызов.
Как конкретно это сделать - сказать сложно, не видя вашего кода.

Можете сделать упрощённый по максимуму пример демонстрирующий нужный вам функционал. Чтобы это было Решение и без загрузки данных из БД, но с какой-то достаточной инициализацией исходных данных.
Загрузите его на GitHub, дайте ссылку. На таком примере будет гораздо проще показать как лучше сделать.
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
28.12.2024, 07:41  [ТС]
От лишней BirdViewModel я избавился. Теперь я беру поля у Entity и вывожу их, но вот проблема.

В самом низу есть кнопка и TextBox, кнопка привязана к команде, окей. Но TextBox я могу связать только со свойствами модели из коллекции Birds, а мне нужно связать его со свойством из связанной ViewModel. То есть выйти за границы коллекции. Как этого можно добиться?

XML
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
<ItemsControl ItemsSource="{Binding Birds}"
              Margin="10 20 20 0">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}"
                           Margin="5 0 20 0"
                           Foreground="White"
                           FontSize="24"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
 
                <TextBlock Text="{Binding Description}"
                           Margin="0 0 20 0"
                           Foreground="White"
                           FontSize="24"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
 
                <TextBlock Text="{Binding Arrival}"
                           Margin="0 0 20 0"
                           Foreground="White"
                           FontSize="24"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
 
                <TextBlock Text="⚊"
                           Margin="0 0 20 0"
                           Foreground="White"
                           FontSize="24"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
 
                <TextBlock Text="{Binding Departure}"
                           Margin="0 0 20 0"
                           Foreground="White"
                           FontSize="24"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center" />
 
                <StackPanel>
                    <Button Style="{StaticResource CSButtons}"
                            BorderThickness="0"
                            Background="#FF483D8B"
                            Padding="5"
                            Margin="0 0 0 0"
                            HorizontalContentAlignment="Center"
                            Command="{Binding DeleteBirdCommand}"
                            CommandParameter="{Binding}"
                            Visibility="{Binding Visibility}">
                        <TextBlock Text="Удалить"
                                   Margin="0 0 0 0"
                                   Foreground="White"
                                   FontSize="24"
                                   VerticalAlignment="Center"
                                   HorizontalAlignment="Center" />
                    </Button>
 
                    <TextBox Foreground="AliceBlue"
                             HorizontalAlignment="Center"
                             MaxLength="10"
                             Padding="5"
                             Height="40"
                             BorderBrush="Black"
                             VerticalContentAlignment="Center"
                             HorizontalContentAlignment="Center"
                             FontSize="20"
                             Background="Transparent"
                             Text="{Binding Departure}"
                             CaretBrush="AliceBlue"
                             Margin="0 0 0 0"
                             Visibility="{Binding Visibility}" />
 
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
0
 Аватар для Andrey-MSK
3388 / 2274 / 388
Регистрация: 14.08.2018
Сообщений: 7,695
Записей в блоге: 4
28.12.2024, 08:33
Цитата Сообщение от Vissmuy Посмотреть сообщение
Так нужно, чтобы при загрузке такой метод вызывался.
Я делаю через свойства VM с помощью выше приведённого сервиса.
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
// Проверка состояния свойства
public SynchronizationContext ViewContext
{
    get => _viewContext;
    set
    {
        if (Set(ref _viewContext, value))
        {
            // Если всё нормально, то запускаем инициализацию данных VM
            if (ViewContext != null)
                _taskQueue.QueueBackgroundWorkItem(LoadVMDataAsync);
        }
    }
}
 
// Метод загрузки данных из БД для работы VM
private async Task LoadVMDataAsync()
{
    try
    {
        List<PPWorkHBGroup> groups = await _mainDA.PPWorkHBDA.GetPPWorkGroupsAsync(Branch, EObject);
        List<LaborHBType> types = await _mainDA.LaborHBTypeDA.GetAllAsync(Branch, EObject);
 
        ViewContext.Send(_ =>
        {
            if (PPGroups.Count > 0)
                PPGroups.Clear();
 
            if (LaborHBTypes.Count > 0)
                LaborHBTypes.Clear();
 
            if (groups.Count > 0)
                groups.ForEach(g => PPGroups.Add(g));
 
            if (types.Count > 0)
                types.ForEach(t => LaborHBTypes.Add(t));
 
            PPGroupsLoaded?.Invoke(this, EventArgs.Empty);
        }, null);
    }
    catch (Exception ex)
    {
        _windowService.ShowErrorMessage(ex.Message, "Ошибка");
    }
}
0
 Аватар для 2033
4 / 4 / 1
Регистрация: 26.12.2024
Сообщений: 225
28.12.2024, 09:42  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Я делаю через свойства VM с помощью выше приведённого сервиса.
Хорошо, с этим буду чуть позже разбираться.

Цитата Сообщение от Vissmuy Посмотреть сообщение
В самом низу есть кнопка и TextBox, кнопка привязана к команде, окей. Но TextBox я могу связать только со свойствами модели из коллекции Birds, а мне нужно связать его со свойством из связанной ViewModel. То есть выйти за границы коллекции. Как этого можно добиться?
Сделал Xaml чуть короче, убрав лишнее, дабы глаза не мылило.
XML
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
<ItemsControl ItemsSource="{Binding Birds}"
              Margin="10 20 20 0">
    
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                
                <TextBlock Text="{Binding Name}"/> <!--Все эти четыре свойства привязаны 
к свойствам Entity из коллекции Birds-->
 
                <TextBlock Text="{Binding Description}"/>
 
                <TextBlock Text="{Binding Arrival}"/>
 
                <TextBlock Text="{Binding Departure}" />
 
                <StackPanel>
                    
                    <Button Command="{Binding DeleteBirdCommand}"
                            CommandParameter="{Binding}"
                            Visibility="{Binding Visibility}"> <!--Вот тут нужно как бы выйти за пределы коллекции и 
привязать к свойству не коллекции, а ViewModel-->
                        <TextBlock Text="Удалить" />
                    </Button>
 
                    <TextBox Text="{Binding Departure}" <!--Тут тоже самое, надо сделать привязку к свойству в vm, 
но тут насильно идет привязка с свойствам Entity-->
                             Visibility="{Binding Visibility}" />
 
                </StackPanel>
                
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
28.12.2024, 10:51
Цитата Сообщение от Vissmuy Посмотреть сообщение
мне нужно связать его со свойством из связанной ViewModel. То есть выйти за границы коллекции. Как этого можно добиться?
Надо мыслить категориями "источников".
Для привязок доступны источники:
1) по умолчанию - это DataContext для FrameworkElement, для Freezable (и некоторых других типов) это DataContext родительского FrameworkElement (родительский это в чьём Dependency Property или Resources объявляется);
2) Source - в этом свойстве можно прямо задать объект источник;
3) ElementName - имя элемента зарегистрированное в текущей области имён. Для XAML это x:Name;
4) RelativeSource - можно как источник: себя, предка по типу, родителя шаблона и др.
Явно можно задавать только один из источников.

В вашем вопросе, источник по умолчанию - это элемент коллекции. Такой источник вам не подходит.
Вам нужна VM установленная на уровне ItemsControl, или даже выше на уровне окна. Можно использовать два варианта (показываю для уровня окна).

По контексту данных предка:
XML
1
    <TextBox Text="{Binding DataContex.Departure, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
По контексту данных элемента с именем:
XML
1
2
3
4
<Window --------------------------
              x:Name="wind">
 
    <TextBox Text="{Binding DataContex.Departure, ElementName=wind}"/>
Но мне нравится больше другой, третий способ. В общем случае мы создаём источники на уровне App. Очень удобно их агрегировать в свойствах какого-то общего контейнера. Часто его называют "Локатор". Это очень простой тип.
Например:
C#
1
2
3
4
5
public class Locator : ViewModelBase
{
    private SomeViewModel _mainVM;
    public SomeViewModel MainVM {get => _mainVM; set => Set(ref _mainVM, value);}
}
Его экземпляр устанавливается в ресурсах App:
XML
1
2
3
   <Application.Resources>
        <local:Locator x:Key="locator"/>
    </Application.Resources>
В App.Startup инициализируете свойства локатора. Если для инициализации достаточно конструктора по умолчанию, то можно инициализировать в XAML.

И потом на любом уровне, в любом элементе:
XML
1
    <TextBox Text="{Binding MainVM.Departure, Source={StaticResource locator}"/>
Можно и напрямую экземпляр VM задать в ресурсах, но это возможно только если у неё есть конструктор по умолчанию:

XML
1
2
3
   <Application.Resources>
        <vm:SomeViewModel x:Key="mainVM"/>
    </Application.Resources>
Тогда упрощается путь к свойству.
XML
1
    <TextBox Text="{Binding Departure, Source={StaticResource mainVM}"/>
В случае однооконного приложения, или если экземпляр VM нужно использовать только в одно Окне, то можно задавать его в ресурсах Окна. Но будут нюансы с его использованием в элементах у которых есть свой собственный XAML: Page, UserConrol и др.

Добавлено через 11 минут
P.S. В случае задания экземпляра VM без локатора, не получится в сеансе работы заменить экземпляр. Расширение разметки {StaticResource ....} разрешается еждиножды. Хотя почти всегда экземпляр основной VM создаётся единожды при старте приложения, ещё до старта первого окна, всё же надо помнить об этом ограничении, чтобы не напороться на него в тех редких случаях, когда требуется смена экземпляра.

Добавлено через 1 минуту
Есть ещё вариант с использование Синглтона. Тоже применим только в том случае, если экземпляр VM создаётся единожды конструктором без параметров.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
28.12.2024, 10:51

Mvvm ViewModel в ViewModel
Есть одно окошко там много вкладок, в каждой вкладке есть подвкладки и там много разных таблиц и кнопок. ViewModel будет просто огромной,...

Как внедрить БД в exe на VB 6.0
Hi! Есть база на Access 7.0 и проект на VB 6.0. Можно ли как-нибудь всунуть .mdb файл в exe-шник, чтобы никто не мог его открыть и...

Как внедрить функцию
Здравствуйте, есть программа которая считает в edit сколько открывающихся скобок и закрывающихся. мой код выглядит так: procedure...

как с внедрить свой код
Всем привет! Не подскажите как можно создать свою папку в вордпресссом и внедрить свой код. Сейчас есть сайт на вордпресс, там нужно...

Как внедрить ProgressBar в макрос
Добрый день! почитал соседние темки на эту тему, нашел у товарища analyst архив с замечательными формами ProgressBar но так как...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru