# Reading picked files: PkDropzone and PkImageGallery with InputFile

> Plainkit 0.28.0. Generated by scripts/build-skills.mjs from the pk-dropzone and pk-image-gallery elements and their generated components. Do not edit: change the source and run the generator.

Both elements keep their files in their own shadow-tree input, which Blazor cannot read. To read the bytes use the Blazor `InputFile`: put it in the `input` slot and the element only draws the target. There is no `IBrowserFile` marshalling in Plainkit and no interop per render; `InputFileChangeEventArgs`, `IBrowserFile` and `OpenReadStream` belong to Blazor and work the same in Blazor Server and Blazor WebAssembly.

## `PkDropzone`

```razor
@using Microsoft.AspNetCore.Components.Forms

<PkDropzone Label="Files to upload" BrowseLabel="Choose files" Multiple="true">
    <ChildContent>
        <InputFile slot="input" multiple OnChange="OnChange" />
        Drop files here or click to choose
    </ChildContent>
    <HintContent>Text files, up to 1 MB each</HintContent>
</PkDropzone>

@code {
    private async Task OnChange(InputFileChangeEventArgs e)
    {
        foreach (var file in e.GetMultipleFiles(10))
        {
            using var stream = file.OpenReadStream(1024 * 1024); // the limit is yours; the default is 500 KB
            // read or copy the stream
        }
    }
}
```

Rules:

- `slot="input"` goes on the `InputFile` itself (it passes unknown attributes to the `<input>`), as a direct child of `PkDropzone`, so the input covers the whole zone. When any named fragment (`HintContent`) is used, wrap the title and the `InputFile` in an explicit `<ChildContent>`.
- A drop and the picker (the zone itself, or the `BrowseLabel` button) both end in the `OnChange` of the `InputFile`: a drop puts the dropped files into that input and raises its `change` event. A single-file input (no `multiple`) keeps the first dropped file; a drop without files does nothing.
- `OnChange` is the source of truth. In this mode the zone does not check `Accept`, `MaxFileSizeBytes` or `MaxFiles`, does not draw a file list and does not raise `OnFiles`: use `accept` and `multiple` on the `InputFile`, check `IBrowserFile.Size` and `ContentType` in `OnChange`, and set `maxAllowedSize` in `OpenReadStream` (it throws `IOException` past the limit).
- `BrowseLabel` and `Disabled` keep working (`Disabled` blocks the button and drops). Render the file list yourself from the `IBrowserFile`s.
- Without an `InputFile` (no slot), `OnFiles` reports counts and the accepted and rejected files stay in the browser: use it for client-side checks, not for reading bytes.

## `PkImageGallery`

The add tile works the same way, through its own `InputContent` parameter (a dedicated named slot, not `ChildContent`: `PkImageGallery` has no default slot). The wrapper sets `slot="input"` for you, so the `InputFile` itself needs none.

```razor
@using Microsoft.AspNetCore.Components.Forms

<PkImageGallery Images="@_images" Editable="true">
    <InputContent>
        <InputFile multiple OnChange="OnChange" />
    </InputContent>
</PkImageGallery>

@code {
    private IReadOnlyList<PkGalleryImage> _images = [];

    private async Task OnChange(InputFileChangeEventArgs e)
    {
        foreach (var file in e.GetMultipleFiles(10))
        {
            using var stream = file.OpenReadStream(5 * 1024 * 1024);
            // read or copy the stream, then append a PkGalleryImage to _images
        }
    }
}
```

`OnAdd` (the `pk-add` event) does not fire for a pick made on the slotted `InputFile` — `OnChange` is the only read path, the same split as `PkDropzone`'s `OnFiles`. Without an `InputFile`, `OnAdd` still reports the chosen files' names, but never their bytes.
