当代码从主 UI 线程移动到 class 时,UI 组件在当前上下文中不存在
UI component does not exist in current context when code moved to class from main UI thread
我目前有在 MainWindow.xaml.cs 中实现的工作代码,我正在尝试移动到 class,这给我一个错误,我的 UI 标签不存在当前上下文。
这是在主窗口中运行的代码:
public partial class MainWindow : Window
{
......
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { Consumer(consumer);}, TaskCreationOptions.LongRunning);
}
}
public void Consumer(Consumer consumer)
{
while (true)
{
Thread.Sleep(1000);
.......
Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
然后我尝试将代码移动到单独的 class:
public partial class MainWindow : Window
{
....
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { consumer.ProcessMessages(); }, TaskCreationOptions.LongRunning);
}
}
}
public class RX
{
public void ProcessMessages()
{
while (true)
{
Thread.Sleep(1000);
....
var m_dispatcher = Application.Current.MainWindow;
m_dispatcher.Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
}
我收到以下错误:
mylbl.Content = value.ToString();
来自 class RX。我尝试按照推荐的 var m_dispatcher = Application.Current.MainWindow 进入 MainWindow 线程,但它仍然给出错误。
除了 MyWindow 之外,您无法从其他 classes 访问 mylbl,因为它是在此处定义的。
您可以实现 MVVM 并将内容 属性 绑定到视图模型中的字符串并更新内容。
或将您的业务逻辑隔离开来 class 并将其公开给 MyWindow.Xaml.cs。
您可以在 RX 中使用 public 方法 returns "value" 。您可以通过访问此方法
来更新 MyWindow.xaml.cs 中的内容
或将 Label 实例传递给 ProcessMessage 方法并更新内容。当然,在你的 class.
中添加引用 System.Windows.Controls
然而这并不是一个好的设计。我建议你通过 MVVM。
public void ProcessMessages(Label mylbl)
{
while (true)
{
Thread.Sleep(1000);
....
var m_dispatcher = Application.Current.MainWindow;
m_dispatcher.Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
来电者看起来像这样
public partial class MainWindow : Window
{
....
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { consumer.ProcessMessages(mylbl); }, TaskCreationOptions.LongRunning);
}
}
}
按照 Clemens 的建议,我正在以 MVVM 方式更新解决方案。
XAML 部分:
<Window x:Class="MvvmExample.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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MvvmExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid >
<Label Content="{Binding LableContent}" Height="100" Width="500" Foreground="Red"/>
</Grid>
</Window>
我正在将 LableContent 字符串 属性 绑定到标签的内容 属性。并将顶部的数据内容设置为我的视图模型。另外,为了将事件绑定到命令,我使用了交互 dll。
ViewModel 将如下所示。
public class ViewModel : INotifyPropertyChanged
{
#region Constants and Enums
#endregion
#region Private and Protected Member Variables
private string _lableContent;
#endregion
#region Private and Protected Methods
private void OnLoaded(object obj)
{
Task.Factory.StartNew(() => { ProcessMessages(); }, TaskCreationOptions.LongRunning);
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Constructors
public ViewModel()
{
LoadedCommand = new RelayCommand(OnLoaded);
}
#endregion
#region Public Properties
public event PropertyChangedEventHandler PropertyChanged;
public string LableContent
{
get
{
return _lableContent;
}
set
{
_lableContent = value;
OnPropertyChanged(nameof(LableContent));
}
}
public ICommand LoadedCommand { get; }
#endregion
#region Public Methods
public void ProcessMessages()
{
while (true)
{
Thread.Sleep(1000);
LableContent = "your value field";
}
}
#endregion
}
我对命令使用了 ICommand 实现。
我还使用 INotifyPropertyChanged 进行绑定。
我假设您了解以下主题,如果不了解,堆栈溢出中有大量关于这些主题的帮助
- INotifyProertyChanged
- 事件到命令绑定
- 什么是数据上下文以及如何设置数据上下文
- 什么是 ICommand 以及实现 ICommand
我目前有在 MainWindow.xaml.cs 中实现的工作代码,我正在尝试移动到 class,这给我一个错误,我的 UI 标签不存在当前上下文。
这是在主窗口中运行的代码:
public partial class MainWindow : Window
{
......
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { Consumer(consumer);}, TaskCreationOptions.LongRunning);
}
}
public void Consumer(Consumer consumer)
{
while (true)
{
Thread.Sleep(1000);
.......
Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
然后我尝试将代码移动到单独的 class:
public partial class MainWindow : Window
{
....
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { consumer.ProcessMessages(); }, TaskCreationOptions.LongRunning);
}
}
}
public class RX
{
public void ProcessMessages()
{
while (true)
{
Thread.Sleep(1000);
....
var m_dispatcher = Application.Current.MainWindow;
m_dispatcher.Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
}
我收到以下错误:
mylbl.Content = value.ToString();
来自 class RX。我尝试按照推荐的 var m_dispatcher = Application.Current.MainWindow 进入 MainWindow 线程,但它仍然给出错误。
除了 MyWindow 之外,您无法从其他 classes 访问 mylbl,因为它是在此处定义的。 您可以实现 MVVM 并将内容 属性 绑定到视图模型中的字符串并更新内容。
或将您的业务逻辑隔离开来 class 并将其公开给 MyWindow.Xaml.cs。
您可以在 RX 中使用 public 方法 returns "value" 。您可以通过访问此方法
来更新 MyWindow.xaml.cs 中的内容或将 Label 实例传递给 ProcessMessage 方法并更新内容。当然,在你的 class.
中添加引用 System.Windows.Controls然而这并不是一个好的设计。我建议你通过 MVVM。
public void ProcessMessages(Label mylbl)
{
while (true)
{
Thread.Sleep(1000);
....
var m_dispatcher = Application.Current.MainWindow;
m_dispatcher.Dispatcher.BeginInvoke(new Action(() =>
{
mylbl.Content = value.ToString();
}), DispatcherPriority.Background);
}
}
来电者看起来像这样
public partial class MainWindow : Window
{
....
private RX consumer = new RX();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
Task backgroundDBTask = Task.Factory.StartNew(() => { consumer.ProcessMessages(mylbl); }, TaskCreationOptions.LongRunning);
}
}
}
按照 Clemens 的建议,我正在以 MVVM 方式更新解决方案。 XAML 部分:
<Window x:Class="MvvmExample.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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MvvmExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid >
<Label Content="{Binding LableContent}" Height="100" Width="500" Foreground="Red"/>
</Grid>
</Window>
我正在将 LableContent 字符串 属性 绑定到标签的内容 属性。并将顶部的数据内容设置为我的视图模型。另外,为了将事件绑定到命令,我使用了交互 dll。
ViewModel 将如下所示。
public class ViewModel : INotifyPropertyChanged
{
#region Constants and Enums
#endregion
#region Private and Protected Member Variables
private string _lableContent;
#endregion
#region Private and Protected Methods
private void OnLoaded(object obj)
{
Task.Factory.StartNew(() => { ProcessMessages(); }, TaskCreationOptions.LongRunning);
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Constructors
public ViewModel()
{
LoadedCommand = new RelayCommand(OnLoaded);
}
#endregion
#region Public Properties
public event PropertyChangedEventHandler PropertyChanged;
public string LableContent
{
get
{
return _lableContent;
}
set
{
_lableContent = value;
OnPropertyChanged(nameof(LableContent));
}
}
public ICommand LoadedCommand { get; }
#endregion
#region Public Methods
public void ProcessMessages()
{
while (true)
{
Thread.Sleep(1000);
LableContent = "your value field";
}
}
#endregion
}
我对命令使用了 ICommand 实现。 我还使用 INotifyPropertyChanged 进行绑定。
我假设您了解以下主题,如果不了解,堆栈溢出中有大量关于这些主题的帮助
- INotifyProertyChanged
- 事件到命令绑定
- 什么是数据上下文以及如何设置数据上下文
- 什么是 ICommand 以及实现 ICommand