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>