解
我自己找到了解决方案。 感谢所有回复,它以正确的方式帮助了我。
实际上有一种非常简单的方法。
我刚刚将命令改为Async方法(public async void" MethodName")如果我想在后面执行ExecuteAbortCommand,我就这样做了:
await Task.Run(() => {
ExecuteAbortSessionCommand();
});
它为我完成了这项工作。我希望它会帮助其他人
答案 0 :(得分:0)
在wpf中创建多线程任务时需要记住的几点
首先,除非您的后台进程与视图直接相关,否则您不应该在视图中包含该主题,将其保留在它所属的视图模型中,这将删除您在更新时遇到的大多数问题,因为这些将被处理在绑定中对你的gui没有影响
第二,如果你确实需要做一些相关的视图,只使用Dispatcher.Invoke进行最小可能的事务,即Textbox.Dispatcher.Invoke(()=>Textbox.Text = "something")
这是因为调度程序将锁定你的线程和调度程序附加到的元素它使调度程序执行控件的主GUI线程中的代码
正如你所说的你的任务是服务器相关的,那么它们不是它的视图元素所以如果你把它保存在视图模型中就像它应该那么你的代码看起来应该是这样的
public class LoaderViewModel
{
public LoaderViewModel()
{
loader.DataLoaded += Loader_DataLoaded;
}
public Dispatcher Dispatcher {get; set; }
public ObservableCollection<DataViewModel> Values { get; } = new ObservableCollection<DataViewModel>();
ModelLoader loader = new ModelLoader();
public void LoadData()
{
loader.LoadData();
}
private void Loader_DataLoaded(object sender, IEnumerable<DataModel> e)
{
Dispatcher.Invoke(() => Values.Clear());
foreach (var item in e)
{
Dispatcher.Invoke(()=>Values.Add(new DataViewModel( item)));
}
}
}
public class DataViewModel : INotifyPropertyChanged
{
public DataViewModel(DataModel model)
{
Model = model;
Model.ValueLoaded += Model_ValueLoaded;
}
private void Model_ValueLoaded(object sender, EventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
public event PropertyChangedEventHandler PropertyChanged;
public DataModel Model { get;private set; }
public string Text
{
get { return Model.Value; }
}
}
public class DataModel
{
public DataModel(int id)
{
ID = id;
}
public int ID { get; set; }
private string _Value = "Loading...";
Task backgroundLoad;
public string Value
{
get {
if (backgroundLoad == null && _Value == "Loading...")//background Load on Demand
{
backgroundLoad = Task.Run(async () =>
{
Value = await Server.GetDataValues(ID);
});
}
return _Value;
}
set
{
if (_Value != value)
{
_Value = value;
ValueLoaded?.Invoke(this, EventArgs.Empty);
}
}
}
public event EventHandler ValueLoaded;
}
public class ModelLoader
{
public void LoadData()
{
loading = Task.Run(async()=>
{
var results = (await Server.GetDataIDs()).Select(i => new DataModel(i));
DataLoaded?.Invoke(this, results);
});
}
private Task loading;
public event EventHandler<IEnumerable<DataModel>> DataLoaded;
}
//simulates a slow responding server
public static class Server
{
private static Random rnd = new Random();
public static async Task<IEnumerable<int>> GetDataIDs()
{
await Task.Delay(5000);
return Enumerable.Range(1, 15);
}
public static async Task<string> GetDataValues(int id)
{
await Task.Delay(rnd.Next(100,6000));
return $"Values {id}";
}
}
快速演示的视图我被击倒了
<Window x:Class="WpfApplication1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:LoaderViewModel x:Name="viewModel" />
</Window.DataContext>
<DockPanel>
<Button Click="Clear_Click" DockPanel.Dock="Top" >Clear</Button>
<Button Click="Load_Click" DockPanel.Dock="Top">Load Data</Button>
<ListView ItemsSource="{Binding Values}" ScrollViewer.VerticalScrollBarVisibility="Visible" >
<ListView.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Text}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DockPanel>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
viewModel.Dispatcher = Dispatcher;
}
private void Clear_Click(object sender, RoutedEventArgs e)
{
viewModel.Values.Clear();
}
private void Load_Click(object sender, RoutedEventArgs e)
{
viewModel.LoadData();
}
}
注意:视图只是演示ViewModel
的快速而肮脏的示例答案 1 :(得分:0)
<强>解强>
我自己找到了解决方案。 感谢所有回复,它以正确的方式帮助了我。
实际上有一种非常简单的方法。
我刚刚将命令更改为异步方法(public async void“MethodName”),如果我想在后面执行ExecuteAbortCommand,我只是这样做了:
NSDictionary
它为我完成了这项工作。我希望它会帮助其他人