#c# #xaml #data-binding #desktop #winui-3
Вопрос:
В WinUI 3 в настольном приложении у меня есть свойство для обновления, которое привязано к пользовательскому интерфейсу через x:Bind
.
Я хочу использовать Dispatcher
то, что я делаю в WPF, чтобы попасть в поток пользовательского интерфейса и избежать ошибки потока, которую я получаю при обновлении prop:
System.Runtime.InteropServices.COMException: 'The application called an interface that was marshalled for a different thread. (0x8001010E (RPC_E_WRONG_THREAD))'
Я просто не уверен, как это сделать в WinUI 3, когда я пытаюсь
DispatcherQueue.GetForCurrentThread().TryEnqueue(() =>
{
AeParty.OnSyncHub = false; // Prop bound in ui using x:Bind
});
Я получаю эту ошибку
DispatcherQueue.GetForCurrentThread()
равно нулю
Я также пытался:
this.DispatcherQueue.TryEnqueue(() =>
{
AeParty.OnSyncHub = false;
});
но он не будет компилироваться:
Затем я обнаружил эту проблему с GitHub, поэтому я попытался:
SynchronizationContext.Current.Post((o) =>
{
AeParty.OnSyncHub = false;
}, null);
Это работает, но почему я не могу попасть в поток пользовательского интерфейса с помощью диспетчера в моей виртуальной машине?
Ответ №1:
DispatcherQueue.GetForCurrentThread()
возвращает a только DispatcherQueue
при вызове в потоке, который на самом деле имеет a DispatcherQueue
. Если вы вызываете его в фоновом потоке, его действительно не DispatcherQueue
нужно возвращать.
Итак, хитрость заключается в вызове метода в потоке пользовательского интерфейса и сохранении возвращаемого значения в переменной, которую вы затем используете из фонового потока, например:
public sealed partial class MainWindow : YourBaseClass
{
public MainWindow()
{
this.InitializeComponent();
}
public ViewModel ViewModel { get; } = new ViewModel();
}
public class ViewModel : INotifyPropertyChanged
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
public ViewModel()
{
Task.Run(() =>
{
for (int i = 0; i < 10; i )
{
string val = i.ToString();
_dispatcherQueue.TryEnqueue(() =>
{
Text = val;
});
Thread.Sleep(2000);
}
});
}
private string _text;
public string Text
{
get { return _text; }
set { _text = value; NotifyPropertyChanged(nameof(Text)); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Комментарии:
1. Если вы определяете
string propertyName = null
вместо этого, вы можете просто использоватьNotifyPropertyChanged()
без необходимости вручную указывать имя свойства. Также в общем случае лучше проверить, не равно ли _text значению, прежде чем вызывать событие.