Визуализация элемента управления контентом в другой вкладке TabControl - PullRequest
2 голосов
/ 09 января 2010

У меня есть программа, в которой есть несколько вкладок, динамически добавляемых к объекту TabControl программным способом. Что я хочу сделать, это отобразить значение Content каждой из этих вкладок в PNG. Я использую скрипт, который я нашел в другом месте на StackOverflow или, возможно, Google (потерял источник). Мой код выглядит так:

if (tabPanel.Items.Count > 0)
{
    SaveFileDialog fileDialog = new SaveFileDialog();
    fileDialog.Filter = "PNG|*.png";
    fileDialog.Title = "Save Tabs";
    fileDialog.ShowDialog();

    if (fileDialog.FileName.Trim().Length > 0)
    {
        try
        {
            string filePrefix = fileDialog.FileName.Replace(".png", "");
            int tabNo = 1;
            foreach (TabItem tabItem in tabPanel.Items)
            {
                string filename = filePrefix + "_" + tabNo + ".png";

                TabContentControl content = tabItem.Content as TabContentControl;
                Rect rect = new Rect(content.RenderSize);
                RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
                rtb.Render(content);

                BitmapEncoder pngEncoder = new PngBitmapEncoder();
                pngEncoder.Frames.Add(BitmapFrame.Create(rtb));

                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                pngEncoder.Save(ms);
                System.IO.File.WriteAllBytes(filename, ms.ToArray());
                ms.Close();

                tabNo++;
            }
        }
        catch (Exception ex)
        {
            // log exception
        }
    }
}

Этот код работает как нужно , если Я прошел и просмотрел все вкладки, которые должны быть отображены перед вызовом этого кода. Это продолжается и создает filePrefix_1.png, filePrefix_2.png и т. Д. С правильным содержимым, отображаемым из TabContentControl. Однако, если я вызову обработчик, который использует этот код до просмотра всех вкладок, мой код выдаст исключение на new RenderTargetBitmap(...), поскольку content.RenderSize равно {0.0, 0.0}. Когда я пытаюсь принудительно настроить размер рендеринга не просмотренной вкладки на один из просмотренных один раз, мой выводимый PNG имеет правильные размеры, но полностью пустой.

Так что я думаю, мне нужен какой-то способ форсировать рендеринг TabContentControl. Похоже, что событие Render запускается, как и должно быть, когда нужно отрендерить UIElement. Есть ли какая-нибудь хитрость, которую я могу выполнить, чтобы обойти это?

Я также пытался «обмануть» WPF при рисовании содержимого вкладок, добавив следующий код при создании вкладок в обработчик событий Page_Loaded:

void Page_Loaded(object sender, RoutedEventArgs e)
{
    // irrelevant code
    foreach (// iterate over content that is added to each tab)
    {
        TabItem tabItem = new TabItem();
        // load content
        tabPanel.Items.Add(tabItem);
        tabItem.IsSelected = true;
    }
    // tabPanel.SelectedIndex = 0;
}

Когда последняя строка в обработчике Page_Loaded закомментирована, последняя вкладка находится в фокусе и имеет свойство RenderSize, определенное для ее содержимого. Когда последняя строка не закомментирована, первая вкладка находится в фокусе, с тем же поведением. Другие вкладки не содержат никакой информации об отрисовке.

Ответы [ 2 ]

3 голосов
/ 12 февраля 2010

Наконец-то понял, благодаря этому сообщению в блоге . Решение заключалось в создании метода расширения для UIElement с помощью метода Refresh, который вызывал пустой делегат с приоритетом рендеринга. Видимо, планирование чего-либо с приоритетом рендеринга привело к выполнению всех других более важных элементов, что привело к изменению вкладки.

Код повторяется здесь в случае удаления блога:

public static class ExtensionMethods
{
   private static Action EmptyDelegate = delegate() { };

   public static void Refresh(this UIElement uiElement)
   {
      uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
   }
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
    // irrelevant code
    foreach (// iterate over content that is added to each tab)
    {
        TabItem tabItem = new TabItem();
        // load content
        tabPanel.Items.Add(tabItem);
        tabItem.IsSelected = true;
        tabItem.Refresh();
    }
    // tabPanel.SelectedIndex = 0;
}

Чтобы использовать его, просто включите пространство имен расширения в файл кода, который вам нужен, чтобы использовать эту функцию, и он появится в списке методов.

1 голос
/ 11 января 2010

Это не совсем идеальное решение, но когда вы создаете свои вкладки и добавляете их в элемент управления вкладками, вы можете просто сохранить открытую вкладку, затем переключиться на только что созданную вкладку и затем вернуться обратно. , Для пользователя это будет лишь незначительное мерцание, но на самом деле вы просто обманули winforms (или wpf соответственно) при рисовании объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...