2

I have a Windows 10 UWP application – using Prism MVVM – in which I programmatically – in C# – create some SVG XML in an XmlDocument which I need to display in an existing Image component.

In my XAML I have:

<Image
 Source="{x:Bind ViewModel.SvgSource, Mode=OneWay}"
 Stretch="Uniform" />

In my ViewModel I have:

private SvgImageSource _svgSource;
public SvgImageSource SvgSource
{
 get => _svgSource;
 set => _ = SetProperty(ref _svgSource, value);
}

...and I set the source in the View Model via:

private SVGGenerator generator;
SvgSource = await generator.GetSourceAsync(_generationCancellationTokenSource.Token);

While in my SVGGenerator class I have (amongst other methods):

public async Task<IRandomAccessStream> GetSourceAsync(CancellationToken cancellationToken)
{
 return await Task.Run(() =>
   {
     using (var stringWriter = new StringWriter())
     {
       XmlWriterSettings settings = new XmlWriterSettings
       {
         Indent = true,
         Encoding = Encoding.UTF8
       };
       using (var memoryStream = new MemoryStream())
       {
         using (var xmlTextWriter = XmlWriter.Create(memoryStream, settings))
         {
           _document.WriteTo(xmlTextWriter);
           xmlTextWriter.Flush();
           var ramStream = new InMemoryRandomAccessStream();
           memoryStream.CopyTo(ramStream);
           ramStream.Seek(0);
           return ramStream;
         }
       }
     }
   }, cancellationToken
 );
}

The memoryStream.CopyTo(ramStream); line does not compile - CS1503 C# Argument 1: cannot convert from ‘Windows.Storage.Streams.InMemoryRandomAccessStream’ to 'System.IO.Stream' - because MemoryStream can’t write to an InMemoryRandomAccessStream (or, to put it another way, I can’t figure out how to do it).

Some texts – e.g. How to convert byte array to InMemoryRandomAccessStream or IRandomAccessStream in windows 8 – I have seen suggest using memoryStream.AsRandomAccessStream() but I can’t figure out how to use the .AsRandomAccessStream() extension method (it’s not available to me and I don’t know where to get it as the code available doesn’t show the using statements).

Other texts – e.g. https://csharp.hotexamples.com/examples/Windows.Storage.Streams/InMemoryRandomAccessStream/AsStream/php-inmemoryrandomaccessstream-asstream-method-examples.html – suggest using memoryStream.CopyTo(ramStream.AsStream()); but the .AsStream() extension isn’t available for similar reasons.

I’m going round and round in circles at the moment.

'All' I want to do is either write the text from an XmlDocument directly to an InMemoryRandomAccessStream or copy the contents of a MemoryStream to an InMemoryRandomAccessStream but I just can’t figure out how to do either.

Can anyone help me please? Should I be doing this in another (simpler) way?

GarryP
  • 143
  • 9
  • Set position of the MemoryStream to zero before writing to the InMemoryRandomAccessStream. After writing to the MemoryStream the position is at the end of the file so when you read the MemoryStream you get no data : memoryStream.postition = 0; – jdweng Jun 03 '20 at 09:45
  • Thanks for the tip, but I should have been clearer in saying that the `memoryStream.CopyTo(ramStream);` line **does not compile**: CS1503 C# Argument 1: cannot convert from ‘Windows.Storage.Streams.InMemoryRandomAccessStream’ to 'System.IO.Stream'. – GarryP Jun 03 '20 at 10:32
  • See following MSDN example : https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-19041 – jdweng Jun 03 '20 at 10:54
  • could you share more detail about `_document`, I need make a code sample to reproduce this, and you could also share min sample for us for quickly response. – Nico Zhu Jun 03 '20 at 11:01
  • jdweng: Thanks for the link. That seems to have taken me a bit further but now I’m getting other errors that I don’t understand. (System.ObjectDisposedException: 'The object has been closed. (Exception from HRESULT: 0x80000013)') It’s my first time using async processing, and SvgImageSource, and InMemoryRandomAccessStream so I’m having trouble figuring out where the problems are actually coming from when using them all at the same time. Thanks anyway. – GarryP Jun 03 '20 at 12:10
  • Nico Zhu: _document is just an XmlDocument object. The SVG XML in it is fine and can be displayed nicely as text in a TextBlock component, it’s getting the text into the SvgImageSource that I’m having trouble with. Sorry, but I’m not comfortable publicly sharing a lot more code than I already have. If you can tell me specifically what you need then I might be able to share it. – GarryP Jun 03 '20 at 12:10
  • It will be better to make reproduce sample, If you could share the complete ViewModel code – Nico Zhu Jun 03 '20 at 12:21
  • The View Model is based upon my own base class (which I don’t want to share) which is itself based upon the Prism ViewModelBase which is based upon the Prism BindableBase. I think I have shared all of the code in the View model which is relevant to this situation (except the definition of _generationCancellationTokenSource = new CancellationTokenSource();) The GetSourceAsync method above is called inside another async method – to refresh the contents of the Image component – which is called when a message is received from the standard Prism EventAggregator. – GarryP Jun 03 '20 at 12:55
  • Ok, I got it, I will make a code for this thread. – Nico Zhu Jun 08 '20 at 01:23
  • Please check `memoryStream` parameter. it is output stream, but you send empty `MemoryStream` to writer. – Nico Zhu Jun 11 '20 at 09:24
  • Please try to modify `WriteTo` to `Save` method that could make sure memoryStream has content. – Nico Zhu Jun 11 '20 at 09:45

1 Answers1

1

For testing your code, I found memoryStream parameter is output type, but you send empty MemoryStream to the method. So, please try to modify WriteTo to Save method that could make sure memoryStream has content. I post the complete GetSourceAsync below, and you could use it directly.

public async Task<IRandomAccessStream> GetSourceAsync(CancellationToken cancellationToken)
{
    return await Task.Run(async () =>
    {
        using (var stringWriter = new StringWriter())
        {
            XmlWriterSettings settings = new XmlWriterSettings
            {
                Indent = true,
                Encoding = Encoding.UTF8
            };
            using (var memoryStream = new MemoryStream())
            {
                using (var xmlTextWriter = XmlWriter.Create(memoryStream, settings))
                {
                    _document.Save(xmlTextWriter);
                    xmlTextWriter.Flush();
                    var ibuffer = memoryStream.GetWindowsRuntimeBuffer();
                    InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
                    await randomAccessStream.WriteAsync(ibuffer);
                    randomAccessStream.Seek(0);
                    return randomAccessStream;
                }
            }
        }
    }, cancellationToken
    );
}
Nico Zhu
  • 32,367
  • 2
  • 15
  • 36
  • Many thanks for that but, after pasting your code in directly, I now get the following errors: On the line “var ibuffer = memoryStream.GetWindowsRuntimeBuffer();” I get “CS1061 'MemoryStream' does not contain a definition for 'GetWindowsRuntimeBuffer' and no accessible extension method 'GetWindowsRuntimeBuffer' accepting a first argument of type 'MemoryStream' could be found.” (I cannot add “using System.Runtime.InteropServices.WindowsRuntime;” to get the extension method.) See next comment... – GarryP Jun 12 '20 at 07:40
  • And on the line “await randomAccessStream.WriteAsync(ibuffer);” I get “CS0012 The type 'IAsyncOperationWithProgress<,>' is defined in an assembly that is not referenced. You must add a reference to assembly 'Windows.Foundation.FoundationContract,…” (I don’t really know what that means yet but I think might have something to do with this being a UWP application, possibly.) – GarryP Jun 12 '20 at 07:41
  • `GetWindowsRuntimeBuffer` namespace is `WindowsRuntimeBufferExtensions` and the assembly is `System.Runtime.WindowsRuntime.dll`. I could share my code sample for you. – Nico Zhu Jun 12 '20 at 07:46
  • Please check this [sample](https://github.com/ZhuMingHao/SvgTest). – Nico Zhu Jun 12 '20 at 07:47
  • I have updated code sample with adding xaml binding – Nico Zhu Jun 12 '20 at 08:14
  • I installed the Nuget package into my related (.NET 2.0) project (a services DLL - I forgot I wasn't in the UWP project) but I now get “CS0012 The type 'IBuffer' is defined in an assembly that is not referenced...” on the line `var ibuffer = memoryStream.GetWindowsRuntimeBuffer();` I can define a variable as, for example, `IBuffer variable = null;` just fine, but the line from your code gives the error and I don’t know why one works and the other doesn’t. (I have included `using System.Runtime.InteropServices.WindowsRuntime;` and that seems to be working now.) See next comment... – GarryP Jun 14 '20 at 12:01
  • I also get “CS0012 The type 'IAsyncOperationWithProgress<,>' is defined in an assembly that is not referenced...” on the line `await randomAccessStream.WriteAsync(ibuffer);` and I don’t know where that has come from either. There must be something very basic that I’m just not seeing but I seem to be going round in circles with packages and references and namespaces and libraries. Can you help further or do these two problems require a different question? (I don’t normally do this sort of thing so I'm probably getting myself confused.) – GarryP Jun 14 '20 at 12:02
  • Have you run my code sample? for my testing it wok well in my side. – Nico Zhu Jun 15 '20 at 08:40
  • Sorry, yes, I should have said that I can run your application without a problem. Unfortunately I don’t know why your application runs okay and mine doesn’t. I don’t know where the extra compiler errors have come from as everything seems to be right (as far as I can tell). Will it make a difference that I used Windows Template Studio to create my application? – GarryP Jun 16 '20 at 07:42
  • You placed above code in the class lib but not uwp project, right? – Nico Zhu Jun 16 '20 at 07:51
  • Could you mind share a blank template studio app with above code for us, I will test base on your demo. – Nico Zhu Jun 16 '20 at 08:08
  • Sharing a blank WTS app might not help as, because of a recent update to WTS, I seem only able to create new Windows 10 2004 applications which I can’t edit fully on my 1909 machine yet. WTS told me I had to update it, so I updated it, then I couldn’t edit the XAML pages of a new WTS-generated app because my machine wasn’t up-to-date. I have no idea why this has happened and I am waiting for my machine to let me update to the latest Windows version to see if things go back to ‘normal’. – GarryP Jun 16 '20 at 12:19
  • I decided to move all of the code from my .NET library project into my UWP project to try and simplify the problem and it’s working now. I guess it could have been an issue with multiple definitions of the same interfaces/classes in different namespaces (or some similar mix-up) but I haven’t got time to look at that at the moment. I’m just so happy it’s working. I really appreciate all of the time and effort you have put into this. You went above and beyond my expectations. Thank you very much. – GarryP Jun 16 '20 at 12:22
  • @GarryP, no bother, if the answer is helpful, please consider mark it thanks. – Nico Zhu Jun 17 '20 at 02:32