开发者

Binding DocumentViewer in MVVM

开发者 https://www.devze.com 2023-04-11 20:43 出处:网络
I\'m trying to bind a DocumentViewer to a document via the ViewModel and am not succeeding at all. Here is my view model code...

I'm trying to bind a DocumentViewer to a document via the ViewModel and am not succeeding at all.

Here is my view model code...

    private DocumentViewer documentViewer1 = new DocumentViewer();

    public DocumentViewerVM()
    {
        string fileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "Here an xps document.xps");
        XpsDocument document = new XpsDocument(fileName, FileAccess.Read);            
        documentViewer1.Document = document.GetFixedDocumentSequence();            
        document.Close();

    }

    public DocumentViewer DocumentViewer1
    {
        get
        { return documentViewer1; }
        set
        {
            documentViewer1 = value;
            OnPropertyChanged("DocumentViewer1");
        }

    }

here is the xaml in the view...

<UserControl x:Class="DemoApp.View.DocumentViewerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
    <Grid>
        <DocumentViewer Name="DocumentViewer1" Document="{Binding Path=DocumentViewer1, UpdateSourceTrigger=PropertyChanged}" ></DocumentViewer>

 开发者_如何转开发   </Grid>
</UserControl>

the code behind for the view contains no code other than 'InitializeComponent()'

What I do find strange is that if I place the document generation code from the view model constructor into the view constructor the document is displayed correctly, this leads me to think it is a binding issue, but where or how I know not.


You are binding the Document property of the DocumentViewer to a property called DocumentViewer1 which is itself a DocumentViewer. The Document property expects an instance of a type that implements IDocumentPaginatorSource, such as a FixedDocument.


If you want to keep your view models pristine and avoid including PresentationCore.dll in your view model library, then use a WPF IValueConverter such as the following.

namespace Oceanside.Desktop.Wpf.Dialogs.Converters
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Xps.Packaging;

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <inheritdoc />
    /// <summary>
    ///     Our view models contain string paths to all XPS documents that we want to show.  However,
    ///     the DocumentViewer.Document property must be of type IDocumentPaginatorSource which we do
    ///     not want to include in our view model because it will tie our view models to the
    ///     PresentationCore.dll.  To assure all view logic and libraries are kept separate from our
    ///     view model, this converter to take a string path and convert it to a
    ///     FixedDocumentSequence which implements the IDocumentPaginatorSource interface.
    /// </summary>
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    [ValueConversion(typeof(string), typeof(IDocumentPaginatorSource))]
    public sealed class DocumentPaginatorSourceConverter : IValueConverter
    {
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <inheritdoc />
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        public object Convert(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            if (!(value is string xpsFilePath)) return null;

            var document = new XpsDocument(xpsFilePath, FileAccess.Read);
            var source = document.GetFixedDocumentSequence();
            document.Close();
            return source;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////
        /// <inheritdoc />
        /// <summary>This function is not supported and will throw an exception if used.</summary>
        ////////////////////////////////////////////////////////////////////////////////////////////////////
        public object ConvertBack(object value, Type targetType,
            object parameter, CultureInfo culture)
        {
            //We have no use to convert from IDocumentPaginatorSource to a string path.  
            throw new NotSupportedException("Unable to convert an IDocumentPaginatorSource to a string path.");
        }
    }
}

The XAML below shows how to use the above converter. This example is a data template that has a view model of Type MessageBoxShowXpsDoc which has a simple string property called DocumentPath. This is passed to the converter to obtain the IDocumentPaginatorSource.

<!-- For showing xps/flow docs such as customer receipts -->
<DataTemplate 
       DataType="{x:Type dialogVms:MessageBoxShowXpsDoc}"
       xmlns:converters="clr-namespace:Oceanside.Desktop.Wpf.Dialogs.Converters">
    <DataTemplate.Resources>
        <converters:DocumentPaginatorSourceConverter x:Key="DocPagConverter" />
    </DataTemplate.Resources>
    <DocumentViewer Document="{Binding DocumentPath, 
                               Converter={StaticResource DocPagConverter}}" />
</DataTemplate>

Although including the full view model is outside the scope of the OP, this is an example of how I set that string path which is passed from the view model to the converter.

var viewModel = MessageBoxShowXpsDoc {DocumentPath = @"TestData\sample.xps"};


As explained already by devdigital (above), a public property of type IDocumentPaginatorSource is needed.

Something like this perhaps:

private IDocumentPaginatorSource _fixedDocumentSequence;

public IDocumentPaginatorSource FixedDocumentSequence
{
    get { return _fixedDocumentSequence; }
    set
    {
        if (_fixedDocumentSequence == value) return;

        _fixedDocumentSequence = value;
        OnPropertyChanged("FixedDocumentSequence");
    }
}

And in your xaml just bind this to the DocumentViewer Document property:

<Grid>
    <DocumentViewer                                          
        Name="DocViewer"
        Document="{Binding FixedDocumentSequence}"/>       
</Grid>


For people who might still have no clue how to get it done. Here is an example:

The View:

<Grid>
        <DocumentViewer HorizontalAlignment="Center" 
                        Margin="0,20,0,0"
                        Document="{Binding Manual}"
                        VerticalAlignment="Top" 
                        Height="508" Width="766" />
</Grid>

The ViewModel:

public OperationManualViewModel()
{
    var _xpsPackage = new XpsDocument(@"C:\Users\me\Desktop\EngManual.xps", FileAccess.Read);
    _manual = _xpsPackage.GetFixedDocumentSequence();

}

private IDocumentPaginatorSource _manual;

public IDocumentPaginatorSource Manual
{
    get { return _manual; }
    set { _manual = value; NotifyOfPropertyChange(() => Manual); }
}
0

精彩评论

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