Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blazor Hybrid: Static files topic #25674

Closed
guardrex opened this issue Apr 19, 2022 · 16 comments · Fixed by #25891
Closed

Blazor Hybrid: Static files topic #25674

guardrex opened this issue Apr 19, 2022 · 16 comments · Fixed by #25891

Comments

@guardrex
Copy link
Collaborator

guardrex commented Apr 19, 2022

When content is provided in an issue comment here, please remove the PU member assignment and assign the issue to @guardrex 🦖. PU member will be pinged on the PR when it goes up.

aspnetcore/blazor/hybrid/static-files.md
@TanayParikh
Copy link
Contributor

TanayParikh commented May 10, 2022

@guardrex Is the ask here for documentation on how to access absolute file paths on the native file system, for all platforms?

cc/ @danroth27

@guardrex
Copy link
Collaborator Author

I wasn't told what the content would be.

@TanayParikh
Copy link
Contributor

@danroth27 did you have a suggestion for what we wanted this issue to cover?

@TanayParikh
Copy link
Contributor

Oh is this to be a MAUI version of https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/static-files?

@guardrex
Copy link
Collaborator Author

If so on ...

MAUI version

@danroth27 ... general ❓ ... is the intention is to circle around later to address any code example deltas for WPF and WinForms? I understand that the concepts will the same (or similar), but devs prefer to see doc examples for the framework that they're working with. We found that out PDQ when RP was released and the docs were mostly MVC-based.

@danroth27
Copy link
Member

The idea behind this topic is that static files are a bit different in Blazor Hybrid apps. In web apps static files are deployed to the server and made available over HTTP. In a Blazor Hybrid app, static files are deployed into the app as static assets. How those static assets get handled is different on every platform. From what I understand .NET MAUI does some work to try to abstract this with their MAUI assets stuff. Then there's the question of how should you refer to static assets in a Blazor Hybrid app. From what I understand you simply use a relative path, similar to what you would do in a web app. Maybe we also need to say something about the base address for the Blazor Hybrid app - not sure. So this doc is about explaining about how static assets in a native app are similar & different than in a web app, and link to the appropriate .NET MAUI content on static asset handling.

@TanayParikh
Copy link
Contributor

TanayParikh commented May 11, 2022

For Blazor Hybrid with WinForms & WPF, we'll be using the native Resources capability to stay consistent with platform standards. Steps are as follows:

  1. Open your WPF/WinForms application, and setup the Blazor Web View (insert crosslink if required to initial setup instructions)
  2. Create an asset you'd like to use in your application. For instance, I created a Resources directory, and placed MyAsset.txt within it, containing This is some text from my asset..
  3. Right click on your project, select Properties and then navigate to Resources > General.

image

  1. Select Create or open assembly resources
  2. Open the Strings dropdown, and select Files

image

  1. Select Add Resource, and then select MyAsset.txt created earlier. You should see the asset reflected in your resources:

image

  1. Go back to your *.razor component and paste the following snippet, replacing INSERT_PROJECT_NAME_HERE with your project name.
@using System.Resources

@AssetText

@code {
    public string AssetText { get; set; } = "Loading asset...";

    protected override void OnInitialized()
    {   
        var resources = new ResourceManager(typeof(INSERT_PROJECT_NAME_HERE.Properties.Resources));
        var assetObject = resources.GetObject("MyAsset");
        if (assetObject is null)
        {
            throw new KeyNotFoundException("'MyAsset' could not be found");
        }

        AssetText = (string)assetObject;
    }
}
  1. Run your application, and you should see the content of your MyAsset.txt file:

image


For Blazor Hybrid MAUI, we'll be using the MauiAsset Build Action, as detailed here, I'm confirming whether MAUI already has / will have official docs for this. If so, we'll just do some cross-linking.

@guardrex could you please create the appropriate docs page based on the content above?

@TanayParikh
Copy link
Contributor

TanayParikh commented May 12, 2022

For Blazor Hybrid MAUI, we'll be using the MauiAsset Build Action, as detailed here, I'm confirming whether MAUI already has / will have official docs for this. If so, we'll just do some cross-linking.

Here's the Blazor Hybrid specific sample:

@using System.IO
@using Microsoft.Maui.Storage

<button @onclick="LoadMauiAsset">Load Asset</button>

<br />


@Contents

@code {
    public string Contents = "Press 'Load Asset' to get started";
    async Task LoadMauiAsset()
    {
        using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
        using var reader = new StreamReader(stream);

        Contents = reader.ReadToEnd();
    }
}

This reads the default Resources/Raw/AboutAssets.txt file part of the maui-blazor template. I've confirmed this works in MacOS, iOS, Windows & Android.

Complete instructions in how to use MauiAsset are summarized in this table and should be part of the MAUI general documentation (ie. not repeated in Blazor Hybrid docs).

@guardrex
Copy link
Collaborator Author

guardrex commented May 17, 2022

@TanayParikh ... WRT WinForms/WPF: I understand that taking the Resource Manager route would be useful in localized content situations, but what about the proverbial low-hanging fruit approach for loading static assets for Razor components in the normal way out of wwwroot? I'm just curious ... and reiterating my minimal experience with desktop/mobile.

  1. Drop a text file and an image into wwwroot:

    data.txt:

    This is test text from data.txt.
    

    The data.txt "Content" file must be marked to Copy if newer for ...

    <ItemGroup>
      <Content Update="wwwroot\data.txt">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
    </ItemGroup>

    yj.png:

    yj

    For whatever reason ... apparently ... images don't require marking Copy if newer.

  2. A component can reference these like the following. Using the Counter component just because it's handy in my test app ...

    <h1>Counter</h1>
    
    <p>@@assetText: @assetText</p>
    
    <p>
        <img alt="1991 Jeep Wrangler YJ" src="/yj.png" />
    </p>
    
    <p>Current count: @currentCount</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        private int currentCount = 0;
    
        private void IncrementCount()
        {
            currentCount++;
        }
    
        public string assetText = "Loading asset...";
    
        protected override async Task OnInitializedAsync()
        {   
            try
            {
                using (var sr = new StreamReader("wwwroot/data.txt"))
                {
                    assetText = await sr.ReadToEndAsync();
                }
            }
            catch (FileNotFoundException ex)
            {
                assetText = ex.Message;
            }
        }
    }

Output ...

Capture

I also published this app and ran the executable out of the publish folder. That worked, too.

Is this valid? ... at all?? ... perhaps, as a Razor components only approach for a components-only section???

Docs for localized resources (i.e., Resource Manager) for WPF seem to be articles under https://docs.microsoft.com/dotnet/desktop/wpf/advanced/how-to-localize-an-application) ... especially this one 👉 https://docs.microsoft.com/dotnet/desktop/wpf/advanced/how-to-use-resources-in-localizable-applications, so I'll see if that's a good cross-link and if there are any deltas with it.

BTW ... valid approach or not ... do you know why the text file in wwwroot (data.txt) required the Copy if newer gesture but the image file (yj.png) didn't? I expected to NOT need to mark anything in wwwroot given the direction of the tutorial with the stylesheet and index.html file.

@guardrex
Copy link
Collaborator Author

btw @TanayParikh ... I only mention that ☝️ wondering about it as an alternate approach (only for components). I'll put your steps into the PR as the primary, recommended approach. If what I hacked up is valid tho, should it be in a separate section further down the topic's content?

@guardrex
Copy link
Collaborator Author

Hummmm 🤔 ... IntelliSense can't find WinFormsBlazor2.Properties in var resources = new ResourceManager(typeof(WinFormsBlazor2.Properties.Resources));.

@guardrex
Copy link
Collaborator Author

guardrex commented May 17, 2022

It's composing a hair different to what you posted (working with WinForms at the moment) ... I mean a GetString thing will work for this particular example without the cast ... and the new ResourceManager seems to be happy in WinForms this way ...

StaticFileExample.razor:

@using System.Resources

<p>@@dataResourceText: @dataResourceText</p>

@code {
    public string dataResourceText = "Loading resource ...";

    protected override void OnInitialized()
    {   
        var resources = new ResourceManager("WinFormsBlazor.Form1", typeof(WinFormsBlazor.StaticFileExample).Assembly);
        dataResourceText = resources.GetString("Data") ?? "'Data' could not be found";
    }
}

@guardrex
Copy link
Collaborator Author

... and I think we can avoid the hoops and get the assembly with ye olde this ...

var resources = new ResourceManager("WinFormsBlazor.Form1", this.GetType().Assembly);

@guardrex
Copy link
Collaborator Author

WPF mirrored your code as ...

@using System.Resources

<p>@@dataResourceText: @dataResourceText</p>

@code {
    public string dataResourceText = "Loading resource ...";

    protected override void OnInitialized()
    {   
        var resources = 
            new ResourceManager(typeof(WpfBlazor.Properties.Resources));
        dataResourceText = resources.GetString("Data") ?? "'Data' not found";
    }
}

@guardrex
Copy link
Collaborator Author

guardrex commented May 17, 2022

@TanayParikh ... Ok ... good start today. I have the WinForms and WPF bits laid out in my draft locally. Besides my RAMBLINGS ON HERE ☝️ 🙈 , can you let me know if my earlier remarks ☝️ about the Razor component-only approach with wwwroot static assets (with the Jeep image and text file) is valid/sane 😵? If so, I'll add it to a separate section at the end of the topic as an alternative (Blazory) approach.

On Wednesday morning, I'll add the .NET MAUI piece to the draft and polish the bits further from this morning. I plan to put the PR up tomorrow and ping u for review, but I might need one more day.

UPDATE (5/18, Wednesday 5:30AM CST): Yes, I'll have the draft PR up in a couple of hours. I'm putting final touches on it now. If you don't have time to review it today (Wednesday), that will be fine with me ... sleeping on it 🛌💤 a night and editing it again on Thursday morning will be good for it. ... and BTW ... I do like the component-only static asset web root approach (wwwroot) because I foresee a use case for that approach that makes sense to me. I was able to get it working on Windows for MAUI, WPF, and WinForms. Idk about perf, security, or platform/OS problems that might crop up with it. I'm going to place the section for it on the PR for review to see if it's sane or not. If InSaNe 😵, I'll pull it off the PR ... and we'll never mention that these RexHax™ ever happened! 😆

UPDATE (5/18, 8AM CST): The PR is up now. Tanay to review first. After Tanay's approval, I'll ping DR and Artak on and offline.

@TanayParikh
Copy link
Contributor

WRT WinForms/WPF: I understand that taking the Resource Manager route would be useful in localized content situations, but what about the proverbial low-hanging fruit approach for loading static assets for Razor components in the normal way out of wwwroot?

Is this valid? ... at all??

If what I hacked up is valid tho, should it be in a separate section further down the topic's content?

Yep totally valid, we can definitely include this approach as well. Going the Resource route is just another option (as resources don't necessarily have to be used with localization (but often are).

BTW ... valid approach or not ... do you know why the text file in wwwroot (data.txt) required the Copy if newer gesture but the image file (yj.png) didn't?

Not really sure why that would be.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants