Search code examples
c#wpfxamlflowdocumentxamlreader

FlowDocument and XamlReader x:Class


In my MainWindow I have a FlowDocumentScrollViewer binding its property Document to a FlowDocument in my MainViewModel.

This document is loaded from an external xaml file store on a remote computer. Currently I'm able to load this document properly via XamlReader.Load(xamlfile) and display it in the FlowDocumentScrollViewer. So far so good.

The problem occurs when I try to add hyperlink in this document. Because to handle the RequestNavigate event I need a x:Class. For the time being this Class need to be my MainWindow because the event is handle in the code-behind. Obviously when I add x:Class="Ugrader.MainWindow" in my external document I get a lovely 'System.Windows.Markup.XamlParseException' at the moment of parsing.

So is there a way to solve this ?

Here is piece of my code

MainWindow.xaml

<Window x:Class="Ugrader.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Geco3-Upgrading version" 
        WindowStyle="none" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
        Height="400" Width="700"
        DataContext="{Binding Main,Source={StaticResource Locator}}">


        <FlowDocumentScrollViewer Grid.Column="1" Background="{x:Null}" VerticalScrollBarVisibility="Hidden"
                                      Document="{Binding WhatsNewDoc}"/>

</Window>

MainViewModel.cs

namespace Ugrader.ViewModel
{
    public class MainViewModel : ViewModelBase
    {        
        #region Constructor
        public MainViewModel()
        {               
            try
            {
                FileStream xamlFile = new FileStream(updateLocation + "whatsnew.xaml", FileMode.Open, FileAccess.Read);
                FlowDocument current = System.Windows.Markup.XamlReader.Load(xamlFile) as FlowDocument;
                WhatsNewDoc = current;
            }
            catch (Exception)
            {  

            }
        }
        #endregion 

        #region Properties
        private FlowDocument _watsNewDoc = new FlowDocument();
        public FlowDocument WhatsNewDoc
        {
            get
            {
                return _watsNewDoc;
            }
            set
            {
                if(_watsNewDoc != value)
                {
                    _watsNewDoc = value;
                    RaisePropertyChanged("WhatsNewDoc");
                }
            }
        }
        #endregion
    }
}

The external FlowDocument

<FlowDocument x:Class="Ugrader.MainWindow" 
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              ColumnWidth="400" FontSize="12" FontFamily="Century Gothic" Foreground="LightGray">

  <Paragraph>
    <Italic>
      For additionnal information, please watch this
      <Hyperlink TextDecorations="{x:Null}" RequestNavigate="Hyperlink_Clicked" NavigateUri="path_to_the_file" >video</Hyperlink>
    </Italic>
  </Paragraph>

</FlowDocument>

By the way, is there a way to handle this parse exception (in case of bad external file), because even in this try/catch block, this stop my program.

Thanks you in advance,

Bastien.


Solution

  • I find a way to deal with my problem, it's not really beautiful, do not respect the spirit of mvvm, but well, this is working.

    So, as it's not possible to add x:Class at runtime (I guess), I came to the idea of handling the RequestNavigate event of each Hyperlinkat runtime. So the solution is pretty simple (and dirty).

    In code-behind (yeah I know, it's ugly), on the MainWindow loaded event, I find all hyperlinks in my document, and handle each RequestNavigate events. As simple (and dirty) as this.

    Here is some code :

     private void Window_Loaded(object sender, RoutedEventArgs e)
     {
        var hyperlinks = GetVisuals(this).OfType<Hyperlink>();
        foreach (var link in hyperlinks)
        link.RequestNavigate += link_RequestNavigate;
     }
    
     public static IEnumerable<DependencyObject> GetVisuals(DependencyObject root)
     {
         foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
         {
             yield return child;
             foreach (var descendants in GetVisuals(child))
                 yield return descendants;
         }
     }
    

    If someone has a better solution, I take it.