Xamarin:从PCL(不是来自参考资料)向我的按钮添加图像

时间:2017-07-20 10:29:19

标签: xamarin imagebutton portable-class-library

我正在使用PCL(而非共享项目)开发Xamarin.Forms项目。
我在Android和iOS项目的资源文件夹中都有一些图像。

这是有效的,图标显示在按钮中,因为它们应该:

<Button Image="speaker.png" Grid.Row="0" Grid.Column="0" />

我的PCL项目中还有一个包含一些图像的文件夹: images / icons / speaker.png 我试过这个:

<Button Image="{local:EmbeddedImage TestThree.images.icons.speaker.png}" />

......但那不起作用......

我希望这些按钮能够在我的PCL项目中显示图像文件夹中的图像。
所以我的问题是......

<Button WHAT GOES HERE? Grid.Row="0" Grid.Column="0" />

2 个答案:

答案 0 :(得分:3)

当Button.Image想要FileImageStream时,我会把它交给他。但是作为项目中的图像,我仍然在PCL(或.NET标准2.0)库(项目)中使用嵌入式资源PNG文件。 例如,PCL项目名称是“MyProject”,我在其子文件夹“Images \ Icons \ ok.png”中放置了一个图像。然后资源名称(例如,对于ImageSource.FromResource)是“MyProject.Images.Icons.ok.png”。 此方法将嵌入的资源文件复制到应用程序本地存储中的文件中(仅第一次)。

public static async Task<string> CopyIcon(string fileName)
{
    if (String.IsNullOrEmpty(fileName)) return "";
    try
    {
        // Create (or open if already exists) subfolder "icons" in application local storage
        var fld = await FileSystem.Current.LocalStorage.CreateFolderAsync("icons", CreationCollisionOption.OpenIfExists);
        if (fld == null) return ""; // Failed to create folder

        // Check if the file has not been copied earlier
        if (await fld.CheckExistsAsync(fileName) == ExistenceCheckResult.FileExists)
            return (await fld.GetFileAsync(fileName))?.Path; // Image copy already exists

        // Source assembly and embedded resource path
        string imageSrcPath = $"MyProject.Images.Icons.{fileName}"; // Full resource name
        var assembly = typeof(FileUtils).GetTypeInfo().Assembly;

        // Copy image from resources to the file in application local storage
        var file = await fld.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
        using (var target = await file.OpenAsync(PCLStorage.FileAccess.ReadAndWrite))
        using (Stream source = assembly.GetManifestResourceStream(imageSrcPath))
            await source.CopyToAsync(target); // Copy file stream

        return file.Path; // Result is the path to the new file
    }
    catch 
    {
        return ""; // No image displayed when error
    }
}

当我有一个常规文件时,我可以将它用于FileImageStream(Button.Image)。 第一个选项是从代码中使用它。

public partial class MainPage : ContentPage
{
    protected async override void OnAppearing()
    {
        base.OnAppearing();
        btnOk.Image = await FileUtils.CopyIcon("ok.png"); 
    }   
}

我也可以在XAML中使用它,但我必须创建一个IMarkupExtension接口的实现。

[ContentProperty("Source")]
public class ImageFileEx : IMarkupExtension
{
    public string Source { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return Task.Run(async () => await FileUtils.CopyIcon(Source)).Result;
    }
}

现在我可以直接在XAML中分配图像。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyProject"
             x:Class="MyProject.MainPage"
             xmlns:lcl="clr-namespace:MyProject;assembly=MyProject">
    <Grid VerticalOptions="Fill" HorizontalOptions="Fill">
        <Button Image="{lcl:ImageFileEx Source='ok.png'}" Text="OK" />
    </Grid>
</ContentPage>

对于此解决方案,需要NuGet PCLStorage

答案 1 :(得分:0)

不起作用的原因是因为属性绑定到不同的类型。

Button的Image属性采用“FileImageSource” - Github

Image的Source属性采用“ImageSource” - Github

来自本地:EmbeddedImage即时猜测您使用的是Xamarin forms image docs

的扩展程序

这不起作用,因为它加载了“StreamImageSource”而不是“FileImageSource”。

在实践中你不应该这样做,因为它不会从不同尺寸的图像(@ 2x,xhdpi等)加载,并且会产生质量差的图像而不支持多种分辨率。

您可以使用带有TapGestureRecognizer的视图容器(堆栈布局等)以及在其中居中的图像,或者创建一个自定义渲染器,它实际上比它的价值更省力。但是,这些仍然显然不能处理多个图像。

正确的解决方案是从基础生成正确大小的图像(我假设它将是MDPI Android或1X for iOS)并将它们放在正确的文件夹中并在您的工作按钮示例中引用它们。