Я сделал приложение для просмотра, редактирования и создания данных, хранящихся на SQL-сервере.Моя проблема в том, что взаимодействие с SQL Server может занять некоторое время, особенно потому, что эта программа также используется моими коллегами в Китае, а мой сервер находится в Германии.Поэтому я хочу показать диалог с ожидающей анимацией во время загрузки данных.Нет необходимости продолжать работу с моим графическим интерфейсом во время загрузки.Моим первым решением было создать этот класс:
public class LongTimeAction
{
public void Run(Action MyMethod)
{
try
{
var thread = new Thread(() =>
{
Dispatcher.CurrentDispatcher.BeginInvoke((Action)(() => new MyWindows.CMN_Splash().Show()));
Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
MyMethod();
thread.Abort();
}
catch (Exception exp)
{
new Errorlogger().write_Error(this.GetType().Name, System.Reflection.MethodBase.GetCurrentMethod().Name, exp.Message);
}
}
}
Заставка имеет анимацию стиля "Рыцарь Райдер", созданную в XAML, которая запускается автоматически при загрузке окна:
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever" Duration="0:0:1.4">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar1" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.5" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar2" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0.8" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0.6" KeyTime="0:0:0.1" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.1" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.6" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:1.3" />
<LinearDoubleKeyFrame Value="0.8" KeyTime="0:0:1.4" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar3" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0.6" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0.2" KeyTime="0:0:0.2" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.2" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.7" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:1.2" />
<LinearDoubleKeyFrame Value="0.6" KeyTime="0:0:1.4" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar4" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0.4" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.3" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.3" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.8" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:1.1" />
<LinearDoubleKeyFrame Value="0.4" KeyTime="0:0:1.4" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar5" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0.2" KeyTime="0:0:0" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.1" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.4" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.9" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:1" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:1.4" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar6" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.5" />
<LinearDoubleKeyFrame Value="0.2" KeyTime="0:0:0.9" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.9" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:1.3" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar7" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.6" />
<LinearDoubleKeyFrame Value="0.6" KeyTime="0:0:0.8" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.8" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:1.2" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Bar8" Storyboard.TargetProperty="Opacity">
<DiscreteDoubleKeyFrame Value="0" KeyTime="0:0:0" />
<DiscreteDoubleKeyFrame Value="1" KeyTime="0:0:0.7" />
<LinearDoubleKeyFrame Value="0" KeyTime="0:0:1.1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Canvas Grid.Row="1" HorizontalAlignment="Stretch" Height="100" Width="275">
<TextBlock Canvas.Top="12" Canvas.Left="39" FontSize="32" FontWeight="Bold" Foreground="Gray" Text="Loading Data" />
<TextBlock Canvas.Top="10" Canvas.Left="37" FontSize="32" FontWeight="Bold" Foreground="White" Text="Loading Data" />
<Rectangle Canvas.Top="67" Canvas.Left="15" Name="Rect" Width="245" Height="20" Stroke="White" RadiusY="2" RadiusX="2" StrokeThickness="2" Fill="Black" />
<Rectangle Canvas.Top="70" Canvas.Left="18" Name="Bar1" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="48" Name="Bar2" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="78" Name="Bar3" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="108" Name="Bar4" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="138" Name="Bar5" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="168" Name="Bar6" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="198" Name="Bar7" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
<Rectangle Canvas.Top="70" Canvas.Left="228" Name="Bar8" Height="14" RadiusY="2" RadiusX="2" Fill="Red" HorizontalAlignment="Left" Width="29" />
</Canvas>
Это сработало хорошо, но не идеально.Иногда моя программа полностью падает («Приложение не отвечает»), поэтому я искал лучшее решение.Поскольку при загрузке нет необходимости взаимодействовать с моим графическим интерфейсом, я изменил свою программу на async-await.Поэтому я изменил все свои функции, взаимодействующие с SQL, на асинхронные.После этого я изменил все функции, которые вызывают эти функции, на асинхронный и так далее.Теперь по крайней мере половина моей программы является асинхронной :-) Я также изменил свой класс LongTimeAction следующим образом:
public static class LongTimeAction
{
public static async Task Run(Func<Task> MyMethod)
{
MyWindows.CMN_Splash MySplashWindow = new MyWindows.CMN_Splash();
MySplashWindow.Show();
await MyMethod();
MySplashWindow.Close();
}
}
Он называется так:
await LongTimeAction.Run(VT_ReloadAllAction);
В принципе, он работает, ноАнимация в заставке не плавная.Также иногда программа полностью зависает.
Не могли бы вы помочь мне выяснить, в чем моя проблема здесь?
РЕДАКТИРОВАТЬ:
Теперь я скачал расширенный инструментарий WPF и добавил его в свое главное окно:
<wpfx:BusyIndicator Name="BusyBar" BusyContent="Loading Data..." />
В своем коде позади ядобавил эту функцию
public async Task StartLongRunningTask(Func<Task> MyMethod)
{
BusyBar.IsBusy = true;
await MyMethod();
BusyBar.IsBusy = false;
}
, которую я называю так:
await StartLongRunningTask(TT_ReloadAllAction);
Мое приложение работает, но BusyIndicator не отображается.Чего мне не хватает?
EDIT2 + 4
У меня все еще есть проблема, что моя программа зависает, если я запускаю ее, а затем использую некоторые другие окна.Когда я возвращаюсь к своему окну, оно полностью замерзает, и я даже не могу его закрыть. Решено.У меня была проблема в моей процедуре опроса, которая проверяет базу данных каждые минуты просмотра на наличие специального флага.Я сделал это с DispatcherTimer, и это в сочетании с асинхронностью кажется немного сложным.поэтому я изменил это обратно на синхронизацию.Теперь все работает нормально.
EDIT3
Вот мой окружающий код:
public ICommand Cmd_OnlyMyItems { get; set; }
Cmd_OnlyMyItems = new CMN_RelayCommand(Execute => CMN_OnlyMyItems(), CanExecute => true);
private async void CMN_OnlyMyItems()
{
//some Code here
await StartLongRunningTask(VT_ApplyFilterAction);
//some Code here
}
private async Task VT_ApplyFilterAction()
{
await DBConnect.VT_Get_FilteredData_V2();
VT_RefreshGrid();
}
public static class DBConnect
{
public async static Task VT_Get_FilteredData_V2()
{
//some Code here
SqlDataReader reader = command.ExecuteReader()
while (await reader.ReadAsync())
{
//some Code here
}
}
}
Теперь я посмотрю на примеры, приведенные враздел комментариев.