Сохранение UserControl в виде JPG - результат "сжат"
У меня странная проблема с сохранением UserControl в виде JPG. По сути, я хочу создать Live Tile, который я могу обновлять с помощью фонового агента (а также из самого приложения).
Я следовал инструкциям в этом блоге, которые отлично подходят для создания плитки; У меня есть пользовательский элемент управления UserControl с несколькими текстовыми блоками на нем, который сохраняется в виде JPG в IsolatedStorage, в точности, как говорится в посте.
Создание плитки не является проблемой - UserControl отображается так, как должно быть. Однако, когда я пытаюсь обновить плитку (используя точно такой же метод - сохранение нового элемента управления в виде JPG, а затем использование этого JPG в качестве BackgroundImage), все выходит из строя.
Полученное изображение, которое помещается на плитку (и сохраняется в IsolatedStorage), выглядит следующим образом: сжатая плитка (извлекается из IsolatedStorage с помощью инструмента изолированного хранилища)
Фон черный, и весь текст проходит по краям изображения (и накладывается друг на друга) - ожидаемый результат - это фон с акцентным цветом телефона, а текст отображается горизонтально вверху.
Код, используемый для генерации и сохранения изображения, одинаков в обоих случаях - я абстрагировал его в статический метод, который возвращает StandardTileData
, Единственное отличие состоит в том, откуда он вызывается: в рабочем случае, когда создается плитка, он вызывается со страницы в основном приложении; в нерабочем случае (когда плитка обновляется) она вызывается со страницы, доступ к которой возможен только путем глубокой ссылки с самой плитки.
Какие-нибудь мысли? Я предполагаю, что что-то не так с рендерингом элемента управления в JPG, поскольку реальное изображение получается таким образом.
Фрагмент кода, который генерирует изображение, находится здесь:
StandardTileData tileData = new StandardTileData();
// Create the Control that we'll render into an image.
TileImage image = new TileImage(textA, textB);
image.Measure(new Size(173, 173));
image.Arrange(new Rect(0, 0, 173, 173));
// Render and save it as a JPG.
WriteableBitmap bitmap = new WriteableBitmap(173, 173);
bitmap.Render(image, null);
bitmap.Invalidate();
IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();
String imageFileName = "/Shared/ShellContent/tile" + locName + ".jpg";
using (IsolatedStorageFileStream stream = storage.CreateFile(imageFileName))
{
bitmap.SaveJpeg(stream, 173, 173, 0, 100);
}
tileData.BackgroundImage = new Uri("isostore:" + imageFileName, UriKind.Absolute);
return tileData;
XAML для элемента управления, который я пытаюсь преобразовать, находится здесь:
<UserControl x:Class="Fourcast.TileImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="173" d:DesignWidth="173" FontStretch="Normal" Height="173" Width="173">
<Border Background="{StaticResource PhoneAccentBrush}">
<StackPanel>
<TextBlock HorizontalAlignment="Stretch"
TextWrapping="Wrap" VerticalAlignment="Stretch" Style="{StaticResource PhoneTextLargeStyle}" x:Name="Temperature"></TextBlock>
<TextBlock x:Name="Condition" HorizontalAlignment="Stretch"
TextWrapping="Wrap" VerticalAlignment="Stretch" Style="{StaticResource PhoneTextNormalStyle}">
</TextBlock>
</StackPanel>
</Border>
</UserControl>
Обновление: после некоторого расследования с помощью отладчика появляются вызовы Measure
а также Arrange
кажется, ничего не делает, когда метод вызывается из класса, который обновляет плитку. Однако, когда создается плитка, эти вызовы функционируют должным образом (ActualWidth и ActualHeight элемента управления изменяются на 173).
3 ответа
Не уверен, что именно здесь происходит, но вот что я в итоге сделал, чтобы обойти эту проблему.
Я переключился с попытки визуализации элемента управления на определение StackPanel
в коде, добавление элементов к нему, а затем рендеринг этого к изображению. Как ни странно, однако, Arrange
а также Measure
методы ничего не делают, если к ним не применен другой элемент, и UpdateLayout
называется.
Полный результирующий код (за вычетом содержимого TextBlock
s и запись на изображение) ниже.
StandardTileData tileData = new
// None of this actually does anything, but for some reason the StackPanel
// won't render properly without it.
Border image = new Border();
image.Measure(new Size(173, 173));
image.Arrange(new Rect(0, 0, 173, 173));
image.UpdateLayout();
// End of function-less code.
StackPanel stpContent = new StackPanel();
TextBlock txbTemperature = new TextBlock();
txbTemperature.Text = temperature;
txbTemperature.Style = (Style)Application.Current.Resources["PhoneTextLargeStyle"];
stpContent.Children.Add(txbTemperature);
TextBlock txbCondition = new TextBlock();
txbCondition.Text = condition;
txbCondition.Style = (Style)Application.Current.Resources["PhoneTextNormalStyle"];
stpContent.Children.Add(txbCondition);
stpContent.Measure(new Size(173, 173));
stpContent.Arrange(new Rect(0, 0, 173, 173));
Чтобы получить фоновое изображение с использованием акцентного цвета телефона, нужно сделать его прозрачным. Для этого потребуется сохранить PNG, а не JPG.
Выходное изображение выглядит так, как будто все упаковано больше, чем нужно. Вам, вероятно, нужно отрегулировать ширину элементов, которые вы включаете в свой элемент управления. Без XAML сложно быть более конкретным.
Я также получаю это странное поведение - сплющенный тайл (используя набор инструментов Ree7 Tile),
Попробуйте позвонить обновить тайл в Диспетчер:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
CustomTile tile = GetTile(card);
StandardTileData tileData = tile.GetShellTileData();
shellTile.Update(tileData);
});
Работает на меня