Controlled Frame Width Size In A Horizontal StackLayout - c#

I am trying to create a set of stacked horizontal StackLayouts composed of Frames with differing widths. The end goal is to create a Schedule View (multiple calendars viewed at once) not unlike what is possible in Outlook (see attached image).
Desired Output: http://i.stack.imgur.com/1c0mu.png
In trying to do this I noticed that the Width of Frames added to a Horizontal StackLayout will match the WidthRequest only if all of the Frames have the same exact width. If the widths of the Frames differ then the Width and WidthRequests don't match.
Here is the XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinSample.SamplePage1">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
</ContentPage.Padding>
<ScrollView x:Name="FrozenBandDataScrollView"
Orientation="Horizontal">
<StackLayout VerticalOptions="Start"
HorizontalOptions="Start"
Orientation="Vertical"
Spacing="0"
BackgroundColor="Silver"
WidthRequest="3600"
x:Name="FrozenBandDataStackLayout">
</StackLayout>
</ScrollView>
</ContentPage>
Here is the Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XamarinSample
{
public partial class SamplePage1 : ContentPage
{
TapGestureRecognizer tapFrameRecognizer = new TapGestureRecognizer();
public SamplePage1()
{
InitializeComponent();
addDataPoints();
}
public void addDataPoints()
{
tapFrameRecognizer.Tapped += (s, e) =>
{
Frame f = s as Frame;
Label l = f.Content as Label;
l.Text = "R:" + f.WidthRequest;
l.Text += ", W: " + f.Width;
l.Text += "\nR: " + f.HeightRequest;
l.Text += ", H: " + f.Height;
};
FrozenBandDataStackLayout.Children.Add(getStackLayoutWithDimensions(70, 3600));
FrozenBandDataStackLayout.Children.Add(getStackLayoutWithDimensions(70, 3600));
FrozenBandDataStackLayout.Children.Add(getStackLayoutWithDimensions(70, 3600));
}
public static double randomNumber(int start, int end){return (new Random()).Next(start, end);}
public StackLayout getStackLayoutWithDimensions(double height, double width)
{
StackLayout sl = new StackLayout {
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Start,
Orientation = StackOrientation.Horizontal,
WidthRequest = width,
HeightRequest = height,
Spacing = 0
};
//OPTION 1: randomly calculated width (total = 3600) -- does not work :(
int minFrameWidth = 1;
int maxFrameWidth = 500;
double currentWidth = 0;
while ((currentWidth + maxFrameWidth) < width)
{
double frameWidth = randomNumber(minFrameWidth, maxFrameWidth);
currentWidth += frameWidth;
sl.Children.Add(getFrameWithDimensions(height, frameWidth));
}
sl.Children.Add(getFrameWithDimensions(height, width-currentWidth));
//OPTION 2: specific homogeneous width (total = 3600) -- works :)
/*
for (int i = 0; i < 12; i++)
{
sl.Children.Add(getFrameWithDimensions(height, 300));
}
*/
//OPTION 3: specific altering width (total = 3600) -- does not work :(
/*
for (int i = 0; i < 12; i++)
{
if (i % 2 == 0) {
sl.Children.Add(getFrameWithDimensions(height, 200));
}else
{
sl.Children.Add(getFrameWithDimensions(height, 400));
}
}
*/
return sl;
}
public Frame getFrameWithDimensions(double height, double width)
{
Frame frame = new Frame
{
BackgroundColor = Color.Gray,
OutlineColor = Color.Black,
HasShadow = false,
HorizontalOptions = LayoutOptions.Start,
WidthRequest = width,
VerticalOptions = LayoutOptions.Start,
HeightRequest = height,
Content = new Label
{
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
TextColor = Color.Black,
FontFamily = "Arial",
FontSize = 8,
Text = "test"
}
};
frame.GestureRecognizers.Add(tapFrameRecognizer);
return frame;
}
}
}
Here are the three different outputs: http://forums.xamarin.com/discussion/74391/controlled-frame-width-size-in-a-horizontal-stacklayout
Does anyone know a way to assure that the WidthRequest matches the actual Width?
Also, can anyone think of another possible Xamarin Forms solution? I've tried using a Grid but it was really slow (as it's not meant for this type of use). It would be nice to have something with data binding like a horizontal ListView.
Note: This app would need to work for iOS, Windows UWP, and possibly Android.

Related

Xamarin Forms Grid Positioning

My code creates the grid just like I want to, but my issue is the formatting of it. For example, the below syntax has the grid (using iPhone emulator) start at almost the half-way point of the screen. From best of I can tell, I am only declaring the width of the date column as I do not want it to be drug onto 2 lines if it is a 2 digit month or 2 digit year.
What would be the best way to alter this code so that the grid will span the entire screen in Up & Down format, not Horizontal.
private void LoadInvoiceGrid()
{
List<InvoiceHistory> IH = new List<InvoiceHistory>();
IH = dal.GetInvoices(company);
Grid usergrid = new Grid();
//Adding in this line makes no diff to the display of the grid
usergrid.VerticalOptions = LayoutOptions.FillAndExpand;
//
usergrid.Padding = new Thickness(10, 120, 0, 0);
usergrid.RowDefinitions.Add(new RowDefinition { Height = 50 });
foreach (var item in IH)
usergrid.RowDefinitions.Add(new RowDefinition { Height = 50 });
usergrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
usergrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Star });
usergrid.ColumnDefinitions.Add(new ColumnDefinition { Width = 50 });
Label invoicenumHeader = new Label
{
Text = "Invoice #"
};
Label invoiceamountHeader = new Label
{
Text = "Amount"
};
Label invoiceduedateHeader = new Label
{
Text = "Due Date"
};
Grid.SetColumn(invoicenumHeader, 1);
Grid.SetColumn(invoiceamountHeader, 2);
Grid.SetColumn(invoiceduedateHeader, 3);
usergrid.Children.Add(invoicenumHeader);
usergrid.Children.Add(invoiceamountHeader);
usergrid.Children.Add(invoiceduedateHeader);
int rowIndex = 1;
foreach (var item in IH)
{
string date = item.invoiceduedate.Date.ToShortDateString();
Label invoicenum = new Label
{
Text = item.invoicenum.ToString()
};
Label invoiceamount = new Label
{
Text = "$" + item.invoiceamt.ToString("0.##")
};
Label invoiceduedate = new Label
{
Text = date
};
Grid.SetRow(invoicenum, rowIndex);
Grid.SetRow(invoiceamount, rowIndex);
Grid.SetRow(invoiceduedate, rowIndex);
Grid.SetColumn(invoicenum, 1);
Grid.SetColumn(invoiceamount, 2);
Grid.SetColumn(invoiceduedate, 3);
usergrid.Children.Add(invoicenum);
usergrid.Children.Add(invoiceamount);
usergrid.Children.Add(invoiceduedate);
rowIndex++;
}
InvoiceGrid = usergrid;
}
EDIT
In my XAML I have the Vertical Option set to Fill And Expand and it is not making any difference. See my XAML for the page.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Pages.DoIt" >
<ContentPage.Content>
<StackLayout Orientation="Vertical" Padding="30" Spacing="40">
<Label TextColor="#77d065" FontSize = "8" Text="Yes, Test, Text" />
<Label TextColor="Blue" FontSize="8" Text="{Binding CompanyWelcomeMessage}" />
<ContentView VerticalOptions="FillAndExpand" Content="{Binding InvoiceGrid,Mode=TwoWay}" Padding="0,10,0,0"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>

C# WPF) DropShadowEffect is sometimes missing when dynamically adding a control

foreach (DataRow dr in dt.Rows)
{
Rectangle rectangle_timeline = new Rectangle();
rectangle_timeline.Height = 19;
rectangle_timeline.Cursor = Cursors.Hand;
rectangle_timeline.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
Grid_Timeline.Children.Add(rectangle_timeline);
}
I dynamically add a Rectangle with above simple code as shown image.
However, sometimes, randomly, there're rectangles without DropShadowEffect like yellow rectangles and 1 blue rectangle at the lowest.
As you see the code, if a rectangle is added, the code for DropShadowEffect should have to be worked.
I'm wondering why this is happening.
Thank you !
XAML code added-
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
Minimal code to re-produce is added-
private void Window_Loaded(object sender, RoutedEventArgs e)
{
int count_each_category = 0;
string current_therapeutic_category = String.Empty;
foreach (DataRow dr_test in dt.Rows)
{
if (dr_test != null)
{
if (current_category == String.Empty)
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
else
{
if (current_category == dr_test["category"].ToString())
{
// empty
}
else
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
}
Rectangle rectangle_test = new Rectangle();
rectangle_test.HorizontalAlignment = HorizontalAlignment.Left;
rectangle_test.VerticalAlignment = VerticalAlignment.Top;
rectangle_test.Margin = new Thickness(119 + (((Convert.ToDateTime(dr_test["date"]) - DateTime.Today.AddYears(-10)).TotalDays) * 0.27), count_each_category * 50, 0, 0);
rectangle_test.Width = 0.27 * Convert.ToInt32(dr_test["howlong"]);
rectangle_test.Height = 19;
rectangle_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rectangle_test.Fill = Brushes.LightGreen;
Grid_Timeline.Children.Add(rectangle_test);
}
}
}
XAML code of WPF Window
<Window x:Class="OperationSWdummy.Green_Timeline"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OperationSWdummy"
mc:Ignorable="d"
Title="Green_Timeline" Width="1165" Background="White" ResizeMode="CanMinimize" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" SnapsToDevicePixels="True" Loaded="Window_Loaded">
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
</Window>
Raw data of dt (DataTable)
date datetime
category NVARCHAR
howlong int
date category howlong
2015-01-25 HHH 60
2014-04-03 AAA 60
2015-01-25 DDD 60
2014-04-03 UUU 60
2015-01-25 CCC 60
2015-11-07 PPP 30
2015-01-25 TTT 60
2015-11-07 MMM 30
2015-02-22 MMM 30
2015-11-07 VVV 8
Result of above minimal code
Another minimal code to create rectangles randomly-
for (int k = 0; k < 191; k++)
{
Rectangle rec_test = new Rectangle();
rec_test.Margin = new Thickness(random_margin.Next(100, 1000), 29 * (k % 10), 0, 0);
rec_test.Width = random_width.Next(10, 40);
rec_test.HorizontalAlignment = HorizontalAlignment.Left;
rec_test.VerticalAlignment = VerticalAlignment.Top;
rec_test.Height = 14;
rec_test.Cursor = Cursors.Hand;
rec_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rec_test.Fill = Brushes.Green;
Grid_Timeline.Children.Add(rec_test);
}
Have you tried to use Canvas to place your rectangles?
Having Canvas to place your shapes such as rectangles, circles, or even graphical paths with random locations is better than placing on fixed layout container such as Grid or StackPanel.
It is quite known that if you just placed shapes with effects such as DropShadow on fixed layout (with arbitrary X,Y locations) such as Grid and StackPanel, your shapes may be clipped. Because the shapes edges will always be checked for pixel boundaries at render time, and often this is caused by overlapped rendering calculations when it tries to recalculate edges of fixed layout controls.
Could be a driver problem with your graphics hardware. Try disabling hardware acceleration and see if it helps.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
}
}
In general I advise against using the DropShadowEffect as it will have a significantly negative impact on rendering performance.

Collision detection Xamarin.Forms

I'm trying to detect a collision between two Xamarin. Forms Controls (BoxViews), but I can't find a way to do it. I have a button that what it does is to decrease the TranslationY of a BoxView until it collides with the other BoxView. I remember that with WinForms this could be done with IntersectsWith, but apparently here does not work, I currently have this:
public class Main : ContentPage
{
public BoxView pjOne = new BoxView { BackgroundColor = Color.Red, HeightRequest = 100, WidthRequest = 100, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.StartAndExpand };
public BoxView pjTwo = new BoxView { BackgroundColor = Color.Green, HeightRequest = 100, WidthRequest = 100, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.CenterAndExpand };
public Button btnDown = new Button { HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.End, Text = "Down", TextColor = Color.White };
public Main()
{
btnDown.Clicked += (s, e) =>
{
if(!pjOne.Bounds.IntersectsWith(pjTwo.Bounds))
{
pjOne.TranslationY -= 100; //If it does not detect collision it decreases the TranslationY
}
else
{
pjOne.TranslationY += 100; //If it detects collision it increases the TranslationY
}
};
Content = new StackLayout
{
Children =
{
pjOne,
pjTwo,
btnDown
}
};
}
}
But this doesn't work, it never detects the collision between the two BoxView.
Bounds do not get updated when a Translation is applied.
From the docs:
Bounds is assigned during the Layout cycle by a call to Layout(Rectangle).
I would try
calling Layout with the new location you want to translate the BoxView to
create a rectangle for pjOne and pjTwo. Then you could use the Xamarin.Forms.Rectangle.IntercectsWith method.
Here is an untested example of how to do this:
var pjRectOne = new Rectangle(pjOne.X + pjOne.TranslationX, pjOne.Y + pjOne.TranslationY, pjOne.Width, pjOne.Height);
var pjRectTwo = new Rectangle(pjTwo.X + pjTwo.TranslationX, pjTwo.Y + pjTwo.TranslationY, pjTwo.Width, pjTwo.Height);
if (pjRectOne.IntercectsWith(pjRectTwo))
{
}
Note: if pjOne or pjTwo are children of a view, their X and Y positions will be relative to the parent. To get the absolute X and Y, loop through all their parents by doing something like this
var y = pjOne.Y;
var parent = pjOne.ParentView;
while (parent != null)
{
y += parent.Y;
parent = parent.ParentView;
}
Hope this helped you out :)

Button on top of the Scrollview does not show up

I have something like this so far for my view:
public StackLayout OffersSlideViewCarouselChild(Offer offer)
{
Image productImage = new Image
{
Source = ImageSource.FromUri(new Uri(offer.Image.Replace("https://", "http://"))),
HeightRequest = 270,
WidthRequest = 270,
Aspect = Aspect.AspectFit
};
var topStackLayout = new StackLayout
{
Spacing = 0
};
topStackLayout.Children.Add(productImage);
StackLayout contentStackLayout = new StackLayout
{
Spacing = 0,
Padding = new Thickness(5, 10, 5, 10),
Orientation = StackOrientation.Vertical
};
var savedBtn = SavedButtonLayout(offer.IsSelected, offer.Id);
var redeemBtn = RedeemBtnLayout(offer.Id);
var timeRemainingLabel = TimeRemainingLayout(offer, offer.Id);
contentStackLayout.Children.Add(new UILabel(16) {
Text = offer.ProductName,
TextColor = ColorHelper.FromHex(CoreTheme.COLOR_OFFERCELL_PRODUCT_TEXT),
FontFamily = CoreTheme.FONT_FAMILY_DEFAULT_BOLD,
WidthRequest = DeviceDisplaySettings.defaultwidth,
VerticalTextAlignment = TextAlignment.Center
});
contentStackLayout.Children.Add(new UILabel(14)
{
Text = offer.Headline,
TextColor = ColorHelper.FromHex(CoreTheme.COLOR_OFFERCELL_PRODUCT_TEXT),
FontFamily = CoreTheme.FONT_FAMILY_DEFAULT_BOLD,
WidthRequest = DeviceDisplaySettings.defaultwidth,
VerticalTextAlignment = TextAlignment.Center
});
contentStackLayout.Children.Add(new UILabel(14) {
Text = offer.LongRewardsMessage,
TextColor = ColorHelper.FromHex(CoreTheme.COLOR_DEAL_PAGE_LONG_REWARD_MESSAGE_RED),
FontFamily = CoreTheme.FONT_FAMILY_DEFAULT_BOLD,
WidthRequest = DeviceDisplaySettings.defaultwidth,
VerticalTextAlignment = TextAlignment.Center
});
if (!string.IsNullOrEmpty(offer.PowerMessage)) {
var htmlText = string.Format("<html><body style='color:#9b9b9b'>{0}</body></html>", offer.PowerMessage.Replace(#"\", string.Empty));
var browser = new WebView() {
//HeightRequest = (DeviceDisplaySettings.defaultheight > 600) ? 500 : 400,
HeightRequest = 800,
Source = new HtmlWebViewSource() { Html = htmlText },
};
browser.Navigating += OnNavigating;
contentStackLayout.Children.Add(browser);
}
var nestedStackLayout = new StackLayout()
{
VerticalOptions = LayoutOptions.FillAndExpand
};
nestedStackLayout.Children.Add(topStackLayout);
nestedStackLayout.Children.Add(timeRemainingLabel);
nestedStackLayout.Children.Add(contentStackLayout);
var mainScrollView = new ScrollView()
{
Padding = new Thickness(0, 0, 0, 10),
VerticalOptions = LayoutOptions.FillAndExpand,
Orientation = ScrollOrientation.Vertical,
Content = nestedStackLayout
};
var mainStackLayout = new StackLayout()
{
Spacing = 5,
Padding = new Thickness(0, 0, 0, 0),
VerticalOptions = LayoutOptions.Fill,
HorizontalOptions = LayoutOptions.Fill,
Orientation = StackOrientation.Vertical,
Children = { savedBtn, mainScrollView, redeemBtn }
};
return mainStackLayout;
}
private StackLayout SavedButtonLayout(bool isSelected, int offerid)
{
int buttonsToShow = 2;
bool displaySaveButton = true;
if (IsPremisesOffer (offerid)) {
buttonsToShow = 3;
displaySaveButton = false;
}
btnShare = new UIFieldDefinition(_pageFieldDefinition.ShareButtonDefinition);
btnShare.Text = "SHARE";
btnShare.ClassId = offerid.ToString();
btnShare.WidthRequest = (DeviceDisplaySettings.defaultwidth / buttonsToShow) - 40;
btnShare.BackgroundColor = Color.FromRgb(167, 188, 33);
btnShare.VerticalContentAlignment = TextAlignment.Center;
btnShare.HandleClick(btnShare_Clicked);
btnSave = new UIFieldDefinition(_pageFieldDefinition.SaveButtonDefinition);
btnSave.Text = isSelected ? "UNSAVE" : "SAVE";
btnSave.ClassId = offerid.ToString();
btnSave.WidthRequest = (DeviceDisplaySettings.defaultwidth / buttonsToShow) - 40;
btnSave.BackgroundColor = Color.FromRgb(167, 188, 33);
btnSave.VerticalContentAlignment = TextAlignment.Center;
btnSave.HandleClick(btnSave_Clicked);
rl = new StackLayout {
Spacing = 10,
Orientation = StackOrientation.Horizontal,
BackgroundColor = Color.FromRgb(196, 221, 57),
Padding = new Thickness(40, 5, 5, 5),
WidthRequest = DeviceDisplaySettings.defaultwidth
};
rl.Children.Add(btnShare);
if (displaySaveButton) rl.Children.Add(btnSave);
return rl;
}
public UIFieldDefinition RedeemBtnLayout(int offerid)
{
int buttonsToShow = 1;
btnRedeem = new UIFieldDefinition(_pageFieldDefinition.RedeemButtonDefinition);
btnRedeem.Text = "REDEEM NOW";
btnRedeem.ClassId = offerid.ToString();
btnRedeem.WidthRequest = (DeviceDisplaySettings.defaultwidth / buttonsToShow) - 10;
// btnRedeem.HorizontalOptions = LayoutOptions.FillAndExpand;
// btnRedeem.VerticalOptions = LayoutOptions.EndAndExpand;
btnRedeem.HandleClick(btnRedeem_Clicked);
return btnRedeem;
}
However, I am noticing that the Redeem button does not even display on the view (It's supposed to be fixed on the bottom).
The scrollview works but the buttom is missing. Why?
Please let me know if you need further code details.
Moving here from comments above. There are two separate issues from what I can tell, and as far as I can tell, are unrelated:
The WebView, nested inside the ScrollView, is not big enough to fully display the content.
The button that is supposed to be at the bottom of the screen is not displaying.
For both of them, the answer is probably in how you are setting HeightRequest. There have been a lot of suggestions by myself and other commenters to change or get rid of some of the HeightRequest settings, and I'm not sure of the current state of your source code. So assuming those are still there:
For solving the WebView issue, read How can I add HTML to a Stacklayout inside a Scrollview in Xamarin forms?. This will let you figure out the right HeightRequest to use. The short answer is that depending on exactly what you want to happen, you may need a custom renderer. Note that the HeightRequest for the WebView will not affect any layout outside of the ScrollView.
For solving the issue of the button not appearing, get rid of the HeightRequest setting on the ScrollView, and the VerticalOptions on the StackLayout created in SavedButtonLayout.
I am assuming you did the experiment suggested above to make sure that the redeemBtn will render if placed before the ScrollView, and it does show up then. If not, you first need to fix that.
If you have "fixed" this by changing the HeightRequest then your real problem is the fixed pixel size of all your views and layouts, I recommend you DON'T use fixed pixel sizes for different screen resolution this will be a bigger problem later, What you can do is get the Screen size and do the math to fit all your elements of the view, one way to get the width and height of the screen is on the OnSizeChanged event of Pages (Like ContentPage), something like this:
SizeChanged += SizeChanged;
void SizeChanged (object sender, EventArgs e)
{
Layout.WidthRequest = Width * 0.3;
Layout.HeightRequest = Height * 0.35;
}
Your layout is pretty busy. A few things:
Set VerticalOptions to EndAndExpand for redeemBtn.
Set VerticalOptions to StartAndExpand for savedBtn.
Set VerticalOptions to Fill for mainScrollView.
Set VerticalOptions to FillAndExpand for mainRelLayout.
Set VerticalOptions and HorizontalOptions to Fill for
mainStackLayout.
I think that will get you to where you want to be.
The options that include "Expand" will grow the element to accommodate the desired height of its contents.

XamarinForms RelativeLayout: Can't get relative layout to resize correctly in a row

Here is my issue:
The red block is meant to be the avatar for the person sometime, and the blue balloon a chat message. The chat message object is a RelativeLayout with a Label and an Image positioned one of top of each other, but not matter what I do, I can't get it to be centered. I only have one View:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace TestChat
{
public partial class ChatPage : ContentPage
{
public ChatPage ()
{
this.Title = "Chat page";
InitializeComponent ();
}
void OnChatClick (object sender, EventArgs args) {
Image pic = new Image {
Source = "bubble.png",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Aspect = Aspect.Fill
};
Label textLabel = new Label {
Text = "Hello",
TextColor = Color.White,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.EndAndExpand
};
Frame picFrame = new Frame {
HasShadow = false,
BackgroundColor = Color.Red,
Padding = new Thickness (0),
Content = pic
};
Frame textFrame = new Frame {
HasShadow = false,
BackgroundColor = Color.Transparent,
Padding = new Thickness (0,0,15,0),
Content = textLabel
};
RelativeLayout overlayLayout = new RelativeLayout { BackgroundColor = Color.Blue, HorizontalOptions = LayoutOptions.FillAndExpand, VerticalOptions = LayoutOptions.FillAndExpand };
overlayLayout.Children.Add (picFrame,
xConstraint: Constraint.RelativeToParent((parent) => parent.X),
yConstraint: Constraint.RelativeToParent((parent) => parent.Y),
widthConstraint: Constraint.RelativeToParent((parent) => parent.Width-2),
heightConstraint: Constraint.RelativeToParent((parent) => parent.Height-2)
);
overlayLayout.Children.Add (textFrame,
xConstraint: Constraint.RelativeToParent((parent) => parent.X),
yConstraint: Constraint.RelativeToParent((parent) => parent.Y),
widthConstraint: Constraint.RelativeToParent((parent) => parent.Width-2),
heightConstraint: Constraint.RelativeToParent((parent) => parent.Height-2)
);
Frame overlayContainerFrame = new Frame {
HasShadow = false,
BackgroundColor = Color.Red,
Padding = new Thickness(1),
HeightRequest = 100,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Content = overlayLayout
};
StackLayout horizontalLayout = new StackLayout {
Orientation = StackOrientation.Horizontal
};
BoxView avatarImage = new BoxView {
Color = Color.Red,
HeightRequest = 50,
WidthRequest = 50
};
horizontalLayout.Children.Add (avatarImage);
horizontalLayout.Children.Add (overlayContainerFrame);
ChatScrollViewStackLayout.Children.Add (horizontalLayout);
//ChatStackLayout.Children.Add (pic);
}
void CreateChatBubble() {
}
}
}
Does anyone have any ideas why I can't get the relative layout to resize accordingly so it doesn't go out of range of the screen? I tried setting its WidthConstraint to parent.With-52 to make up for the avatar taking up 50 units horizontally, but instead I get this:
I've been stuck at this for at least 8 hours now, and I'm pretty much out of ideas. Any tips would be greatly appreciated. Here is the project's git repo so you can clone it if you would like to test anything:
https://github.com/sgarcia-dev/xamarin-chat.git
Any help would be greatly appreciated, and feel free to completely ignore my code if it looks messy if you can replicate what I want. (One image on the left, and a message bubble on the right with an underlying image background)
Check out this implementation
void OnChatClick (object sender, EventArgs args) {
var pic = new Image {
Source = "bubble.png",
Aspect = Aspect.Fill
};
var textLabel = new Label {
Text = "Hello",
TextColor = Color.White,
VerticalOptions = LayoutOptions.Center,
LineBreakMode = LineBreakMode.WordWrap
};
var relativeLayout = new RelativeLayout {
BackgroundColor = Color.Navy,
// HeightRequest = 1000
};
var absoluteLayout = new AbsoluteLayout {
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Color.Blue
};
var frame = new Frame {
BackgroundColor = Color.Red
};
absoluteLayout.Children.Add (pic,
new Rectangle (0, 0, 1, 1),
AbsoluteLayoutFlags.All);
absoluteLayout.Children.Add (textLabel,
new Rectangle (0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize),
AbsoluteLayoutFlags.PositionProportional);
// textLabel.SizeChanged += (object label, EventArgs e) => {
// relativeLayout.HeightRequest = textLabel.Height + 30;
// absoluteLayout.HeightRequest = textLabel.Height + 30;
// };
relativeLayout.Children.Add (frame,
heightConstraint: Constraint.RelativeToParent (parent => parent.Height),
widthConstraint: Constraint.RelativeToParent (parent => parent.Width * 0.3));
relativeLayout.Children.Add (absoluteLayout,
xConstraint: Constraint.RelativeToParent (parent => parent.Width * 0.3),
widthConstraint: Constraint.RelativeToParent (parent => parent.Width * 0.7));
ChatScrollViewStackLayout.Children.Add (relativeLayout);
}
If you need to auto-adjust height of the chat message for long text uncomment all five commented lines.

Categories

Resources