Wpf 当代码从主UI线程移动到类时,UI组件在当前上下文中不存在

Wpf 当代码从主UI线程移动到类时,UI组件在当前上下文中不存在,wpf,task-parallel-library,Wpf,Task Parallel Library,我目前有一个在MainWindow.xaml.cs中实现的工作代码,我正试图将其移动到一个类,该类给我一个错误,即我的UI标签在当前上下文中不存在 以下是在主窗口中工作的代码: public partial class MainWindow : Window { ...... private RX consumer = new RX(); private void Window_Loaded(object sender, RoutedEventArgs e) {

我目前有一个在MainWindow.xaml.cs中实现的工作代码,我正试图将其移动到一个类,该类给我一个错误,即我的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);
        }
    }
然后我尝试将代码移动到一个单独的类:

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线程,但仍然出现错误。

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

或者分离业务逻辑以分离类,并将其公开给MyWindow.Xaml.cs

您可以有一个在RX中返回“value”的公共方法。您可以通过访问此方法更新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);
        }
    }

 }
正如克莱门斯所建议的,我正在以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>
我对命令使用了ICommand实现。 我还使用INotifyPropertyChanged进行绑定

我假设您已经了解了以下主题,如果没有的话,stack overflow中有很多关于这些主题的帮助

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

  • mylbl
    是MainWindow类的成员,可以从同一个类中的Consumer方法访问该类,但显然不能从另一个类访问该类。我是否可以这样做?我见过很多帖子使用这个:var m_dispatcher=Application.Current.MainWindow来访问MainWindow线程,但它仍然不起作用。或者我只是把代码放在主窗口上?你能告诉我怎么做最后的建议吗。我认为这将是实现这一目标的最简单方法。谢谢。我仍然有一个来自mylbl.Content=value.ToString()的错误;另外,我从不从ProcessMessages返回,因为它是一个while(true)循环。它是一个listener.ProcessMessages是一个名为from的后台任务;公共部分类主窗口:窗口{….private RX consumer=new RX();private void Window_Loaded(对象发送方,RoutedEventArgs e){try{Task backgroundDBTask=Task.Factory.StartNew(()=>{consumer.ProcessMessages();},TaskCreationOptions.LongRunning);}}请将mylbl实例传递给ProcessMessages,如编辑的答案所示。并将Label控件作为ProcessMessage方法中的参数
    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
    
    
    }