更新另一个ui线程正在加载的UI线程

时间:2017-05-16 08:19:12

标签: c# wpf multithreading

我正在尝试从第一个窗口(GUI_1)创建辅助窗口(GUI_2),并在GUI_1初始化时从GUI_2更新标签。 (就像加载屏幕一样)。

我尝试使用Task.Factory,Dispatcher,但我认为我缺少某些东西。

public MainWindow()
{
   InitializeComponent();
   Task.Factory.StartNew(() =>
     {
        DispatcherOperation op = Dispatcher.BeginInvoke((Action)(() => {
           Window1 gui = new Window1();
           gui.Show();
        }));
     });
 }

有了这个,我的GUI_2就打开了但如果我试图更新标签,它只显示最后一个值。 (即:a for i = 0到10,thread.sleep只显示10个值)。

我怎样才能做到这一点?

编辑: 我试过这段代码:

public partial class Loading : Window
{
    public static object uiContext;
    private List<string> paths;
    private List<Color>  colors;
    public Loading(List<string> _paths, List<Color> _colors)
    {
        InitializeComponent();
        paths = _paths;
        colors = _colors;
    }
    private async void Window_Loaded(object sender, RoutedEventArgs e)
    {
        Storyboard s = (Storyboard)TryFindResource("sb");
        s.Begin();  // Start animation
        await Task.Run(() => EXCEL.Analogize(paths, this, colors));
        Close();
    }
    public void UpdateWindow(string text, int percent)
    {
        Dispatcher.Invoke(() =>
        {
            progress_label.Content = text;
            progress.Value = percent;
        });
    }
}

在我的EXCEL.Analogize我使用gui_loading.UpdateWindow(text,percent);

它正在运行,但如果我尝试对Windows类做同样的事情,它就不再更新了。

1 个答案:

答案 0 :(得分:1)

可能是在GUI_2 UI任务中忙于加载,并且仅在完成时显示。尝试将更新放在其他任务上并与UI同步。

在GUI_2 ctor中

修改

 public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            _SyncContext = SynchronizationContext.Current;
            UpdateLabel();
        }
        SynchronizationContext _SyncContext = null;
        private async void UpdateLabel()
        {
            await Task.Run(() =>
            {
                for (int i = 1; i < 11; i++)
                {
                    Load(i);
                    Thread.Sleep(1000);
                }
            });

        }

        private void Load(int i)
        {
            SendOrPostCallback updateUI = new SendOrPostCallback(arg =>
            {
                yourLabelName.Content = $"{i}/10";
            });

            _SyncContext.Send(updateUI, null);
        }
    }

我从mainWindow构造函数调用这个窗口:

 Window1 gui = new Window1();
            gui.Show();

这是我测试过的,它对我有用。

编辑2 对的,这是可能的 一种方法是直接在构造函数中传递参数 比如Window1(string text);,当您调用该窗口时,您会传递参数Window1(text); 第二种方式是(我更喜欢这个)制作接口(类似于IWindow)并在其中加入简单的Load方法 像这样

public interface IWindow
    {
       void Load(string text);
    }

public partial class Window1 : Window , IWindow
    {
//here you implement Load(string text) method.

然后你通过IWindow我的MainWindow构造函数与一些DI解决它(Autofac,Unity)Autofac 从mainWindow你可以实例化这样的东西:

// MainWindow构造函数

MainWindow(IWindow iWindow)
{
_iWindow = iWindow;
}
public IWindow _iWindow { get; private set; }

并在MainWindow中使用Load()方法

public void Load()
        {
            _iWindow.Load(textToPassAsParameter);
//this will call Load method from Window1
        }

要调用该加载方法,您可以在xaml中执行此操作 ADD 名称空间

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">

并从MainWindo调用Load方法(将从Window1调用Load)

 <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <ei:CallMethodAction MethodName="Load"
                                 TargetObject="{Binding}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

检查依赖注入因为你将接口作为参数传递,它需要返回使用该接口的类(Window1)。

我强烈建议将MVVM和ViewModels用于ea View,而不是在后面执行代码。

编辑3
我认为你可以用xceed toolkit(NuGet包)来完成你想要的东西,并将BusyContent与一些正在改变的属性绑定(以显示加载进度)

在您的MainWindow的xaml中

围绕您正在加载

的区域

命名空间

xmlns:extToolkit="http://schemas.xceed.com/wpf/xaml/toolkit"

环绕

<extToolkit:BusyIndicator Grid.Row="1"
                                  BusyContent="Loading..."
                                  IsBusy="{Binding IsBusy}">

<!-- your loading area -->

和IsBusy是MainWindow中的属性

private bool _isBusy;
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    OnPropertyChanged();
                }
            }
        }

在你的负载方法

中得到更新
private void Load()
        {
            IsBusy = true;
            // some load logic
            //{
            // IsBusy=false;
            //}
        }

并在MainWindow构造函数中调用该load方法。这样您就不需要Window1,因为此工具包将作为子项放在您的mainWindow中,并将作为进度条。  如果你在BusyContent="This is xceed progress bar..."中写字,你会从xceed那里得到子装载窗口。enter image description here