How to set Android AlertDialog's height depending on it's content?

Igor Kravchenko 281 Reputation points
2022-04-17T17:55:47.68+00:00

I have a bindable property Content in my MaterialDialog and want to show it in AndroidX.AppCompat.App.AlertDialog.

Demo

<popups:MaterialDialog Headline="Dialog"  
                                       Accept="Yes"  
                                       Decline="No"  
                                       Cancel="Cancel">  
                    <StackLayout Padding="15">  
                        <controls:MaterialEntry Placeholder="Price 1"/>  
                        <controls:MaterialEntry Placeholder="Price 2"/>  
                        <controls:MaterialEntry Placeholder="Price 3"/>  
                    </StackLayout>  
                </popups:MaterialDialog>  

MaterialDialog:

[ContentProperty(nameof(Content))]  
    public class MaterialDialog : View  
    {  
        public static readonly BindableProperty ContentProperty = BindableProperty.Create(nameof(Content), typeof(View),  
           typeof(MaterialDialog), default(View), BindingMode.TwoWay);  
  
        public static readonly BindableProperty HeadlineProperty = BindableProperty.Create(nameof(Headline), typeof(string),  
           typeof(MaterialDialog), default(string), BindingMode.TwoWay);  
  
        public static readonly BindableProperty MessageProperty = BindableProperty.Create(nameof(Message), typeof(string),  
           typeof(MaterialDialog), default(string), BindingMode.TwoWay);  
  
        public static readonly BindableProperty CancelProperty = BindableProperty.Create(nameof(Cancel), typeof(string),  
           typeof(MaterialDialog), default(string), BindingMode.TwoWay);  
  
        public static readonly BindableProperty DeclineProperty = BindableProperty.Create(nameof(Decline), typeof(string),  
           typeof(MaterialDialog), default(string), BindingMode.TwoWay);  
  
        public static readonly BindableProperty AcceptProperty = BindableProperty.Create(nameof(Accept), typeof(string),  
           typeof(MaterialDialog), default(string), BindingMode.TwoWay);  
  
        public View Content  
        {  
            get => (View)GetValue(ContentProperty);  
            set => SetValue(ContentProperty, value);  
        }  
  
        public string Headline  
        {  
            get => (string)GetValue(HeadlineProperty);  
            set => SetValue(HeadlineProperty, value);  
        }  
  
        public string Message  
        {  
            get => (string)GetValue(MessageProperty);  
            set => SetValue(MessageProperty, value);  
        }  
  
        public string Cancel  
        {  
            get => (string)GetValue(CancelProperty);  
            set => SetValue(CancelProperty, value);  
        }  
  
        public string Decline  
        {  
            get => (string)GetValue(DeclineProperty);  
            set => SetValue(DeclineProperty, value);  
        }  
  
        public string Accept  
        {  
            get => (string)GetValue(AcceptProperty);  
            set => SetValue(AcceptProperty, value);  
        }  
    }  

Renderer:

public class MaterialDialogRenderer : VisualElementRenderer<MaterialDialog>  
{  
    public AndroidX.AppCompat.App.AlertDialog Control { get; private set; }  
  
    public MaterialDialogRenderer(Context context) : base(context)  
    {  
    }  
  
    protected override void OnElementChanged(ElementChangedEventArgs<MaterialDialog> e)  
    {  
        base.OnElementChanged(e);  
        if (e.OldElement != null)  
        {  
            return;  
        }  
        if (e.NewElement != null)  
        {  
            if (Control == null)  
                CreateControl();  
        }  
    }  
  
    private void CreateControl()  
    {  
        if (Element.Content == null)  
            return;  
        //Creating container to wrap XF content  
        ContainerView container = new ContainerView(Context, Element.Content);  
        //Trying to calculate height  
        container.Measure((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);  
  
        //returns 652  
        //int height = container.MeasuredHeight;  
        //returns 1956  
        int height = (int)Context.ToPixels(container.MeasuredHeight);  
  
        MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(Context);  
  
        Control = alertBuilder  
            .SetTitle(Element.Headline)  
            .SetMessage(Element.Message)  
            .SetPositiveButton(Element.Accept, OnPositiveClicked)  
            .SetNegativeButton(Element.Decline, OnNegativeClicked)  
            .SetNeutralButton(Element.Cancel, OnNeutralClicked)  
            .SetView(container)  
            .Create();  
        Control.Show();  
  
        WindowManagerLayoutParams lp = new WindowManagerLayoutParams();  
        lp.CopyFrom(Control.Window.Attributes);  
        //Setting new height here. 652 is too small and cuts content, 1956 is too big and shows empty space  
        lp.Height = height;  
        Control.Window.Attributes = lp;  
    }  
  
    private void OnPositiveClicked(object sender, DialogClickEventArgs e)  
    {  
    }  
  
    private void OnNegativeClicked(object sender, DialogClickEventArgs e)  
    {  
    }  
  
    private void OnNeutralClicked(object sender, DialogClickEventArgs e)  
    {  
    }  
}  

I use a ContainerView to wrap XF content and after this try to measure by container.Measure(). Then set properties to dialog builder and show it. Finally set calculated height.
I have next questions.

What units are returned by container.MeasuredHeight? Pixels or dps? How to properly use them? Because my AlertDialog is too small or too big. Checked on pixel x86, pixel 3 xl and Galaxy S20+, same result.
My goal is setting height of AlertDialog depending on content's height.
Thanks.

P.S. I use a Xamarin.Google.Android.Material to implement material styles. My colors.xml and styles.xml:

colors.xml:

<?xml version="1.0" encoding="utf-8"?>  
<resources>  
 <color name="launcher_background">#EADDFF</color>  
 <color name="colorPrimary">#465880</color>  
 <color name="colorPrimaryDark">#D0BCFF</color>  
 <color name="colorAccent">#7D5260</color>  
  
 <color name="co.andriy.tcuclient_primary">#465880</color>  
 <color name="co.andriy.tcuclient_primary_dark">#D0BCFF</color>  
 <color name="co.andriy.tcuclient_secondary">#625B71</color>  
 <color name="co.andriy.tcuclient_background">#FFFBFE</color>  
 <color name="co.andriy.tcuclient_error">#B3261E</color>  
  
 <!-- MaterialComponents attributes (needed if parent="Theme.AppCompat"). -->  
 <color name="co.andriy.tcuclient_primary_variant">#D0BCFF</color>  
 <color name="co.andriy.tcuclient_secondary_variant">#625B71</color>  
 <color name="co.andriy.tcuclient_surface">#FFFBFE</color>  
 <color name="co.andriy.tcuclient_on_primary">#FFFFFF</color>  
 <color name="co.andriy.tcuclient_on_secondary">#FFFFFF</color>  
 <color name="co.andriy.tcuclient_on_background">#1C1B1F</color>  
 <color name="co.andriy.tcuclient_on_error">#FFFFFF</color>  
 <color name="co.andriy.tcuclient_on_surface">#1C1B1F</color>  
 <color name="mtrl_scrim">#000000</color>  
  
 <!-- Material3 attributes (needed if parent="Theme.MaterialComponents"). -->  
 <color name="co.andriy.tcuclient_primary_inverse">#D0BCFF</color>  
 <color name="co.andriy.tcuclient_primary_container">#EADDFF</color>  
 <color name="co.andriy.tcuclient_on_primary_container">#21005E</color>  
 <color name="co.andriy.tcuclient_secondary_container">#E8DEF8</color>  
 <color name="co.andriy.tcuclient_on_secondary_container">#1E192B</color>  
 <color name="co.andriy.tcuclient_tertiary">#7D5260</color>  
 <color name="co.andriy.tcuclient_on_tertiary">#FFFFFF</color>  
 <color name="co.andriy.tcuclient_tertiary_container">#FFD8E4</color>  
 <color name="co.andriy.tcuclient_on_tertiary_container">#370B1E</color>  
 <color name="co.andriy.tcuclient_surface_variant">#E7E0EC</color>  
 <color name="co.andriy.tcuclient_on_surface_variant">#49454E</color>  
 <color name="co.andriy.tcuclient_inverse_surface">#313033</color>  
 <color name="co.andriy.tcuclient_inverse_on_surface">#F4EFF4</color>  
 <color name="co.andriy.tcuclient_outline">#79747E</color>  
 <color name="co.andriy.tcuclient_error_container">#F9DEDC</color>  
 <color name="co.andriy.tcuclient_on_error_container">#370B1E</color>  
</resources>  

styles.xml:

<?xml version="1.0" encoding="utf-8" ?>  
<resources>  
 <!-- Base theme applied no matter what API -->  
 <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">  
 <!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->  
 <item name="windowNoTitle">true</item>  
 <!--We will be using the toolbar so no need to show ActionBar-->  
 <item name="windowActionBar">false</item>  
 <!-- Set theme colors from https://aka.ms/material-colors -->  
 <!-- colorPrimary is used for the default action bar background -->  
 <item name="colorPrimary">#465880</item>  
 <!-- colorPrimaryDark is used for the status bar -->  
 <item name="colorPrimaryDark">#182f53</item>  
 <!-- colorAccent is used as the default value for colorControlActivated  
         which is used to tint widgets -->  
 <item name="colorAccent">#7485b0</item>  
 <!-- You can also set colorControlNormal, colorControlActivated  
         colorControlHighlight and colorSwitchThumbNormal. -->  
 <item name="windowActionModeOverlay">true</item>  
 <item name="android:actionOverflowButtonStyle">@style/OverflowStyle</item>  
 <item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>  
 </style>  
  
 <style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">  
 <item name="colorAccent">#7485b0</item>  
 </style>  
 <style name="splashscreen" parent="Theme.AppCompat.Light.NoActionBar">  
 <item name="android:windowBackground">@drawable/splash</item>  
 <item name="android:windowNoTitle">true</item>  
 </style>  
 <style name="OverflowStyle" parent="Widget.AppCompat.ActionButton.Overflow">  
 <item name="tint">#49454E</item>  
 </style>  
 <style name="MainTheme" parent="Theme.Material3.DayNight">  
  
 If you are using revision 22.1 please use just windowNoTitle. Without android:  
 <item name="windowNoTitle">true</item>  
 We will be using the toolbar so no need to show ActionBar  
 <item name="windowActionBar">false</item>  
 <item name="windowActionModeOverlay">true</item>  
 <item name="android:statusBarColor">@color/co.andriy.tcuclient_surface</item>  
  
  Original AppCompat attributes.   
 <item name="colorPrimary">@color/co.andriy.tcuclient_primary</item>  
 <item name="colorPrimaryDark">@color/co.andriy.tcuclient_primary_dark</item>  
 <item name="colorSecondary">@color/co.andriy.tcuclient_secondary</item>  
 <item name="android:colorBackground">@color/co.andriy.tcuclient_background</item>  
 <item name="colorError">@color/co.andriy.tcuclient_error</item>  
  
  MaterialComponents attributes (needed if parent="Theme.AppCompat").   
 <item name="colorPrimaryVariant">@color/co.andriy.tcuclient_primary_variant</item>  
 <item name="colorSecondaryVariant">@color/co.andriy.tcuclient_secondary_variant</item>  
 <item name="colorSurface">@color/co.andriy.tcuclient_surface</item>  
 <item name="colorOnPrimary">@color/co.andriy.tcuclient_on_primary</item>  
 <item name="colorOnSecondary">@color/co.andriy.tcuclient_on_secondary</item>  
 <item name="colorOnBackground">@color/co.andriy.tcuclient_on_background</item>  
 <item name="colorOnError">@color/co.andriy.tcuclient_on_error</item>  
 <item name="colorOnSurface">@color/co.andriy.tcuclient_on_surface</item>  
 <item name="scrimBackground">@color/mtrl_scrim</item>  
 <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>  
 <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>  
 <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>  
 <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>  
 <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>  
 <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>  
 <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>  
 <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>  
 <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>  
 <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>  
 <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>  
 <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>  
 <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>  
  
  Material3 attributes (needed if parent="Theme.MaterialComponents").   
 <item name="colorPrimaryInverse">@color/co.andriy.tcuclient_primary_inverse</item>  
 <item name="colorPrimaryContainer">@color/co.andriy.tcuclient_primary_container</item>  
 <item name="colorOnPrimaryContainer">@color/co.andriy.tcuclient_on_primary_container</item>  
 <item name="colorSecondaryContainer">@color/co.andriy.tcuclient_secondary_container</item>  
 <item name="colorOnSecondaryContainer">@color/co.andriy.tcuclient_on_secondary_container</item>  
 <item name="colorTertiary">@color/co.andriy.tcuclient_tertiary</item>  
 <item name="colorOnTertiary">@color/co.andriy.tcuclient_on_tertiary</item>  
 <item name="colorTertiaryContainer">@color/co.andriy.tcuclient_tertiary_container</item>  
 <item name="colorOnTertiaryContainer">@color/co.andriy.tcuclient_on_tertiary_container</item>  
 <item name="colorSurfaceVariant">@color/co.andriy.tcuclient_surface_variant</item>  
 <item name="colorOnSurfaceVariant">@color/co.andriy.tcuclient_on_surface_variant</item>  
 <item name="colorSurfaceInverse">@color/co.andriy.tcuclient_inverse_surface</item>  
 <item name="colorOnSurfaceInverse">@color/co.andriy.tcuclient_inverse_on_surface</item>  
 <item name="colorOutline">@color/co.andriy.tcuclient_outline</item>  
 <item name="colorErrorContainer">@color/co.andriy.tcuclient_error_container</item>  
 <item name="colorOnErrorContainer">@color/co.andriy.tcuclient_on_error_container</item>  
 <item name="textAppearanceDisplayLarge">@style/TextAppearance.Material3.DisplayLarge</item>  
 <item name="textAppearanceDisplayMedium">@style/TextAppearance.Material3.DisplayMedium</item>  
 <item name="textAppearanceDisplaySmall">@style/TextAppearance.Material3.DisplaySmall</item>  
 <item name="textAppearanceHeadlineLarge">@style/TextAppearance.Material3.HeadlineLarge</item>  
 <item name="textAppearanceHeadlineMedium">@style/TextAppearance.Material3.HeadlineMedium</item>  
 <item name="textAppearanceHeadlineSmall">@style/TextAppearance.Material3.HeadlineSmall</item>  
 <item name="textAppearanceTitleLarge">@style/TextAppearance.Material3.TitleLarge</item>  
 <item name="textAppearanceTitleMedium">@style/TextAppearance.Material3.TitleMedium</item>  
 <item name="textAppearanceTitleSmall">@style/TextAppearance.Material3.TitleSmall</item>  
 <item name="textAppearanceBodyLarge">@style/TextAppearance.Material3.BodyLarge</item>  
 <item name="textAppearanceBodyMedium">@style/TextAppearance.Material3.BodyMedium</item>  
 <item name="textAppearanceBodySmall">@style/TextAppearance.Material3.BodySmall</item>  
 <item name="textAppearanceLabelLarge">@style/TextAppearance.Material3.LabelLarge</item>  
 <item name="textAppearanceLabelMedium">@style/TextAppearance.Material3.LabelMedium</item>  
 <item name="textAppearanceLabelSmall">@style/TextAppearance.Material3.LabelSmall</item>  
 <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance.Material3.SmallComponent</item>  
 <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance.Material3.MediumComponent</item>  
 <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.Material3.LargeComponent</item>  
 </style>  
</resources>  

193667-small.png
193711-big.png

Developer technologies | .NET | Xamarin
{count} votes

1 answer

Sort by: Most helpful
  1. JessieZhang-MSFT 7,716 Reputation points Microsoft External Staff
    2022-04-27T08:22:36.3+00:00

    Hello,

    I did a test on my side, there is no need to calculate height for the dialog.

    You can try the following code in MaterialDialogRenderer:

           private void CreateControl()  
        {  
            if (Element.Content == null)  
                return;  
            //Creating container to wrap XF content  
            ContainerView container = new ContainerView(Context, Element.Content);    
    
            MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(Context);  
    
            Control = alertBuilder  
                .SetTitle(Element.Headline)  
                .SetMessage(Element.Message)  
                .SetPositiveButton(Element.Accept, OnPositiveClicked)  
                .SetNegativeButton(Element.Decline, OnNegativeClicked)  
                .SetNeutralButton(Element.Cancel, OnNeutralClicked)  
                .SetView(container)   
                .Create();  
            Control.Show();  
        }  
    

    If the dialog box has many child views, you can add a ScrollView to wrap the StackLayout in yourpage.xaml, for example:

            <dialog:MaterialDialog Headline="Dialog" VerticalOptions="Center"  
                               Accept="OK"  
                               Cancel="Cancel">  
            <ScrollView>  
            <StackLayout>  
                <Entry Placeholder="Price 1"/>  
                <Entry Placeholder="Price 2"/>  
                <Entry Placeholder="Price 3"/>  
                <Entry Placeholder="Price 4"/>  
                <Entry Placeholder="Price 5"/>  
                <Entry Placeholder="Price 6"/>  
    
                <Entry Placeholder="Price 7"/>  
                <Entry Placeholder="Price 8"/>  
                <Entry Placeholder="Price 9"/>  
            </StackLayout>  
    
            </ScrollView>  
        </dialog:MaterialDialog>  
    

    Best Regards,
    Jessie Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.