首页 > 解决方案 > 当代码从主 UI 线程移动到类时,UI 组件在当前上下文中不存在

问题描述

我目前有在 MainWindow.xaml.cs 中实现的工作代码,我正试图移动到一个类,这给了我一个错误,即我的 UI 标签在当前上下文中不存在。

这是在 MainWindow 中工作的代码:

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);
        }
    }

然后我尝试将代码移动到一个单独的类:

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();

从类RX。我按照推荐的 var m_dispatcher = Application.Current.MainWindow 尝试了这个以进入 MainWindow 线程,但它仍然给出错误。

标签: wpftask-parallel-library

解决方案


您不能从 MyWindow 以外的其他类访问 mylbl,因为它是在那里定义的。您可以实现 MVVM 并将内容属性绑定到视图模型中的字符串并更新内容。

或将您的业务逻辑分离为单独的类并将其公开给 MyWindow.Xaml.cs。

您可以有一个在 RX 中返回“值”的公共方法。您可以通过访问此方法更新 MyWindow.xaml.cs 中的内容

或将 Label 实例传递给 ProcessMessage 方法并更新内容。当然,在你的类中添加一个引用 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 字符串属性绑定到 Label 的 Content 属性。并将顶部的数据内容设置为我的视图模型。此外,为了将事件绑定到命令,我使用了交互 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 进行绑定。

我假设您了解以下主题,如果没有,在这些方面的堆栈溢出方面有很多帮助

  1. INotifyProertyChanged
  2. 事件到命令绑定
  3. 什么是数据上下文以及如何设置数据上下文
  4. 什么是 ICommand 和实现 ICommand

推荐阅读