Сохранение 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 называется.

Полный результирующий код (за вычетом содержимого TextBlocks и запись на изображение) ниже.

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);
});

Работает на меня

Другие вопросы по тегам