【问题标题】:Render a content control in another tab of TabControl在 TabControl 的另一个选项卡中呈现内容控件
【发布时间】:2010-01-09 05:06:32
【问题描述】:

我有一个程序,其中有多个选项卡以编程方式动态添加到TabControl 对象。我想要做的是将每个选项卡的 Content 值呈现为 PNG。我正在使用我在 StackOverflow 或谷歌的其他地方找到的脚本(丢失了源代码)。我的代码如下所示:

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

此代码按预期工作if 我已经查看并查看了在调用此代码之前必须呈现的所有选项卡。它继续并使用从 TabContentControl 呈现的正确内容创建 filePrefix_1.png、filePrefix_2.png 等。但是,如果我在查看所有选项卡之前调用使用此代码的处理程序,我的代码会在new RenderTargetBitmap(...) 处引发异常,因为content.RenderSize{0.0, 0.0}。当我尝试将未查看选项卡的渲染大小强制为查看过的选项卡之一时,我输出的 PNG 尺寸正确但完全为空。

所以我想我需要一些方法来强制呈现 TabContentControl。似乎只有在需要渲染 UIElement 时才运行 Render 事件。他们有什么诡计可以让我解决这个问题吗?

我还尝试通过在创建选项卡时在 Page_Loaded 事件处理程序中添加以下代码来“欺骗”WPF 来绘制选项卡内容:

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 属性。当最后一行没有被注释掉时,第一个标签是焦点,具有相同的行为。其他选项卡没有任何渲染信息。

【问题讨论】:

    标签: c# wpf tabcontrol render contentcontrol


    【解决方案1】:

    终于弄明白了,感谢this blog post。该解决方案涉及使用 Refresh 方法为 UIElement 创建扩展方法,该方法调用具有呈现优先级的空委托。显然,以渲染优先级安排某些内容会导致执行所有其他更重要的项目,从而更改选项卡。

    此处复制代码以防博客被删除:

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

    要使用它,只需在代码文件中包含使用此功能所需的扩展命名空间,它就会出现在方法列表中。

    【讨论】:

    • 我找到的最佳解决方案,谢谢。如果有人知道解决方案而不修改所选项目,那将是完美的。
    【解决方案2】:

    这并不是一个真正理想的解决方案,但是当您创建选项卡并将它们添加到选项卡控件时,您可以只存储当前打开的选项卡,然后切换到您刚刚创建的选项卡,然后再切换回来.对用户来说,这只是轻微的闪烁,但实际上你只是欺骗了 winforms(或相应的 wpf)来绘制对象。

    【讨论】:

    • 我尝试做类似的事情,但我一生都无法弄清楚如何以编程方式更改可见选项卡。尝试将TabControl.SelectedItem 设置为TabItem 以及增加TabControl.SelectedIndex 属性。
    • 实际上.....我刚刚意识到我正在做的是在单击保存按钮后尝试从处理程序中更改选项卡...所以我想没有机会要重新绘制标签?我想如果我在添加标签时这样做不会有问题。
    • 刚试了下,不行。 WPF 似乎进行了优化,并且只绘制最后打开的选项卡。
    • 在这种情况下,您始终可以一个一个地浏览您的标签,并随时保存
    • 我很确定我已经尝试过了,但我会再试一次。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    • 2011-01-06
    相关资源
    最近更新 更多