Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Access ContentPage or ContentView from MarkupExtension

RedRaRedRa Member ✭✭✭
edited March 29 in Xamarin.Forms

Hi everyone,

I have faced with the new issue, how to get ContentPage from MarkupExtension ?

I have tried the following method:

static class ViewExtensions
{
    /// <summary>
    /// Gets the page to which an element belongs
    /// </summary>
    /// <returns>The page.</returns>
    /// <param name="element">Element.</param>
    public static Page GetParentPage(this Element element)
    {
        if (element != null)
        {
            var parent = element.Parent;
            while (parent != null)
            {
                if (parent is Page)
                {
                    return parent as Page;
                }
                parent = parent.Parent;
            }
        }
        return null;
    }
}

... but it did not work properly, parent is always null ... (

Then I have found the following question on StackOverflow https://stackoverflow.com/questions/55869794/access-contentpage-from-imarkupextension
But the solution that was suggested by Sharada Gururaj is not possible because it is internal API:

public object ProvideValue(IServiceProvider serviceProvider)
{
        if (serviceProvider == null)
            throw new ArgumentNullException(nameof(serviceProvider));
        if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideParentValues valueProvider))
            throw new ArgumentException();

        foreach (var p in valueProvider.ParentObjects) {
            if (!(target is ContentPage page))
                continue;

                /// Do your processing for content-page here..
         }
}

I cannot find proper solution for getting ContentPage or ContentView !?

Has anyone found solution ?

Best Answer

  • RedRaRedRa Member ✭✭✭
    edited March 30 Accepted Answer

    @Jarvan said:
    Could you post the related code of calling the 'GetParentPage' method?

    Thank to Sharada Gururaj, he has updated his answer on question https://stackoverflow.com/questions/55869794/access-contentpage-from-imarkupextension/55873982?noredirect=1#comment107788763_55873982

    [ContentProperty(nameof(Value))]
    public class SomeExtension : IMarkupExtension
    {
        public string? Value { get; set; }
    
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
                throw new ArgumentNullException(nameof(serviceProvider));
    
            var valueProvider = serviceProvider.GetService<IProvideValueTarget>()    
                                ?? throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
    
            Page? parentPage = null;
            PropertyInfo cachedPropertyInfo = valueProvider.GetType().GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects", BindingFlags.NonPublic | BindingFlags.Instance);
            if(cachedPropertyInfo != null)
            {
                var parentObjects = cachedPropertyInfo.GetValue(valueProvider) as IEnumerable<object>;
                if (parentObjects == null)
                    throw new ArgumentException("Unable to access parent objects");
    
                foreach (var target in parentObjects)
                {
                    if (!(target is Page page))
                    {
                        parentPage = target as Page;
                        // ---->>> Access target here for root parent page.
                        break;
                    }
                }
            }
    
            throw new XamlParseException($"Unable to access parent page");
        }
    }
    

    It is very useful, but it is sad that it is necessary to use private API for this (:

    Also sad that after getting the parent page there is absent BindingContext (it is null) ... all this was made for getting the BindingContext of ContentPage, but at the moment of call MarkupExtension it is absent (:

Answers

  • JarvanJarvan Member, Xamarin Team Xamurai

    How did you use the 'GetParentPage' method? Pass a control of the page to call the method, it could return a valid result.

    private void Button_Clicked(object sender, EventArgs e)
    {
        Page result = GetParentPage(label);
    
        if (result != null)
        {
            Console.WriteLine("The page type: "+ result.GetType().ToString());
        }
    }
    
    Page GetParentPage(Element element)
    {
        if (element != null)
        {
            var parent = element.Parent;
            while (parent != null)
            {
                if (parent is Page)
                {
                    return parent as Page;
                }
                parent = parent.Parent;
            }
        }
        return null;
    }
    
  • RedRaRedRa Member ✭✭✭

    @Jarvan said:
    How did you use the 'GetParentPage' method? Pass a control of the page to call the method, it could return a valid result.

    private void Button_Clicked(object sender, EventArgs e)
    {
        Page result = GetParentPage(label);
    
        if (result != null)
        {
            Console.WriteLine("The page type: "+ result.GetType().ToString());
        }
    }
    
    Page GetParentPage(Element element)
    {
        if (element != null)
        {
            var parent = element.Parent;
            while (parent != null)
            {
                if (parent is Page)
                {
                    return parent as Page;
                }
                parent = parent.Parent;
            }
        }
        return null;
    }
    

    I use it inside of my MarkupExtension and inside this MarkupExtension it returns null ... (

  • JarvanJarvan Member, Xamarin Team Xamurai

    Try to add a breakpoint to check if the 'Element' is null.

  • RedRaRedRa Member ✭✭✭

    @Jarvan said:
    Try to add a breakpoint to check if the 'Element' is null.

    Element is not null, but there are no Parent that is ContentPage ... (

  • JarvanJarvan Member, Xamarin Team Xamurai

    Could you post the related code of calling the 'GetParentPage' method?

  • RedRaRedRa Member ✭✭✭
    edited March 30

    @Jarvan said:
    Could you post the related code of calling the 'GetParentPage' method?

    [ContentProperty(nameof(Value))]
    public class SomeExtension : IMarkupExtension
    {
        public string? Value { get; set; }
    
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            var elem = (Element)provideValueTarget.TargetObject;
            var contentPage = elem.GetParentPage();
            // Some logic ...
            return true;
        }
    }
    

    ContentPage is null ... (

  • RedRaRedRa Member ✭✭✭
    edited March 30 Accepted Answer

    @Jarvan said:
    Could you post the related code of calling the 'GetParentPage' method?

    Thank to Sharada Gururaj, he has updated his answer on question https://stackoverflow.com/questions/55869794/access-contentpage-from-imarkupextension/55873982?noredirect=1#comment107788763_55873982

    [ContentProperty(nameof(Value))]
    public class SomeExtension : IMarkupExtension
    {
        public string? Value { get; set; }
    
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null)
                throw new ArgumentNullException(nameof(serviceProvider));
    
            var valueProvider = serviceProvider.GetService<IProvideValueTarget>()    
                                ?? throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
    
            Page? parentPage = null;
            PropertyInfo cachedPropertyInfo = valueProvider.GetType().GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects", BindingFlags.NonPublic | BindingFlags.Instance);
            if(cachedPropertyInfo != null)
            {
                var parentObjects = cachedPropertyInfo.GetValue(valueProvider) as IEnumerable<object>;
                if (parentObjects == null)
                    throw new ArgumentException("Unable to access parent objects");
    
                foreach (var target in parentObjects)
                {
                    if (!(target is Page page))
                    {
                        parentPage = target as Page;
                        // ---->>> Access target here for root parent page.
                        break;
                    }
                }
            }
    
            throw new XamlParseException($"Unable to access parent page");
        }
    }
    

    It is very useful, but it is sad that it is necessary to use private API for this (:

    Also sad that after getting the parent page there is absent BindingContext (it is null) ... all this was made for getting the BindingContext of ContentPage, but at the moment of call MarkupExtension it is absent (:

Sign In or Register to comment.