我正在构建一个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);
}
当设置了“加载”属性时,视图会通过“加载”消息进行更新,以使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));
那么,我做错了什么?再次,它执行一次,然后在那之后不再执行,尽管执行相同的代码。
答案 0 :(得分: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线程上。 在长时间运行的任务中摆脱调度程序,让它继续。 :)
答案 1 :(得分:1)
当您已经开始转换时,可能还没有更新UI。尝试将加载设置为true和转换器线程的开始之间等待几毫秒:)
答案 2 :(得分:0)
我建议将“Loading = true”移动到任务开始块..但确保在设置“加载”值时使用调度程序。我可能不会给出问题的实际原因,但值得一试......
这样的事情可能有所帮助......
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;
}
答案 3 :(得分:0)
好的,我用windows表单做过,但我认为是相同的
我在Task线程中创建一个Label并调用表单的Invoke
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);
});
}
}