Любой общепринятый шаблон для хранения SynchronizationContext или другого объекта модели представления для маршалла в потоке пользовательского интерфейса? - PullRequest
0 голосов
/ 03 мая 2018

Мой вопрос теперь изменился: я ищу «правильный» шаблон, чтобы иметь возможность хранить SynchronizationContext или что-то еще в модели представления; чтобы я мог перенаправить внешние вызовы обратно в поток окна.

Моя цель:

  1. Получить объект, независимый от платформы, в модели представления, который позволяет выполнить маршалинг обратно в поток этого окна.
  2. Уметь проверять , находится ли исполняемый код в правильном контексте синхронизации или нет.

Если модель представления имеет некоторый входящий код, который может поступать из произвольного потока, то как маршалировать в поток окна? --- И я не хочу хранить Dispatcher; чтобы быть более независимым от платформы.

У меня был диспетчер абстракция , но я избавился от него, потому что думал, что SynchronizationContext в библиотеке core сделает эту работу лучше ... также немного сложнее иметь дело с абстракцией; и SynchronizationContext может быть даже сам подклассом.

Таким образом, я сохранил текущий SynchronizationContext в конструкторе модели представления и использовал метод, чтобы проверить, равен ли текущий контекст, и определить, нужно ли мне публиковать в сохраненном контексте. Одна проблема с этим показана ниже: Wpf фактически создает оболочки для обработчиков событий, и проверка на равенство SynchronizationContext по существу всегда ложна.

Я также обнаружил BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance: https://docs.microsoft.com/en-us/dotnet/api/system.windows.basecompatibilitypreferences?view=netframework-4.7

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

Есть ли какой-нибудь лучший способ, которым модель представления может публиковать, и делать надежные проверки? Я делаю также хочу делать проверки, поскольку некоторый код должен знать, будет ли он публиковать асинхронно ...

Я также сейчас играю с концепцией вызова SetSynchronizationContext в конструкторе window (потому что у меня есть фабрика окон); но это кажется потенциально хакерским. Возможно, мне нужно снова разобраться с моей собственной абстракцией ... С такими моделями представлений приходится сталкиваться с некоторыми сложностями.


Код приложения ниже иллюстрирует только то, что я обнаружил: отслеживание запуска, я смотрю на SynchronizationContext. Главное окно открывается в главном потоке - который является единственным потоком - и имеется диспетчер SynchronizationContext. В обработчике команд кнопки обратный вызов вызывается с другим SynchronizationContext. (Но это все еще в главном потоке.) и , эти контексты не сравниваются равными.

Вывод программы:

App OnStartup: SynchronizationContext.Current: 26765710
MainWindowVIewModel ctor: SynchronizationContext.Current: 26765710
MainWindow ctor: SynchronizationContext.Current: 26765710
Command Execute: SynchronizationContext.Current: 1048160

Весь код:

using System;
using System.Threading;
using System.Windows;

namespace WpfApp1
{
    public partial class App
    {
        protected override void OnStartup(StartupEventArgs e)
            => Console.WriteLine(
                    "App OnStartup:"
                    + " SynchronizationContext.Current:"
                    + $" {SynchronizationContext.Current.GetHashCode()}");
    }
}


<Application x:Class="WpfApp1.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        StartupUri="MainWindow.xaml"
        ShutdownMode="OnMainWindowClose">
    <Application.Resources/>
</Application>


using System;
using System.Threading;

namespace WpfApp1
{
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            Console.WriteLine(
                    "MainWindow ctor:"
                    + " SynchronizationContext.Current:"
                    + $" {SynchronizationContext.Current.GetHashCode()}");
        }
    }
}


<Window x:Class="WpfApp1.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"
        xmlns:wpfApp1="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="232"
        Width="422">
    <Window.DataContext>
        <wpfApp1:MainWindowVIewModel/>
    </Window.DataContext>
    <Grid>
        <Button Content="Button"
                HorizontalAlignment="Left"
                Margin="80,44,0,0"
                VerticalAlignment="Top"
                Width="75"
                Command="{Binding Command}"/>
    </Grid>
</Window>


using System;
using System.Threading;
using System.Windows.Input;

namespace WpfApp1
{
    public class MainWindowVIewModel
    {
        public MainWindowVIewModel()
            => Console.WriteLine(
                    "MainWindowVIewModel ctor:"
                    + " SynchronizationContext.Current:"
                    + $" {SynchronizationContext.Current.GetHashCode()}");


        public ICommand Command { get; }
            = new Command();
    }
}


using System;
using System.Threading;
using System.Windows.Input;

namespace WpfApp1
{
    public class Command
            : ICommand
    {
        public bool CanExecute(object parameter)
            => true;

        public void Execute(object parameter)
            => Console.WriteLine(
                    "Command Execute:"
                    + " SynchronizationContext.Current:"
                    + $" {SynchronizationContext.Current.GetHashCode()}");

        public event EventHandler CanExecuteChanged;
    }
}
...