Мой вопрос теперь изменился: я ищу «правильный» шаблон, чтобы иметь возможность хранить SynchronizationContext
или что-то еще в модели представления; чтобы я мог перенаправить внешние вызовы обратно в поток окна.
Моя цель:
- Получить объект, независимый от платформы, в модели представления, который позволяет выполнить маршалинг обратно в поток этого окна.
- Уметь проверять , находится ли исполняемый код в правильном контексте синхронизации или нет.
Если модель представления имеет некоторый входящий код, который может поступать из произвольного потока, то как маршалировать в поток окна? --- И я не хочу хранить 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;
}
}