Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3

Синхронизация Модели с UI потоком

09.01.2024, 12:06. Показов 2769. Ответов 43
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Тема создана разделением исходной: Как определить из кода библиотеки Standard платформу приложения (Forms, WPF, UWP, Framework, Core и т.д.)?

Немного нубский вопрос -- насколько плохо считается прокидывать Dispatcher в модель/команду? Я просто сейчас для себя пишу WPF приложуху, и для отслеживания изменения не из UI потока прям в модель запихиваю диспетчер из окна/контролера. Я так понимаю желательно максимально отвязывать от UI модели/команды, но не особо представляю как это сделать малой кровью.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
09.01.2024, 12:06
Ответы с готовыми решениями:

Модуль EVO II синхронизация 50гц, На какой ножке контроллера синхронизация шим двигателя?
Частый дефект для модулей EVO II с коллекторным двигателем - нет управления двигателем при помехах в сети. Помехи создают диммеры, блоки...

Как обратиться к исходной модели моего класса из прокси-модели в pyqt5?
У меня есть модель my_model класса MyModelClass(QAbstractTableModel), у которой есть переменная класса columns. Я подключаю эту модель к...

Как создать скелет для stl модели по точкам поверхности модели?
нужно придумать алгоритм, чтобы автоматически создавался скелет для stl модели по точкам поверхности модели. Перерыл весь интернет (как мне...

43
 Аватар для Andrey-MSK
3368 / 2254 / 388
Регистрация: 14.08.2018
Сообщений: 7,631
Записей в блоге: 4
10.01.2024, 17:10
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Меня больше интересует другое.
Потому я и делаю как показал выше. Тогда 100% контекст будет именно того окна, который мне в этот момент нужен. Да и во всех примерах со стандартной реализацией, которые я видел, именно такой подход и показан. Загрузилось окно -> захватили контекст -> работаем с коллекциями... Просто в CB окна пара лишних строк и одно свойство в VM
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
10.01.2024, 18:19  [ТС]
Элд Хасп, писал по памяти. WPF, соотвественно CanExecuteChanged и PropertyChanged.

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Wolfdp, Если честно не очень понял что вы пытаетесь сделать...
проблема сводится к тому что из не UI потока обновляется модель, которая через INotifyPropertyChanged должна обновлять привязки на UI.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
10.01.2024, 19:38
Цитата Сообщение от Wolfdp Посмотреть сообщение
писал по памяти. WPF, соотвественно CanExecuteChanged и PropertyChanged.
Для WPF не нужно маршалинга PropertyChanged в UI поток.
Там нужен маршалинг CollectionChanged.

Добавлено через 3 минуты
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
захватили контекст -> работаем с коллекциями..
Здесь задача более общая.
Если работа с коллекциями в разных потоках, то нужно обеспечить потокобезопасную работу с ними. А решение синхронизации с GUI - это незначительная часть этой потокобезопасности.
Поэтому, лично я, считаю плохой практикой использование UI потока для этого.
Только в каких-то крайних случаях и с пониманием, что это костыль.
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.01.2024, 07:01  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Для WPF не нужно маршалинга PropertyChanged в UI поток.
хм... по ходу да -- тупанул. Кажись всё решается проще -- просто в фабрике элементов, создавать все UI объекты в нужном потоке. Сейчас набрал пример без IoC -- все работает без ошибок. Надо будет ковырнуть свою фабрику.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 08:25
Цитата Сообщение от Wolfdp Посмотреть сообщение
Кажись всё решается проще -- просто в фабрике элементов, создавать все UI объекты в нужном потоке.
Ещё проще.
Все UI элементы должны создаваться в XAML (Content, Template, DataTemplate и пр.) и никаких "фабрик UI элементов" не нужно. Шаблоны это и есть их фабрики.

Добавлено через 8 минут
Для "стандартного" WPF (так же и для Форм) UI поток - это основной поток приложения и он не меняется всё время сеанса приложения. Поэтому все теоретические проблемы, о которых я писал выше, там не возникают. Иначе я бы давно создал реализацию с их учётом.

Проблемы возникают, когда "отклоняются от стандарта": вызывают WPF окно из Форм; искусственно создают несколько корневых UI потоков (тех в которых открываются окна); многоплатформенные GUI-приложения и прочее.
Так как нет чётко описания в документации связанности UI элементов с единственным потоком, то в теории возможно реализовать это как угодно. Вот учёт этого "как угодно" и вызывает проблемы.
Насколько помню, только у Форм по документации Контекст Синхронизации связан с потоком 1 к 1.
0
 Аватар для Andrey-MSK
3368 / 2254 / 388
Регистрация: 14.08.2018
Сообщений: 7,631
Записей в блоге: 4
11.01.2024, 08:49
Цитата Сообщение от Wolfdp Посмотреть сообщение
проблема сводится к тому что из не UI потока обновляется модель, которая через INotifyPropertyChanged должна обновлять привязки на UI.
А причём тут модель и INPC? INPC реализуется во ViewModel для обновления UI и ещё чего-нибудь. Именно VM готовит данные для отображения, модели вообще фиолетово куда и кому эти данные улетают и кто ими пользуется.
Паттерн MVVM:
1. Model - бизнес-логика, слой работает сам по себе и не знает вообще ни о чём кроме себя самого и классов DTO.
2. ViewModel - слой для связи Model и View, знает только о Model и о классах DTO.
3. View - слой приложения с UI, в 90% случаев сделан совместно с Application, знает только про ViewModel и классы DTO.
4. Services - всякое вспомогательное нечто, которое через DI внедряется куда надо, не знает вообще ни о чём.
5. Application - связывает все слои вместе с помощью DI или ещё чего-нибудь, включает платформозависимые сервисы и т.д.
Вот такая жесткая иерархия и должна быть у вас. Пункты 1, 2, 3 и 4 - разные проекты в одном решении. И тогда никакого проникновения логики вышестоящего слоя в нижестоящий не будет.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 09:20
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А причём тут модель и INPC? INPC реализуется во ViewModel для обновления UI и ещё чего-нибудь.
1) INPC может быть реализован и на уровне Модели. Этот интерфейс объявлен в сборке System.ObjectModel.dll и относится к типам "общего применения". И то, что он дефолтный для Привязок в WPF не значит, что его применение следует ограничить на этом уровне. Но согласен, что на уровне Модели обычно удобнее применять другие интерфейсы, чаще с кастомными событиями.

2) Использование в VM этого интерфейса не гарантирует, что его событие будет вызываться только в UI потоке. Скорее даже в типичной реализации это событие чаще будет вызываться асинхронно: Модель обновилась асинхронно -> подняла также асинхронно своё кастомное событие -> в этом же потоке VM обновляет своё состояние -> VM подымает асинхронно событие INPC.

Добавлено через 2 минуты
Это же относится и к событиям интерфейсов ICommand, INotifyCollectionChanged, IBindingList и, возможно, DataTable.

Добавлено через 3 минуты
Тот же INotifyCollectionChanged (ObservableCollection), например, используется в EF. А это уровень даже не Модели, а Репозитория.
1
 Аватар для Andrey-MSK
3368 / 2254 / 388
Регистрация: 14.08.2018
Сообщений: 7,631
Записей в блоге: 4
11.01.2024, 09:21
Wolfdp, И ещё по типам проектов, чтоб вообще всё по правильному было
Пункты те же
1. NET Standard
2. В идеале или при использовании сторонних пакетов (ReactiveUI, CommunityToolkit) - NET Standard, если писать только под WPF, использование CommandManager (RelayCommand), то тип Библиотека NET с поддержкой PresentationFramework. Единственный используемый тип из этой библиотеки - CommandManager в реализации RelayCommand().
3 + 5. Приложение WPF
4. NET Standard
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.01.2024, 10:34  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Все UI элементы должны создаваться в XAML
хм... окей, возможно моя идея с UIElementFactory действительно не рабочая. Попробую заменить логику так, чтобы просто менять модель в свойстве, и подтягивалась нужная (я так понял можно указывать какой темплейт подтягивать в зависимости от типа данных).

Сейчас накидал вот вообще по минимуму кода чтобы проверить что к чему -- вроде остается проблема с ICommand -- если команду создаю вне UI потока, то потом валится на CanExecuteChanged. Для команд таки придется прокидывать маршалинг (или как оно правильно называется)?

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<Window x:Class="Nya.CheckThread.MainWindow"
        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"
        mc:Ignorable="d"
        Title="{Binding CurrentTime}" Height="450" Width="800">
    <Grid>
        <Button Content="Button"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Height="47"
                Width="128"
                Command="{Binding GetTime}"
                />
 
    </Grid>
</Window>
C#
1
2
3
4
5
6
7
8
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = Task.Factory.StartNew(() => new UIModel()).Result;
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UIModel : INotifyPropertyChanged
{
    private string currentTime = "none";
    private UICommand uiCommand;
 
    public UIModel()
        => uiCommand = new(this);
 
    public string CurrentTime
    {
        get => currentTime;
        set
        {
            currentTime = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentTime)));
        }
    }
 
    public event PropertyChangedEventHandler? PropertyChanged;
 
    public ICommand GetTime => uiCommand;
}
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
public class UICommand : ICommand
{
    private readonly UIModel _model;
    private Task? task;
 
 
    public event EventHandler? CanExecuteChanged;
 
    public UICommand(UIModel model)
        => _model = model;
 
    public bool CanExecute(object? parameter)
        => task is null;
 
    public void Execute(object? parameter)
    {
        task = Task.Run(async () => 
        {
            try
            {
                var i1 = Environment.CurrentManagedThreadId;
                await Task.Delay(TimeSpan.FromSeconds(2));
                var i2 = Environment.CurrentManagedThreadId;
                _model.CurrentTime = $"[{i1} ; {i2}] = {DateTime.Now}";
                task = null;
 
                CanExecuteChanged?.Invoke(this, EventArgs.Empty); // $exception {"The calling thread cannot access this object because a different thread owns it."} System.InvalidOperationException
            }
            catch
            {
                ;
            }
        });
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}
0
 Аватар для Andrey-MSK
3368 / 2254 / 388
Регистрация: 14.08.2018
Сообщений: 7,631
Записей в блоге: 4
11.01.2024, 10:47
Wolfdp, Попробуйте вот такую реализацию - MVVM - Going async with async command
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.01.2024, 12:51  [ТС]
Andrey-MSK, насколько вижу там используется MvvmLight, который в nuget manager имеет пометку This package has been deprecated as it is legacy and no longer maintained.
0
 Аватар для Andrey-MSK
3368 / 2254 / 388
Регистрация: 14.08.2018
Сообщений: 7,631
Записей в блоге: 4
11.01.2024, 14:03
Цитата Сообщение от Wolfdp Посмотреть сообщение
насколько вижу там используется MvvmLight
Там общая реализация по интерфейсу.
We are not forced to use a RelayCommand and we can craft our own ICommand implementation:
Добавлено через 1 минуту
Wolfdp, На GitHub есть исходный код, там ни слова про сторонние библиотеки.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 15:02
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Попробуйте вот такую реализацию - MVVM - Going async with async command
Это, вообще, о другом - об асинхронном исполнении. А речь за маршалинг CanExecuteChanged.

Цитата Сообщение от Wolfdp Посмотреть сообщение
вроде остается проблема с ICommand -- если команду создаю вне UI потока, то потом валится на CanExecuteChanged
Всё верно.
Решать надо либо через захват контекста потоков слушателей - ссылку на мою реализацию я давал выше.
Либо через маршалинг внутри View. Реализовать его можно разными способами. Но все они непросты...
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.01.2024, 15:04  [ТС]
Andrey-MSK, честно говоря не понял задума по IAsyncCommand -- просто пинается таска и по сути все. Более того, этот метод выглядит неправильным: мы меняем статус, выполняем задачу (т.е. дожидаемся завершения), опять меняем статус и только тогда пинаем ивент. По сути UI узнает об изменениях только после завершения.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public async Task ExecuteAsync()
    {
        if (CanExecute())
        {
            try
            {
                _isExecuting = true; // вот кажись тут явно нужен вызов RaiseCanExecuteChanged
                await _execute();
            }
            finally
            {
                _isExecuting = false;
            }
        }
 
        RaiseCanExecuteChanged();
    }
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 15:09
Цитата Сообщение от Wolfdp Посмотреть сообщение
public UICommand(UIModel model)
        => _model = model;
Треш!!!
0
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
11.01.2024, 15:10  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Решать надо либо через захват контекста потоков слушателей
ок, пока остановлюсь на этом. всем спасибо за участие.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 15:10
Цитата Сообщение от Wolfdp Посмотреть сообщение
насколько вижу там используется MvvmLight, который в nuget manager имеет пометку This package has been deprecated as it is legacy and no longer maintained.
Для обучения он вполне хорош по прежнему.
И скорее всего так же до сих пор лидер по загрузкам.
0
11.01.2024, 15:12  [ТС]

Не по теме:

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Треш!!!
возможно. Менять это 100% у себя не буду, смысла сейчас делать по другому нет.

0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16151 / 11272 / 2890
Регистрация: 21.04.2018
Сообщений: 33,145
Записей в блоге: 2
11.01.2024, 15:28
Цитата Сообщение от Wolfdp Посмотреть сообщение
остановлюсь на этом.
Обновлю ссылку на Реализацию Команд с захватом контекста синхронизации потоков слушателей: Исполнение делегатов обобщённого типа

Эти команды работают во всех стандартных приложениях: формы, WPF, UWP, Avalonia и др.
В приложениях с "перекрёстными" платформами (например, из Форм вызывается WPF окно) - не тестировал.

Сам базовый класс для событий можно использовать с любым типом событий: PropertyChanged, CollectionChanged и прочими.

Добавлено через 1 минуту
Цитата Сообщение от Wolfdp Посмотреть сообщение
Менять это 100% у себя не буду, смысла сейчас делать по другому нет.
Надо!

Команда должна инициализироваться передачей в неё одного или двух делегатов, а не Модели.

Добавлено через 6 минут
"Стандартная" простейшая реализация команд с МЕТАНИТ:
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
using System;
using System.Windows.Input;
 
namespace MVVM
{
    public class RelayCommand : ICommand
    {
        private Action<object> execute;
        private Func<object, bool> canExecute;
 
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
 
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            this.execute = execute;
            this.canExecute = canExecute;
        }
 
        public bool CanExecute(object parameter)
        {
            return this.canExecute == null || this.canExecute(parameter);
        }
 
        public void Execute(object parameter)
        {
            this.execute(parameter);
        }
    }
}
И её использование:
C#
13
14
15
16
17
18
19
20
21
22
23
24
25
        public RelayCommand AddCommand
        {
            get
            {
                return addCommand ??
                  (addCommand = new RelayCommand(obj =>
                  {
                      Phone phone = new Phone();
                      Phones.Insert(0, phone);
                      SelectedPhone = phone;
                  }));
            }
        }
Можно упростить в современном Шарпе:
C#
13
14
15
16
17
18
        public RelayCommand AddCommand =>addCommand ??= new RelayCommand(obj =>
        {
            Phone phone = new Phone();
            Phones.Insert(0, phone);
            SelectedPhone = phone;
         });
Добавлено через 4 минуты
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
есть исходный код, там ни слова про сторонние библиотеки.
В MvvmLight есть своя реализация команд, но он позволяет использовать и любую другую, из любой другой сторонней библиотеки.
Нет жёсткой завязки именно на свою реализацию. Именно за это здесь и написано.
0
11.01.2024, 15:32  [ТС]

Не по теме:

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

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
11.01.2024, 15:32
Помогаю со студенческими работами здесь

При выборе из Ad модели выбрать по кол-ву в связанной модели ad_images
Всем привет В laravel 6 с фильтром из таблицы ads и хочу установить условие чтобы возвращались только с существующими каритинками в...

Выбор в таблице данных модели ссылки на экземпляр связанной модели
Есть ли какой-то автоматизированный способ вывести на страницу таблицу с записями модели, одно из полей которой ссылается на связанную...

Как вытащить значение поля модели внутри самой модели
Здравствуйте. При написании моего проекта возник следующий вопрос. Имеется модель: class Candidate(models.Model): ...

Как использовать представление одной модели в представлении другой модели?
Добрый день! Хотел бы узнать как использовать представление одной модели в представлении другой модели? В Yii2 совсем новичок. ...

Скрыть все 3д модели или их части что находятся за границами другой модели
Есть стекло, за ним находиться 3д модель персонажа почти в притык, и надо что бы если персонаж будет вылезать например в право или в лемо...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Опции темы

Новые блоги и статьи
[golang] Двоичная куча, min-heap
alhaos 20.05.2026
Двоичная куча Двоичная куча — структура данных, которая всегда держит самый важный элемент наготове. Представьте очередь к хилеру в игре, и очередь из игроков в приоритете те у кого меньше. . .
[golang] Breadth-First Search
alhaos 19.05.2026
BFS (Breadth-First Search) — это базовый алгоритм обхода графа в ширину, который поуровнево исследует все связанные вершины. Он начинает с выбранной точки и проверяет всех соседей, прежде чем. . .
[golang] Алгоритм «Хак Госпера»
alhaos 17.05.2026
Алгоритм «Хак Госпера» Хак Госпера (Gosper's Hack) — алгоритм нахождения следующего по величине числа с тем же количеством установленных бит. Придуман Биллом Госпером в 1970-х, опубликован в. . .
Рисование бинарного древа до 6-го колена на js, svg.
russiannick 17.05.2026
<svg width="335" height="240" viewBox="0 0 335 240" fill="#e5e1bb"> <style> <!]> </ style> <g id="bush"> </ g> </ svg> function fn(){ let rost;/ / высота древа let xx=165,yy=210,w=256;
FSharp: interface of module
DevAlt 16.05.2026
Интерфейс модуля F# позволяет управлять доступностью членов, содержащихся в реализации модуля. По-умолчанию все члены модуля доступны: module Foo let x = 10 let boo () = printfn "boo" . . .
Хитросплетение родственных связей пантеона греческих богов.
russiannick 14.05.2026
Однооконник, позволяющий узреть и изучить отдельных героев древней Греции. <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible". . .
[golang] Угол между стрелками часов
alhaos 12.05.2026
По заданным значениям часа и минуты необходимо определить значение меньшего угла между стрелками аналогового циферблата часов. import "math" func angleClock(hour int, minutes int) float64 { . . .
Debian 13: Установка Lazarus QT5
ВитГо 09.05.2026
Эта инструкция моя компиляция инструкций volvo https:/ / www. cyberforum. ru/ blogs/ 203668/ 10753. html и его же старой инструкции по установке Lazarus с gtk2. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru