Quantcast
Channel: Xamarin.Forms — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 58056

Platform Specific Properties

$
0
0

Over in the Custom Renders Feedback thread I floated the idea of being able to have a simple method to set platform specific properties that are not currently implemented in XForms (X.F?). So I started playing around with what it would take to make something like that work and I came up with the following proof-of-concept example:

In the shared/portable view or layout I wanted to be able to write something like this:

var customSearchBar = new PlatformSpecificSearchBar
        {
            Placeholder = "Search",
            WidthRequest = 300,
            PlatformSpecificProperties =
            {
                new PlatformSpecificProperty
                {
                    Name = "BarTintColor",
                    Platform = TargetPlatform.iOS,
                    Value = Color.Red
                }
            }
        };

To make this work you would need a custom class to implement the IPlatformSpecificPropertiesElement interface (a custom SearchBar is used as example below):

 public interface IPlatformSpecificPropertiesElement
    {
        List<PlatformSpecificProperty> PlatformSpecificProperties { get; set; }
    }

   public class PlatformSpecificSearchBar : SearchBar, IPlatformSpecificPropertiesElement
    {
        public PlatformSpecificSearchBar()
        {
            PlatformSpecificProperties = new List<PlatformSpecificProperty>();
        }

        public List<PlatformSpecificProperty> PlatformSpecificProperties { get; set; }
    }

And to implement the properties you will need a custom renderer in each of your supported platforms. The custom render will call a PropertiesRender (in this case a custom SearchBarRender is shown):

   public class CustomSearchBarRenderer : SearchBarRenderer
    {
        private readonly PropertiesRenderer<SearchBar, UISearchBar> _propertiesRenderer;

        public CustomSearchBarRenderer()
        {
            _propertiesRenderer = new PropertiesRenderer<SearchBar, UISearchBar>(this);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
        {
            base.OnElementChanged(e);
            _propertiesRenderer.SetCustomProperties();
        }
    }

The PropertiesRender does all of the heavy lifting for mapping the properties to the base type (iOS example seen below):

    public class PropertiesRenderer<TSource, TArget>
        where TSource : View
        where TArget : UIView 
    {
        private readonly ViewRenderer<TSource, TArget> _renderer;

        public PropertiesRenderer(ViewRenderer<TSource, TArget> renderer)
        {
            _renderer = renderer;
        }

        public void SetCustomProperties()
        {
            var element = (IPlatformSpecificPropertiesElement) _renderer.Element;
            var iOSProperties = element.PlatformSpecificProperties.Where(x => x.Platform == TargetPlatform.iOS).ToList();
            var props = typeof (UISearchBar).GetProperties();

            foreach (var p in iOSProperties)
            {
                var prop = props.FirstOrDefault(x => x.Name == p.Name);
                if (prop == null) continue;

                if (prop.PropertyType == typeof (UIImage))
                {
                    SetImage((ImageSource) p.Value, prop);
                    continue;
                }

                if (prop.PropertyType == typeof(UIFont))
                {
                    SetFont((Font)p.Value, prop);
                    continue;
                }

                if (prop.PropertyType == typeof (UIColor))
                {
                    var typedValue = (Color) p.Value;
                    prop.SetValue(_renderer.Control, typedValue.ToUIColor());
                    continue;
                }

                if (prop.PropertyType.IsEnum)
                {
                    object typedValue = Enum.Parse(prop.PropertyType, p.Value as string);
                    prop.SetValue(_renderer.Control, typedValue);
                    continue;
                }

                prop.SetValue(_renderer.Control, p.Value);
            }
        }

        private void SetFont(Font value, PropertyInfo property)
        {
            property.SetValue(_renderer.Control, value.ToUIFont());
        }

        private async void SetImage(ImageSource source, PropertyInfo property)
        {
            if (source == null) return;
            var type = source.GetType();
            var handler = GetFileHandler(type);

            if (handler == null) return;
            UIImage uiimage;
            try
            {
                uiimage =
                    await handler.LoadImageAsync(source, new CancellationToken(), UIScreen.MainScreen.Scale);
                property.SetValue(_renderer.Control, uiimage);
            }
            finally
            {
                uiimage = null;
            }
        }

        private static IImageSourceHandler GetFileHandler(Type type)
        {
            if (type == typeof(FileImageSource))
            {
                return new FileImageSourceHandler();
            }

            if (type == typeof(StreamImageSource))
            {
                return new StreamImagesourceHandler();
            }
            return null;
        }
    }

It can currently handle Colors, Fonts, Images, Enums and basic types. A few more usage examples:

     // Set SearchBarStyle which is an enum    
    customSearchBar.PlatformSpecificProperties.Add(
                new PlatformSpecificProperty
                {
                    Name = "SearchBarStyle",
                    Platform = TargetPlatform.iOS,
                    Value = "Minimal"
                });

    // Set an image
    customSearchBar.PlatformSpecificProperties.Add(new PlatformSpecificProperty
            {
                Name = "BackgroundImage",
                Platform = TargetPlatform.iOS,
                Value = ImageSource.FromFile("logo.png")
            });

This is all obviously very rough around the edges and I haven't worked on an Android property renderer class, though it shouldn't be much different than the iOS version. It also is non-bindable for XAML folks (and unless someone who is more comfortable with XAML can come up with a solution). But it has already helped me be more productive with my project, so I thought I'd put it out there.


Viewing all articles
Browse latest Browse all 58056

Trending Articles