Есть задача выполнить длительную по времени операция с контролом (WPF), дабы она не блокировала UI решил подсмотреть вариант здесь. По итогу вышел вот такой метод
async void PrintMethod()
{
try
{
await Task.Factory.StartNew(() => messageView.PrintDirect(), TaskCreationOptions.LongRunning);
}
catch(Exception ex)
{
logger.Error("Ошибка печати: {0}", ex.Message);
}
}
Однако, получаю ошибку следующего содержания: "Вызывающим потоком должен быть STA, поскольку этого требуют большинство компонентов UI".
Если бы я создавал поток с использованием Thread
, я бы использовал метод SetApartmentState(ApartmentState.STA)
, а вот что делать в таком случае я не знаю. Есть какие-нибудь вариаты?
Вот так можно показать окно в отдельном потоке и при этом оно будет в режиме STA, да, в этом окне вам придется повторить свой контрол (т.к. просто перенести его на другую форму у вас не получится):
BusyWindow _busyWindow = null;
object _busyWindowSync = new object();
private void ShowBusy()
{
lock (_busyWindowSync)
{
if (_busyWindow == null)
{
double left = Dispatcher.Invoke((Func<double>)(() => this.Left + this.Width / 2));
double top = Dispatcher.Invoke((Func<double>)(() => this.Top + this.Height / 2));
Thread newWindowThread = new Thread(new ParameterizedThreadStart(AnimationThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start(new Point() { X = left, Y = top });
}
}
}
private void AnimationThreadStartingPoint(object position)
{
lock (_busyWindowSync)
{
if (_busyWindow == null)
{
_busyWindow = new BusyWindow();
_busyWindow.Left = ((Point)position).X;
_busyWindow.Top = ((Point)position).Y;
_busyWindow.Show();
}
}
System.Windows.Threading.Dispatcher.Run();
}
private void HideBusy()
{
lock (_busyWindowSync)
{
if (_busyWindow != null)
{
_busyWindow.Dispatcher.BeginInvoke((Action)_busyWindow.Close);
}
}
}
BusyWindow - это класс окна показываемого в отдельном потоке. Пример вот от сюда, и там это окно использовалось для анимации, которую нельзя отобразить в основном окне. Вы, наоборот, можете в этом окне выполнять длительную операцию, чтобы не подвисало основное окно. Ну или прямо взять этот пример и пока у вас "висит" главное окно, пользователь будет видеть спиннер.
По итогу я решил свою проблему следующим способом:
Создание этого окна сделал в отдельном потоке, отображал окно с помощью ShowDialog()
так как он не дает потоку завершиться раньше времени.
Thread hidePrintThread = new Thread(PrintMethod);
hidePrintThread.SetApartmentState(ApartmentState.STA);
hidePrintThread.IsBackground = true;
hidePrintThread.Start();
void PrintMethod()
{
try
{
CriteriaOperator co=null;
List<MessageGridContent> mess = Service.Messages.ToList();
Dispatcher.Invoke(() => co = messageGrid.FilterCriteria);
HiddenPrintWindow window = new HiddenPrintWindow();
window.MessagesToPrint = mess;
window.CriteriaOperator = co;
Service.Printing = true;
window.ShowDialog();
}
catch(Exception ex)
{
logger.Error("Ошибка печати: {0}", ex.Message);
}
}
Так как мне необходимо было, чтобы данная длительная операция проходила незаметно для пользователя, я решил скрыть окно. Стандартные методы работать отказывались. Я пробовал прописывать Visibyliti
в разметке, пробовал в обработчике события загрузки окна, пробовал в методе, который запускается из обработчика загрузки, но окно упрямо висело видимым, тогда я убрал его на задний план (тоже подходило по ТЗ) с помощью WinAPI:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
Код инициализации и загрузки самого окна:
public HiddenPrintWindow()
{
InitializeComponent();
Loaded += HiddenPrintWindow_Loaded;
}
private void HiddenPrintWindow_Loaded(object sender, RoutedEventArgs e)
{
SetWindowPos(new System.Windows.Interop.WindowInteropHelper(this).Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
Work();
}
И спасибо всем помогавшим!!!
Как меняется крипторынок и к чему готовиться владельцам криптообменников
Нашел в интернете 3 варианта, попробовал все, но прыжок все равно резкий (будто телепорт)Видел совет умножать на Time
Всем привет! Задача такая - необходимо получить количество писем за определенный промежуток времениК примеру, за прошедший месяц
Как авторизироваться через LDAP, или через какую-то библиотеку
Товарищи, доброго времени суток! Чисто теоретический вопрос, есть понимание, как настроить автогенерацию итемов в ListView через ViewModel, путем...