0

我正在构建一个 WPF 应用程序,当您从列表中选择一个时,它会将 powerpoint 转换为 WPF 元素。我正在使用 MVVM 灯将 ViewModel 绑定到我的视图并添加 ViewModel 之间的通信。

我有两个视图:OpenLocalView 和 PresentationView。当我在 OpenLocalView 中选择一个 powerpoint 时,MVVM 灯将向 PresentationView 的 ViewModel 和 MainViewModel 发送一条消息,其中包含该 powerpoint 的路径。MainViewModel 将视图切换到 PresentationView,PresentationViewModel 执行此代码以转换 powerpoint,完成后,设置当前幻灯片,使其显示在 PresentationView 中:

  public void StartPresentation(string location)
  {
        var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
        Loading = true;
        Task.Factory.StartNew(() =>
            {
                var converterFactory = new ConverterFactory();
                var converter = converterFactory.CreatePowerPointConverter();
                _slides = converter.Convert(location).Slides;
            }, 
            CancellationToken.None, 
            TaskCreationOptions.LongRunning, 
            scheduler).ContinueWith(x =>
                {
                    Loading = false;
                    CurrentSlide = _slides.First();
                }, 
                CancellationToken.None, 
                TaskContinuationOptions.OnlyOnRanToCompletion, 
                scheduler);
   }

设置 Loading 属性后,视图会使用“正在加载”消息进行更新,以使 UI 响应更快:

    public Boolean Loading
    {
        get { return _loading; }
        set
        {
            _loading = value;
            RaisePropertyChanged("Loading");
        }
    }

问题是,这在我第一次加载 powerpoint 时正确执行:视图切换到 PresentationView,显示“正在加载”消息,转换完成后,消息消失并显示幻灯片。但是当我回到 OpenLocalView 并选择另一个 powerpoint 时,OpenLocalView 挂起,并在转换器完成后切换到 PresentationView,根本没有显示“正在加载”消息。

作为参考,我将添加一些更相关的代码。

这在 OpenLocalViewModel 中选择了一个 powerpoint 时执行:

    private void PerformOpenPresentation(string location)
    {
        Messenger.Default.Send<OpenPowerPointMessage>(new OpenPowerPointMessage {Location = location});
    }

MainViewModel 订阅了 messenger 并切换视图:

Messenger.Default.Register<OpenPowerPointMessage>(this,
            delegate
            {
                if (_presentation == null) _presentation = new PresentationView();
                CurrentView = _presentation;
            });

PresentationViewModel 也订阅了 messenger 并执行上面显示的方法:

Messenger.Default.Register<OpenPowerPointMessage>(this, message => StartPresentation(message.Location));

那么,我做错了什么?再次,它执行一次很好,然后不再执行,尽管执行了相同的代码。

4

4 回答 4

1

看这里:

 var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
 Loading = true;

Task.Factory.StartNew(() =>
        {
            var converterFactory = new ConverterFactory();
            var converter = converterFactory.CreatePowerPointConverter();
            _slides = converter.Convert(location).Slides;
        }, 
        CancellationToken.None, 
        TaskCreationOptions.LongRunning, 
here  ----> scheduler).ContinueWith(x =>
            {
                Loading = false;
                CurrentSlide = _slides.First();
            }, 
            CancellationToken.None, 
            TaskContinuationOptions.OnlyOnRanToCompletion, 
            scheduler);

您正在同步上下文(即 UI 线程)上启动“长时间运行”任务。在长时间运行的任务中摆脱调度程序,让它继续运行。:)

于 2012-05-30T13:19:39.900 回答
1

当您已经开始转换时,也许 UI 尚未更新。尝试在将 Loading 设置为 true 和转换器线程开始之间等待几毫秒 :)

于 2012-05-31T08:05:05.740 回答
0

我建议将“Loading = true”移动到任务启动块中。但请确保在设置“Loading”值时使用调度程序。我可能不会给出问题的实际原因,但值得一试......

这样的事情可能会有所帮助..

Task.Factory.StartNew(() =>
            {
               System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
                 {
                     Loading = true;
                  });
                var converterFactory = new ConverterFactory();
                var converter = converterFactory.CreatePowerPointConverter();
                _slides = converter.Convert(location).Slides;
            }
于 2012-05-30T13:46:55.930 回答
0

好的,我是用 windows 窗体做的,但我认为是一样的

我在任务线程中创建一个标签并调用表单的调用

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public void AddControl(Control control)
    {
        if (InvokeRequired)
        {
            this.Invoke(new Action<Control>(AddControl), new object[] { control });
            return;
        }
        this.Controls.Add(control);
    }

    private void Form1_Load(object sender, EventArgs e)
    {

        Task.Factory.StartNew(() =>
            {
                var label = new Label
                {
                    Location = new Point(0, 0),
                    Text = "hola",
                    ForeColor = Color.Black
                };
                this.Invoke(new Action<Control>(AddControl), new object[] { label });
            });
    }
}

编辑

好的,如何使用 Dispather.Invoke 我不确定这是否会阻止 UI ....

  public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public void AddControl()
    {
        var l = new Label
        {
            Content = "Label",
            Height = 28,
            HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
            Margin = new Thickness(209, 118, 0, 0),
            Name = "label1",
            VerticalAlignment = System.Windows.VerticalAlignment.Top
        };

        Grid.Children.Add(l);
    }


    private void Grid_Loaded(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(() =>
        {
            Dispatcher.Invoke(new Action(AddControl), null);
        });
    }
}
于 2012-05-30T14:24:15.150 回答