CefSharp с WPF MVVM

Я использую элемент управления CefSharp WPF в своем проекте с шаблоном MVVM. Я попытался показать внешний URL-адрес в браузере Chromium. Я использовал событие LoadingStateChanged для отслеживания события загрузки страницы в браузере Chromium. Я получаю сообщение об ошибке ниже при прослушивании события LoadingStateChanged. Не могли бы вы помочь мне решить проблему?

Сообщение об ошибке:

Вызывающий поток не может получить доступ к этому объекту, потому что другой нить владеет им.

HomeControl.xaml:

<wpf:ChromiumWebBrowser WebBrowser = "{Binding WebBrowser, Mode=OneWayToSource}" Address = "{Binding Address, Mode=OneWay}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName = "LoadingStateChanged">
            <i:InvokeCommandAction Command = "{Binding Path=LoadingStateChangedCmd, Mode=OneWay}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</wpf:ChromiumWebBrowser>

HomeViewModel.cs:

public ICommand LoadingStateChangedCmd { get; set; }

public HomeViewModel() 
{
    LoadingStateChangedCmd = new CommunityToolkitInput.RelayCommand<LoadingStateChangedEventArgs>(LoadingStateChanged);
}

public void LoadingStateChanged(LoadingStateChangedEventArgs args)
{
    if (args.IsLoading == false)
    {
        Dispatcher.CurrentDispatcher.Invoke(() =>
        {
        });
    }
}

Подробное сообщение об ошибке:

System.InvalidOperationException HResult=0x80131509 Сообщение=The вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им. Источник = WindowsBase StackTrace: в System.Windows.Threading.Dispatcher.VerifyAccess() в System.Windows.DependencyObject.GetValue (DependencyProperty dp) в System.Windows.Interactivity.TriggerBase.get_Actions() в System.Windows.Interactivity.TriggerBase.InvokeActions(Объект параметр) в System.Windows.Interactivity.EventTriggerBase.OnEvent(EventArgs eventArgs) в System.Windows.Interactivity.EventTriggerBase.OnEventImpl(Объект отправитель, EventArgs eventArgs) в CefSharp.Wpf.ChromiumWebBrowser.CefSharp.Internals.IWebBrowserInternal.SetLoadingStateChange(LoadingStateChangedEventArgs аргументы) в CefSharp.Internals.ClientAdapter.OnLoadingStateChange(ClientAdapter*, scoped_refptr* браузер, логическое значение isLoading, логическое значение canGoBack, логическое значение canGoForward)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

Также не имеет отношения, но вместо этого вы должны использовать WebView2, который предназначен для рендеринга непосредственно в стеке окон XAML. В отличие от CefBrowser, который, насколько мне известно, больше похож на остров XAML, покрывая элементы управления, которыми он должен управлять, и имеет большие последствия для производительности.

Blindy 06.06.2023 18:13

Спасибо за ваш вклад. Позвольте мне попробовать с WebView2.

RGS 06.06.2023 18:51

Чтобы было ясно, они ведут себя одинаково в этом отношении. Преимущество WebView2 заключается в том, что это полностью поддерживаемый элемент управления WPF (а также UWP, WinForms и WinUI 2 и 3), а не предоставленный сообществом.

Blindy 06.06.2023 19:07

Если я помещаю события обратного вызова, т.е. LoadingStateChanged в код представления за файлом С#, это нарушает дизайн MVVM. Правильно ли я понимаю? Есть ли лучший подход, кроме этого?

RGS 06.06.2023 19:29

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

Blindy 07.06.2023 16:22
WebView2 на момент написания предоставляет только HwndHost основанную WPF реализацию CefSharp предоставляет две реализации CefSharp.Wpf собственный WPF элемент управления, он делает это путем рендеринга каждого кадра в виде растрового изображения и пересылки событий в браузер, это действительно влияет на производительность. Три тоже CefSharp.Wpf.HwndHost, что тоже HwndHost. Реализации на основе HwndHost будут иметь AirSpace проблемы, вы не сможете накладывать другие элементы управления, выполнять преобразования и т. д. См. Learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/…
amaitland 10.06.2023 00:48

Реализация CefSharp.Wpf не имеет отношения к XAML Islands. Он будет участвовать в WPF рендеринге так же, как и любой обычный WPF элемент управления. HwndHost реализации не будут корректно участвовать в WPF рендеринге (см. проблемы AirSpace). См. также github.com/MicrosoftEdge/WebView2Feedback/issues/286

amaitland 10.06.2023 00:52

LoadingStateChanged событие по умолчанию вызывается на CEF UI Thread, которое по умолчанию отличается от вашего WPF UI Thread. См. MultiThreadedMessageLoop для получения дополнительной информации по этому вопросу. Вы можете интегрировать CEF в существующий цикл сообщений вашего приложения, хотя обычно в WPF в этом нет необходимости.

В качестве альтернатив вашему текущему коду есть несколько вариантов.

Опция 1 Событие LoadingStateChangedEventArgs имеет три свойства, для использования MVVM они сопоставляются с тремя DependencyProperties, которые вы можете привязать к своему ViewModel.

Вариант № 2. Вы можете получить доступ к WebBrowser в своей модели просмотра.

// ViewModel property
private IWpfWebBrowser webBrowser;
public IWpfWebBrowser WebBrowser
{
    get { return webBrowser; }
    set { Set(ref webBrowser, value); }
}
<!-- XAML bind the WebBrowser property to your ViewModel -->
<cefSharp:ChromiumWebBrowser x:Name = "browser" WebBrowser = "{Binding WebBrowser, Mode=OneWayToSource}"/>

Затем вы можете subscribe/unsubscribe перейти к нужным вам событиям браузера.

Вариант №3. Вы можете подписаться на событие LoadingStateChanged в View codebind и вручную вызвать команду ViewModel.

Другие вопросы по теме