question

IgorKravchenko-7896 avatar image
0 Votes"
IgorKravchenko-7896 asked JessieZhang-2116 edited

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

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



dotnet-xamarin
small.png (60.9 KiB)
big.png (52.8 KiB)
· 7
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Removed dotnet-android tag as the tag is deprecated as part of merging all Xamarin tags into one dotnet-xamarin tag

0 Votes 0 ·

What are the MaterialDialog and MaterialEntry? What nuget do you use in your project? If it is convinient for you, could you please post a basic demo to github or onedriver so that we can test on our side?

0 Votes 0 ·

MaterialDialog is a View with bindable properties. It has a renderer where I try to use AlertDialog to show this dialog. Also to implement material styles I use Xamarin.Google.Android.Material from Nuget.
193746-materialdialog.txt
193842-materialdialogrenderer.txt
193747-colors.txt
193852-styles.txt

MaterialEntry is Entry with additional properties to implement material design with using custom renderer inherited from Xamarin.Forms.Material.Android.MaterialEntryRenderer (see attached files). But a result is same when I use default controls, Entry or Label for example.
For labels the result is
193777-screenshot-1650272009.png

MaterialEntry and it's renderer:
193825-materialentry.txt
193826-extendedmaterialentryrenderer.txt


0 Votes 0 ·
materialdialog.txt (2.3 KiB)
colors.txt (2.3 KiB)
styles.txt (7.6 KiB)
JessieZhang-2116 avatar image JessieZhang-2116 IgorKravchenko-7896 ·

Sorry,we couldn't open the links of MaterialEntry and it's renderer.

0 Votes 0 ·
Show more comments

1 Answer

JessieZhang-2116 avatar image
0 Votes"
JessieZhang-2116 answered JessieZhang-2116 edited

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.




5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.