Всем привет.
Создал контрол, производный от Canvas
. Рисую в нем так:
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
if (Map != null)
{
if (Map.Count > 0)
{
FontFamily family = new FontFamily(new Uri("pack://application:,,,/Resources/Fonts/CSTITCHHD.ttf"), "CrossStitch_TG");
Brush brush = Brushes.Black;
foreach (var m in Map)
{
FormattedText text = new FormattedText(m.Key.ToString(),
new System.Globalization.CultureInfo("en-US"),
FlowDirection.LeftToRight,
new Typeface(family, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), 18, brush);
var X = m.Value.X + (BlockSize / 2 - text.Width / 2) + Offset;
var Y = m.Value.Y + (BlockSize / 2 - text.Height / 2) + Offset;
Point p = new Point(X, Y);
dc.DrawText(text, p);
}
}
}
}
Map
- это большая коллекция (10000-50000 элементов), заполняется она во ViewModel
в асинхронном методе. При >20000 вешает UI сильно и долго.
Как быть? Есть ли относительно простой вариант рисовать асинхронно? Уже много чего пробовал, ничего не получается.
Скорость уже не важна, главное чтобы UI не вешало, пусть хоть анимация загрузки в Canvas
повисит, лишь бы остальная часть не висела.
Процесс рендеринга в WPF и так довольно сложный, в частности, основной этап рисования и без того идет асинхронно - так что вряд ли весь этот алгоритм можно перенести в другой поток напрямую.
Можно попробовать вместо Canvas использовать DrawingVisual.
Если простой переход не исправит ситуацию - можно растеризовать векторный рисунок в RenderTargetBitmap и вывести его в Image
Насколько я понимаю, сам процесс растеризации можно провести в отдельном потоке, а в UI-поток передать уже результат (RenderTargetBitmap).
Странно все это. Пока не задашь вопрос, мысли в голову не лезут.
Итак, решение не было найдено. Наткнулся я совсем случайно на одну реализацию, и на ее основе решил проблему.
Что я делал.
Конструктор, контрола, который рисует:
public MarkMapper()
{
_timer = new DispatcherTimer();
_timer.Interval = RealizationInterval;
_timer.Tick += _timer_Tick;
}
При изменению колекции "символов-точка" возвращаю стандартные значения переменных, очищаю Children
у котрола. Запускаю таймер. По тику, получаю из основной коллекции Map
куски заданной длины. Затем, как видно, рисую в Realize()
. Также попутно проверяю, чтобы длина части не выходила за рамки, и заканчивалось рисование.
private void _timer_Tick(object sender, EventArgs e)
{
if ((Map.Count - _start) < RealizationCountAtTime)
{
if ((Map.Count - _start) <= 0) { Stop(); return; }
else
_count = Map.Count - _start;
}
_bunch = Map.ToList().GetRange(_start, _count);
_start += _count;
Realize();
}
Рисую, и добавляю в Children
как VisualHost
:
private void Realize()
{
//Тут рисуем с помощью DrawingVisual
VisualHost host = new VisualHost()
{
Visual = drawing,
IsHitTestVisible = false
};
SetTop(host, 0);
SetLeft(host, 0);
Children.Add(host);
}
Результат (100 символов раз в 10мс):
Ну и при увеличении:
Конечно, возможно это костыли и/или индийский код, но другого варианта у меня нет. И получил примерно то, что хотел.
Если не получается с асинхронностью - значит, пора оптимизировать алгоритм.
Для начала, надо пройтись по иерархии родительских элементов до корня, преобразовать их границы в систему координат вашего элемента, пересечь - получив тем самым видимые границы, после чего ограничиться выводом только тех надписей, которые в эти самые границы попадают.
После этого надо исходя из RenderTransform определить текущий уровень масштабирования, после чего отбросить те надписи, которые оказываются слишком мелкими.
Или же вместо отбрасывания следует произвести кластеризацию.
Скорее всего, вам потребуется особая структура данных для быстрого выполнения этих операций.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Помогите сравнить 2 массиваОба одномерные, надо узнать , в каком массиве меньше единиц
Как правильно реализовать вызов контекстного меню на Item в ListViewИ как реализовать событие клика на кнопке удалить в этом меню для удаления...
Нужна была параллельная обработка очереди, я её реализовал, но есть проблема в том, что в определенном месте обработчика мне нужно реализовать...
Привет всемПомогите решить проблему с picturebox