WPF: Обновление свойства Points в Polyline через привязку приводит к задержке
У меня есть пользовательский элемент управления графиком, который использует Polyline
объект для визуализации сигналов.
<Polyline Name="Line" Points="{Binding LinePoints}"
Stroke="{Binding LineColor}"
StrokeThickness="{Binding LineThickness}">
В моей ViewModel я буду генерировать различные наборы "LinePoints" каждый короткий промежуток времени (50-200 мс) через DispatcherTimer
, Привязка работает отлично, я получаю анимированную форму волны в представлении, за исключением того, что она вызывает значительное отставание в пользовательском интерфейсе. Например, когда я щелкаю правой кнопкой мыши по чему-то еще в окне, появляется контекстное меню с очень очень запаздывающей анимацией.
Конечно, я мог бы изменить таймер на каждые 500 мсек, и задержка была бы значительно уменьшена. Но это сделало бы мой график неаккуратным. Есть ли какие-либо методы, которые я могу сделать, чтобы перенести некоторые из них в другой поток?
(Примечание: поколение LinePoints
не является основной причиной отставания. Каждое поколение использует около 1 мс времени выполнения. Это значение получается из System.Diagnostics.Stopwatch
)
1 ответ
Таким образом, поскольку операция вычисления всех требуемых баллов является тяжелой для пользовательского интерфейса, вы должны использовать дополнительные Thread
чтобы покрыть расчет. UI Thread
должен отвечать только за представление, то есть он должен только назначать данное значение, а не вычислять его.
Это идеальный вариант использования для новых await async keywords
введено в 4.5
рамочная версия.
Для достижения описанного поведения я бы предложил использовать Task class
в качестве таких:
private async void InvokeHandler()
{
while(true)
{
// Passes variable to calculation method and waits for result without blocking.
ViewModel.Points = await Task.Run(() => CalculatePoints(new object()));
await Task.Delay(100);
}
}
private Task<PointCollection> CalculatePoints(object requiredArguments)
{
var points = new PointCollection();
// Do calculations here with requiredArgument passed from a caller.
return Task.FromResult(points);
}
Как это работает? В InvokeHandler
метод вы начинаете Task
который, по сути, может быть другим Thread
(собрано из ThreadPool
если текущий Thread
не хватает ресурсов для запуска при условии Delegate
), который не заблокирует ваш текущий запущенный Thread
и после завершения выполнения вернет вам результат (PointCollection
).