Страницы

Поиск по вопросам

среда, 10 июля 2019 г.

Как передать коллекцию прямоугольников в ItemsControl с Canvas асинхронно?

При решении вопроса, возник новый.
Что делаю: из ViewModel передаю коллекцию прямоугольников, вот так:
public async void Start() { RectItems.Clear();
CrossStitch cs = new CrossStitch() { BlockSize = _blockSize, Source = _sourceImage };
var data = await cs.Create();
foreach (var r in data) RectItems.Add(r);
}
Получаю во View вот так:

Все замечательно работало, до того как метод Start() стал async. Теперь я получаю вместо результата, это:
Необходимо создать DependencySource в том же потоке, в котором создан DependencyObject.
Нашел только одну похожую проблему, но в ней передавалось изображение в Canvas, и проблема решалась вызовом Freeze у изображения. А как быть в моем случае?
Метод Create и прилежащие:
public Task> Create() { return PixelateAsync(_source); }
private Task> PixelateTask(Bitmap source) { return Task.Factory.StartNew(() => Pixelate(source)); }
private Task> PixelateAsync(Bitmap source) { return PixelateTask(source); }
Pixelate(source) синхронный.
Класс RectItem
public class RectItem { public double X { get; set; } public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } public System.Windows.Media.Brush C { get; set; } }
Метод Pixelate весь:
private List Pixelate(Bitmap source) { var result = new Bitmap(source);
List rectangs = new List();
using (var graphics = Graphics.FromImage(result)) { graphics.PageUnit = GraphicsUnit.Pixel;
for (int x = 0; x < source.Width; x += _blockSize) { for (int y = 0; y < source.Height; y += _blockSize) { var sums = new Sums();
for (int xx = 0; xx < _blockSize; ++xx) { for (int yy = 0; yy < _blockSize; ++yy) { if (x + xx >= source.Width || y + yy >= source.Height) { continue; }
var color = source.GetPixel(x + xx, y + yy); sums.A += color.A; sums.R += color.R; sums.G += color.G; sums.B += color.B; sums.T++; } }
var average = Color.FromArgb( sums.A / sums.T, sums.R / sums.T, sums.G / sums.T, sums.B / sums.T);
average = GetNearestColor(average); System.Windows.Media.Color mcolor = System.Windows.Media.Color.FromArgb(average.A, average.R, average.G, average.B); System.Windows.Media.Brush brush = new System.Windows.Media.SolidColorBrush(mcolor); rectangs.Add(new RectItem() { X = x + BlockSize, Y = y + BlockSize, Height = BlockSize, Width = BlockSize, C = brush }); } } }
return rectangs; }


Ответ

Смотрите. Проблема в том, что VM-классы создаются в фоновом потоке, это в обычной ситуации неправильно.
Но в вашем случае RectItem — не DependencyObject, а значит, он не привязан к определённому потоку. Поэтому можно пойти более простым путём: создавать этот объект где угодно. Единственная проблема, которую нужно вынести в UI — создание Brush. Но Brush является Freezable- значит, его можно также создавать где угодно, просто нужно после создания вызвать brush.Freeze();
Ещё один framework-класс — Color — тоже не является проблемой, т. к. он не является ни DependencyObject'ом, ни Freezable
Итого: просто добавьте после
System.Windows.Media.Brush brush = new System.Windows.Media.SolidColorBrush(mcolor);
строку
brush.Freeze();

Комментариев нет:

Отправить комментарий