这是代码示例:
public partial class MainWindow : Window
{
public MainWindow()
{
Closed += MainWindow_Closed;
_items = new(Enumerable.Range(0,10));
}
private ObservableCollection<int> _items;
private async void MainWindow_Closed(object? sender, EventArgs e)
{
await LongTask();
}
private async Task LongTask()
{
await Task.Run(() =>
{
Trace.WriteLine("LongTask Start");
Application.Current.Dispatcher.BeginInvoke(() =>
{
// do some action in the UI thread
_items.Clear();
Trace.WriteLine("Cleared elements");
});
Thread.Sleep(5000);
Trace.WriteLine("LongTask End");
});
}
}
控制台输出切勿打印“已清除元素”或“ longtask end”。 我还在异步任务中提出了一个操作,它需要UI线程来完成操作,这在我的用例中需要。 (因此,一个人不能在主线程中使用longtask()。getawaiter()。getResult(),因为它会导致死锁)。 thanks寻求帮助!!
作为概念证明,这是您可以做到这一点的一种方法。这里的关键是
_confirmClosure
UI可以防止任何重新进入的机会
同时执行保存。,然后在等待保存之后,再次致电
Close
_confirmClosure
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (sender, e) =>
{
if(dataGrid.Columns
.OfType<DataGridTextColumn>()
.FirstOrDefault(c => c.Header.ToString() == "Name") is { } column)
{
// Autosize "Fill" for item name
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
};
Closing += async (sender, e) =>
{
if (_confirmClosure)
{
switch (
MessageBox.Show(
"Do you want to save before closing?",
"Confirm Exit",
MessageBoxButton.YesNoCancel,
MessageBoxImage.Question))
{
case MessageBoxResult.Yes:
e.Cancel = true;
await Dispatcher.BeginInvoke(async () =>
{
IsEnabled = false; // Prevent any more calls.
await DataContext.Save();
_confirmClosure = false;
Close();
});
break;
case MessageBoxResult.No:
break;
case MessageBoxResult.Cancel:
e.Cancel = true;
break;
}
}
};
}
bool _confirmClosure = true;
new MainPageViewModel DataContext => (MainPageViewModel)base.DataContext;
}
test测试的少量VM我们将列出10000个项目的“大”列表,然后在关闭时将其保存到文件中。
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>(
Enumerable.Range(1, 10000)
.Select(_=>new Item { Id = _, Name = $"Item {_}" }));
public event PropertyChangedEventHandler PropertyChanged;
internal async Task Save()
{
try
{
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(() =>
{
var path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"StackOverflow",
Assembly.GetEntryAssembly()?.GetName()?.Name ?? "SaveThenClose");
var json = JsonConvert.SerializeObject(Items);
File.WriteAllText(path, json);
});
// Add a few seconds for good measure, just for demo purposes.
await Task.Delay(TimeSpan.FromSeconds(2.5));
}
finally
{
// Reset the cursor to default
Mouse.OverrideCursor = null;
}
}
}
public class Item
{
public int Id { get; set; }
public string? Name { get; set; }
}
<Window x:Class="save_list_then_close.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:save_list_then_close"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="400"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainPageViewModel/>
</Window.DataContext>
<Grid>
<DataGrid
Name="dataGrid"
ItemsSource="{Binding Items}"
AutoGenerateColumns="True"
IsReadOnly="True"
AlternatingRowBackground="LightGray"/>
</Grid>
</Window>