开发者

WPF: FixedDocument in Visual Studio 2008 Designer

开发者 https://www.devze.com 2022-12-18 05:19 出处:网络
It\'s a well-known bug that Visual Studio shows an error when you try to construct a FixedDocument in XAML. For example, the following snippet

It's a well-known bug that Visual Studio shows an error when you try to construct a FixedDocument in XAML. For example, the following snippet

<DocumentViewer>
    <FixedDocument>
        <PageContent>
            <FixedPage Width="21.0cm" Height="29.7cm">
                <TextBlock>Hello World!</TextBlock>
            </FixedPage>
        </PageContent>
 开发者_JAVA百科   </FixedDocument>
</DocumentViewer>

compiles and runs perfectly fine, but Visual Studio shows an error in the error list (Property 'Pages' does not support values of type 'PageContent'.) This is quite annoying.

I'm looking for a solution that allows me to construct my documents in a XAML file in Visual Studio without getting that error message. I've found a workaround, which I'd like to share below as an answer, but I'm curious if there's a better (more elegant) solution around.


As a workaround, I put the DocumentViewer as well as the page into a grid:

<Grid>
    <FixedPage Width="21.0cm" Height="29.7cm" x:Name="uiPage1">
        <TextBlock>Hello World!</TextBlock>
    </FixedPage>
    <DocumentViewer>
        <FixedDocument x:Name="uiReport">
        </FixedDocument>
    </DocumentViewer>
</Grid>

Then I attach the page to the DocumentViewer in the Loaded event of the window:

VB example:

DirectCast(Me.uiPage1.Parent, Grid).Children.Remove(Me.uiPage1)
Dim content As New PageContent()
DirectCast(content, IAddChild).AddChild(Me.uiPage1)
Me.uiReport.Pages.Add(content)

C# example:

((Grid)uiPage1.Parent).Children.Remove(uiPage1);
var content = new PageContent();
((IAddChild)content).AddChild(uiPage1);
uiReport.Pages.Add(content);


I know this had already been answered, but I think this answer is nicer because it doesn't require you to add a DocumentView.

If there's a way to reference the resources by the key name and put them in the FixedDocument with XAML, please let me know. I can't seem to find a way to do that, but maybe it's possible.

Use:

var doc = System.Windows.Application.LoadComponent(new Uri("/FixedDocumentExample.xaml", UriKind.Relative)) as FixedDocument;
doc.AddPages();

Extension Method:

using System.Collections;
using System.Windows.Documents;

public static class FixedDocumentExtended {
    public static void AddPages(this FixedDocument fixedDocument) {
        var enumerator = fixedDocument.Resources.GetEnumerator();
        while (enumerator.MoveNext()) {
            var pageContent = ((DictionaryEntry)enumerator.Current).Value as PageContent;
            if (pageContent != null) {
                fixedDocument.Pages.Add(pageContent);
            }
        }
    }
}

XAML:

<FixedDocument
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <FixedDocument.Resources>
        <PageContent x:Key="page1">
            <FixedPage Width="793.76" Height="1122.56">
                <TextBlock Margin="50" Text="Page 1"/>
            </FixedPage>
        </PageContent>
        <PageContent x:Key="page2">
            <FixedPage Width="793.76" Height="1122.56">
                <TextBlock Margin="50" Text="Page 2"/>
            </FixedPage>
        </PageContent>
    </FixedDocument.Resources>
</FixedDocument>


A cleaner workaround:

[ContentProperty("Pages")]
public class XamlFixedDocument : FixedDocument
{
    private ObservableCollection<PageContent> _pages;

    public XamlFixedDocument()
    {
        this.Pages = new ObservableCollection<PageContent>();
    }

    public new ObservableCollection<PageContent> Pages
    {
        get => _pages;
        set
        {
            _pages = value;

            foreach (var page in _pages)
            {
                base.Pages.Add(page);
            }

            _pages.CollectionChanged += (o, e) =>
            {
                if (e.NewItems != null)
                {
                    foreach (PageContent page in e.NewItems)
                    {
                        base.Pages.Add(page);
                    }
                }
            };
        }
    }
}

This subclass of FixedDocument fakes a Pages property and redirect all added pages to the real Pages property in its base class.

Usage:

<doc:XamlFixedDocument xmlns:doc="clr-namespace:Hillinworks.WPF.Document">
    <PageContent>
        <FixedPage Background="White">
            <TextBlock Text="hello, world" />
        </FixedPage>
    </PageContent>
</doc:XamlFixedDocument>

Change Hillinworks.WPF.Document to the namespace where the XamlFixedDocument class is located.

This also enables design-time preview of your document.


So I was messing with fixed documents and I came across the same problem. and I think this is maybe even a cleaner workaround than what others have suggested.

So basically you should create a custom class derived from FixedDocument as hillin suggested, and add a property to get FixedDocument from this object's PageContents. but since these pages are now visual children of another object you should make a copy of them using XmlReader and XmlWriter classes.

[ContentProperty("Pages")]
public class CustomFixedDocument : FixedDocument
{
    private ObservableCollection<PageContent> _pages;

    public CustomFixedDocument()
    {
        this.Pages = new ObservableCollection<PageContent>();
    }

    public FixedDocument FixedDocument
    {
        get
        {
            var document = new FixedDocument();
            foreach (var p in Pages)
            {
                var copy = XamlReader.Parse(XamlWriter.Save(p)) as PageContent;
                document.Pages.Add(copy);
            }
            return document;
        }
    }

    public new ObservableCollection<PageContent> Pages
    {
        get => _pages;
        set
        {
            _pages = value;

            foreach (var page in _pages)
            {
                base.Pages.Add(page);
            }

            _pages.CollectionChanged += (o, e) =>
            {
                if (e.NewItems != null)
                {
                    foreach (PageContent page in e.NewItems)
                    {
                        base.Pages.Add(page);
                    }
                }
            };
        }
    }
}

now in the xaml you could easily create a CustomFixedDocument StaticResource and bind your DocumentViewer to the 'FixedDocument' property of it.

<Window x:Class="MyProject.DocumentWindow"
    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"
    xmlns:local="clr-namespace:MyProject"
    mc:Ignorable="d"
    Title="DocumentWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Window.Resources>
    <local:CustomFixedDocument x:Key="Report">
        <PageContent>
            <FixedPage Width="793.76" Height="1122.56">
                <TextBlock Margin="50" Text="Page 1"/>
            </FixedPage>
        </PageContent>
        <PageContent>
            <FixedPage Width="793.76" Height="1122.56">
                <TextBlock Margin="50" Text="Page 2"/>
            </FixedPage>
        </PageContent>
    </local:CustomFixedDocument>
</Window.Resources>
<Grid>
    <DocumentViewer x:Name="viewer" Document="{Binding Source={StaticResource Report}, Path=FixedDocument}"/>
</Grid>

Now both issues have been addressed. there is live design time preview with no compile errors and the output could be printed.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号