简体   繁体   中英

How to implement 'Xamarin.Forms Two bindable properties inside a DataTemplateView'?

I'm working on this kind of code

My ImageResource class

[ContentProperty(nameof(Source))]
class ImageResourceExtension : IMarkupExtension
{
  public string Source { get; set; }
  public object ProvideValue(IServiceProvider serviceProvider)
  {
    var newImageSource = ImageSource.FromResource(Source, typeof(ImageResourceExtension).GetTypeInfo().Assembly);
    return newImageSource;
  }
}

My ListModel class

class ListModel
{
   public string IconSource { get; set; }
   public string IconPath { get; set; }
}

My MyViewModel class

class MyViewModel : INotifyPropertyChanged
{
   public List<Models.ListModel> MyListModel { get; }
   public Command MyListCommand { get; }
   public MyViewModel()
   {
      MyListModel = new List<Models.ListModel>()
      {
         new Models.ListModel(){ IconSource="https://example.com/image1",IconPath="MyImage1.jpg"},
         new Models.ListModel(){ IconSource="https://example.com/image2",IconPath="MyImage2.jpg"}
      };
      MyListCommand = new Command(OnListClicked);
   }

   private async void OnListClicked(object value)
   {
      string valueString = value as string;
      await Launcher.OpenAsync(valueString);
   }
 }

My View class

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:images="clr-namespace:MyProject.Images"
    xmlns:models="clr-namespace:MyProject.Models"
    xmlns:viewModels="clr-namespace:MyProject.ViewModels"

...

   <ContentPage.BindingContext>
      <viewModels:MyViewModel/>
   </ContentPage.BindingContext>

   <ContentPage.Content>
     <StackLayout BindableLayout.ItemsSource="{Binding MyListModel}">
       <BindableLayout.ItemTemplate>
         <DataTemplate> <!-- line{setup path} -->
           <Image Source="{images:ImageResource Source={Binding Source={RelativeSource AncestorType={x:Type viewModels:MyListModel}}, Path=ImageSource}}">
             <Image.GestureRecognizers>
               <TapGestureRecognizer 
                 NumberOfTapsRequired="1" 
                 Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:MyListModel}}, Path=MyListCommand}" 
                 CommandParameter="{Binding ImagePath}"/>
             </Image.GestureRecognizers>
           </Image>
         </DataTemplate>
       </BindableLayout.ItemTemplate>
     </StackLayout>
   </ContentPage.Content>

My problem is how to set a dynamic or bindable property for my image source at this line: {setup path} ?

I really appreciate it if you can solve this problem. Thank you.

Edited: I forgot to add my images are Embedded resource inside of my shared code.

You can use Value Converters to achieve this function.

Suppose your images is placed in folder Images of your xamarin form project(eg MyFormDemo ).

1.create EmbeddedToImageSourceConverter.cs

public class EmbeddedToImageSourceConverter: IValueConverter
{


    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string ResourceId = value.ToString();
        if (String.IsNullOrWhiteSpace(ResourceId))
            return null;
        return ImageSource.FromResource(ResourceId);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

2.In the constructor of MyViewModel.cs ,init IconPath as follows(add MyFormDemo.Images. ):

       public MyViewModel()
    {
        MyListModel = new List<ListModel>()
  {
     new ListModel(){ IconSource="https://example.com/image1",IconPath="MyFormDemo.Images.cherry.png"},
     new ListModel(){ IconSource="https://example.com/image2",IconPath="MyFormDemo.Images.love.png"}
  };
        MyListCommand = new Command(OnListClicked);
    }

3.Implement interface INotifyPropertyChanged for class ListModel :

  public class ListModel: INotifyPropertyChanged
{
    public string IconSource { get; set; }
    //public string IconPath { get; set; }

    string _iconPath;
    public string IconPath
    {
        set { SetProperty(ref _iconPath, value); }

        get { return _iconPath; }
    }


    bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

4.in xaml of your page:

<ContentPage.Resources>
    <MyFormDemo:EmbeddedToImageSourceConverter x:Key="converter">
    </MyFormDemo:EmbeddedToImageSourceConverter>
</ContentPage.Resources>

<ContentPage.BindingContext>
    <viewmodel:MyViewModel></viewmodel:MyViewModel>
</ContentPage.BindingContext>

<ContentPage.Content>
    <StackLayout BindableLayout.ItemsSource="{Binding MyListModel}">
        <BindableLayout.ItemTemplate>
            <DataTemplate>

                <!-- line{setup path} -->
                <Image Source="{Binding IconPath, Converter={StaticResource converter}}"  WidthRequest="60" HeightRequest=" 60" BackgroundColor="Gray">

                </Image>


            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </StackLayout>
</ContentPage.Content>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM