В настоящее время я занимаюсь разработкой приложения, которое должно иметь возможность делиться или сохранять все, что находится на экране. Я наткнулся на эту статью по Люком ван ден Ouweland о RenderTargetBitmap и подумал, что я мог) сделать это в более общем плане (повторно) использовать и б) сделать это играть хорошо с MVVM.
Ответ был — как вы уже догадались — поведение. Самое интересное, что вы можете перетащить его на любой элемент пользовательского интерфейса, и он создаст скриншот того, что находится внутри этого элемента (и это не обязательно весь экран!), И сохранит его в хранилище. Два свойства зависимости, Target и Prefix, определяют, какое сообщение прослушивает поведение, и каков префикс файла.
Основной код функциональности — который все еще очень похож на оригинальный образец Лука — находится в самом поведении. Чтобы вызвать его из модели представления, я вызываю помощь мессенджера MVVMLight . Итак, я начну с сообщения, которое viewmodel и поведение используют для связи:
using GalaSoft.MvvmLight.Messaging;
namespace WpWinNl.Behaviors
{
public class ScreenshotMessage : MessageBase
{
public ScreenshotMessage(object sender = null, object target = null,
ScreenshotCallback callback = null) : base(sender, target)
{
Callback = callback;
}
public ScreenshotCallback Callback { get; set; }
}
public delegate void ScreenshotCallback(string fileName);
}
Это стандартное сообщение MVVMLight с дополнительным дополнением, которое может нести дополнительную полезную нагрузку обратного вызова. Созданное мной поведение сохраняет файл в KnownFolder, но оно может отправить имя файла, который был создан, обратно вызывающему объекту, вызвав указанный обратный вызов.
Основы поведения — реализованные снова как SafeBehavior — на самом деле довольно просты:
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Imaging;
using GalaSoft.MvvmLight.Messaging;
namespace WpWinNl.Behaviors
{
public class ScreenshotBehavior : SafeBehavior<FrameworkElement>
{
protected override void OnSetup()
{
Messenger.Default.Register<ScreenshotMessage>(this, ProcessMessage );
base.OnSetup();
}
protected override void OnCleanup()
{
Messenger.Default.Unregister(this);
base.OnCleanup();
}
private async void ProcessMessage(ScreenshotMessage m)
{
if (m.Target != null && Target != null)
{
if (m.Target.Equals(Target))
{
await DoRender(m.Callback);
}
}
else
{
await DoRender(m.Callback);
}
}
}
}
Поэтому он слушает сообщение ScreenshotMessage. Когда Target (свойство зависимости в этом поведении) равно цели, указанной в сообщении, рендеринг выполняется. Думайте о Target как об общем ключе — это позволяет модели представления запускать определенное поведение, используя — обычно — строку. Это я показываю в примере решения . Если оба нацелены на модели поведения в и цель данного сообщения являются недействительными, это пожары , а также. Вы можете использовать это, если у вас есть только одно поведение и один вызов. Однако если такое поведение используется в нескольких местах, я настоятельно рекомендую указать цель, иначе вы можете получить очень интересные условия гонки.
Фактический метод рендеринга — это почти весь код Лука с небольшими изменениями:
private async Task DoRender(ScreenshotCallback callback)
{
var renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(AssociatedObject);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
var storageFile =
await KnownFolders.SavedPictures.CreateFileAsync(
string.Concat(Prefix, ".png"),
CreationCollisionOption.GenerateUniqueName);
using (var stream = await storageFile.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder =
await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint) renderTargetBitmap.PixelWidth,
(uint) renderTargetBitmap.PixelHeight, 96d, 96d,
pixelBuffer.ToArray());
await encoder.FlushAsync();
if (callback != null)
{
callback(storageFile.Name);
}
}
}
Он создает уникальный файл на основе свойства зависимостей префикса в известной папке «SavedPictures», отображает экран в виде файла png, сохраняет его и при желании вызывает обратный вызов, передающий имя обратно этому обратному вызову — скорее всего, метод модель представления, которая затем может воздействовать на нее.
Остальная часть поведения — это два свойства зависимостей, которые я опущу для краткости. Вам не нужно их вообще использовать — если вы не установите Prefix, он будет использовать «Скриншот»
Чтобы использовать его: как было сказано ранее, перетащите его поверх элемента пользовательского интерфейса, из которого вы хотите сделать снимки экрана, а затем добавьте, например, следующий код в вашу модель представления:
public ICommand ScreenshotCommand
{
get
{
return new RelayCommand(() =>
{
var m = new ScreenshotMessage(this,
"screenshot", MyCallback);
Messenger.Default.Send((m));
});
}
}
private void MyCallback(string name)
{
Debug.WriteLine(name);
}
В сообщении я установил цель как «снимок экрана», поэтому в XAML теперь должно быть написано
<Behaviors:ScreenshotBehavior Target="screenshot" Prefix="MyScreenshot"/>
иначе поведение не будет отвечать на сообщение. Когда это сделано, он записывает имя созданного файла в консоль — не очень полезно в производственном сценарии, но показывает, что оно работает. В этом обратном вызове вы можете, например, сообщить пользователю, что файл был сохранен, активировать контракт на совместное использование или все, что вы считаете необходимым.
Самое интересное, конечно, что в этом удивительном мире, в котором мы живем сегодня, поведение на самом деле может быть кросс-платформенным и определяться в PCL и работать как на Windows Phone 8.1, так и на «большой Windows» 8.1 :).
В демонстрационном решении , которое содержит приложение для Windows Phone, приложение для Windows и общий проект, вы увидите, что я снова перетащил файл Main.Xaml в общий проект просто для удовольствия. Не забудьте установить доступ к библиотеке изображений в обоих манифестах приложения. Я всегда об этом забываю.

Интересная деталь — я установил серый фон для корневой сетки приложения. Если я не установлю цвет, фон изображения в Windows будет не черным, а обрезанным до 1297×1080 вместо 1920×1080, как и в моем родном разрешении. Я еще не смог определить, почему это так.
Я построил это поведение поверх моего пакета WpWinNl 2.0.3, но вы можете легко адаптировать его, чтобы заставить его работать как обычное поведение, просто используя процедуру, описанную здесь .
Ох, а картина? Это просто старая фотография грузовика Mercedes моей жены в 2004 году, когда она и ее коллега вошли в Книгу рекордов Гиннеса, пытаясь создать самый длинный грузовой конвой из когда-либо существовавших . Это короткая остановка на обочине дороги Дайк до того, как фактическая колонна длиной 9,5 км была собрана. Интересная деталь: все 416 водителей были женщинами . Конвой без проблем проехал 22 км, и, как метко сказала моя жена, она впервые попала в пробку.

