Setting a maximum height to a control, or getting ScrollView to respect other's MinimumHeightRequest - xamarin.forms

There is MinimumHeightRequest, but no MaximumHeightRequest for Views in Xamarin Forms. In its absence I'm trying to achieve the following:
I have two controls on my screen: a Signature and some legal-text. I would like the signature to take up much screen as possible, and for the legal-text to take the minimum space for itself but only up to half the parent height - and then to scroll if the text doesn't fit. I've tried StackLayout and Grid but neither quite do what I want in coping with all situations. If it existed simply setting a MaximumHeightRequest on the ScrollingLegal View would sort it out. But it doesn't. Here's what I've tried...
Controls defined as:
var Pad = new SignaturePadView() {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
MinimumHeightRequest = Application.Current.MainPage.Height / 2,
};
// Put legal text in a scroll view in case it doesn't fit on the screen.
var ScrollingLegal = new ScrollView { VerticalOptions = LayoutOptions.End, Content = new Label { Text = caption } };
Arranged with StackLayout like this it looks great on an iPad - text at the bottom of the screen signature taking up all the other space. On a small iPhone though, the text takes up all the screen and the signature appears to ignore the MinimumHeightRequest (this might be a bug I suppose?).
Stack.Children.Add(Pad);
Stack.Children.Add(ScrollingLegal);
I can add a HeightRequest to the ScrollView but then it always takes up X height. With a small amount of text I don't want that.
I have also tried a Grid:
var GridLayout = new Grid
{
Padding = 1,
RowDefinitions = {
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) },
},
ColumnDefinitions = {
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
},
};
GridLayout.Children.Add(Pad, 0, 0);
GridLayout.Children.Add(ScrollingLegal, 0, 1);
But that too always gives too much space to the text.
Is there a way of setting a Max Height?

Related

Xamarin Forms Collection View Extra Padding

I"m trying out the Xamarin Forms Collection View. It appears to have some extra padding around each item in the list. I can't see how to remove it.
Here is a picture of my view. The view has an aqua background. The 2 column grid in the template has labels set to red.
And here is the XAML/C#
<CollectionView
x:Name="BodyView"
SelectionMode="Single"
HorizontalOptions="FillAndExpand"
BackgroundColor="Aqua"
Grid.Row="2" />
BodyView.ItemTemplate = new DataTemplate(() =>
{
var converter = (IValueConverter) Application.Current.Resources["GridCellConverter"];
Grid grid = new Grid( );
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
foreach (var col in _gridColumns)
{
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var content = new Label();
content.Style = (Style)Application.Current.Resources["GridCellLabel"];
content.BackgroundColor = Color.Red;
Binding binding = new Binding("CellData", BindingMode.OneWay, converter, col.PageFieldId);
content.SetBinding(Label.TextProperty, binding);
grid.Children.Add(content, grid.ColumnDefinitions.Count - 1, 0);
}
return grid;
});
I zeroed everything I could find to zero.
I tried setting a negative margin on the grid but it won't move left.
Is there any way to remove the padding?
What you are looking for is the Grid's Row and Column Spacing!
Grid grid = new Grid( ){ ColumnSpacing= "0", RowSpacing="0" };
Also, this needs to be removed:
grid.Margin = new Thickness(-20, 0, 0, 0);
A quick question though are you using it in UWP?
Also since you are using CollectionView why not use a single item in DataTemplate that goes into a GridItemsLayout?
More information here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#vertical-grid

xamarin forms: button text wrap

I tried to text wrap in a button inside a grid, but only the first line appears, the code is:
grid.Children.Add(new Button {
Text = la[0].Floor,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
BorderWidth = 1,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand,
} , column, row );
You probably need to change your Grid.RowDefinitions's Height to Auto for what ever row that button is being placed on. Using Auto instead of GridUnitType.Star should give the Button as much height as it needs to show it's content.
That being said, if the text is super long, it still might not work.
If that button is on the first row, then your would change that row's Height to:
Grid grid = new Grid {
RowDefinitions = new RowDefinitions {
new RowDefinition { Height = GridLength.Auto }
}
}
If that does not help, please show your Grid's initialization code.

How do I scroll an Editor that has been obscured by the keyboard into view?

I'm designing a UI in Xamarin.Forms to collect feedback from users about our application. There is an Editor control at the bottom of this page. On an iPhone 4S, and in many landscape orientations, the keyboard completely obscures this editor control. On Android, this is not a big deal because the OS automatically scrolls (though the sizing behavior is a little weird.) On iOS, the only things resembling solutions are very wonky.
In native iOS, the solution is simple: wrap your views in a UIScrollView, then when the keyboard appears add that much space to the content size and scroll appropriately. Xamarin doesn't expose anything to control the scroll position in ScrollView, and ContentSize is private, so that's out. A few posts (here and here) seem to indicate ScrollView is at least part of the solution. It does appear Xamarin has some automatic scrolling behavior, but it's... curious.
My layout is fairly simple:
At the top, a fixed navigation bar that I do not want to scroll out of view.
Beneath that, a 180px tall image that represents a screenshot of the application.
Beneath that, a label with information such as the timestamp. (2-3 lines of text).
Beneath that, the editor, filling the remaining available space.
I've included code for a layout I've tried at the bottom of my post. I created a StackLayout that contains the image, the label, and the editor. I put that inside a ScrollView. Then, I create a RelativeLayout and place the navigation bar at the top-left with the ScrollView beneath it.
What I want to happen when the Editor is tapped is for the keyboard to be displayed and, if it obscures the Editor, for the layout to be nudged upwards to make the Editor visible. What happens instead is it seems like Xamarin scrolls the layout upwards by the keyboard height plus some margin that looks suspiciously like the keyboard utility bar height. This shoves the Editor upwards so high it's obscured by the navigation bar.
I've tried a lot of different tweaks and I'm at a loss. I can't control enough of the ScrollView to get the behavior I need. I've seen suggestions that use a BoxView resized when the Editor gains focus, but to make it work really well I'd still have to hook into the iOS notifications to get the appropriate size and have a fairly intimate knowledge of where my Editor's bounds are. It feels wrong.
Does anyone else have a solution to this on Xamarin.Forms? Even if I have to dip into native, I'd like an answer.
(Here's an example layout that demonstrates the problem, there's a little bit of weird structure because I was debugging. The funky colors are also a relic of layout debugging.)
using System;
using Xamarin.Forms;
namespace TestScroll
{
public class MainPage : ContentPage {
public MainPage() {
InitializeComponent();
}
private ScrollView _scroller;
protected void InitializeComponent() {
var mainLayout = new RelativeLayout();
var navbar = new Label() {
BackgroundColor = Color.Blue,
TextColor = Color.White,
Text = "I am the Nav Bar",
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.StartAndExpand
};
var subLayout = new ScrollView() {
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand
};
_scroller = subLayout;
var subStack = new StackLayout();
subStack.Spacing = 0;
subLayout.Content = subStack;
var image = new BoxView() {
Color = Color.Green,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.Fill,
HeightRequest = 300
};
subStack.Children.Add(image);
var infoLabel = new Label() {
BackgroundColor = Color.Blue,
TextColor = Color.Black,
Text = "Timestamp!\r\nOther stuff!",
VerticalOptions = LayoutOptions.Start
};
subStack.Children.Add(infoLabel);
var editor = new Editor() {
VerticalOptions = LayoutOptions.FillAndExpand
};
subStack.Children.Add(editor);
mainLayout.Children.Add(navbar,
Constraint.Constant(0),
Constraint.Constant(20),
Constraint.RelativeToParent((parent) => parent.Width),
Constraint.Constant(70));
mainLayout.Children.Add(subLayout,
Constraint.Constant(0),
Constraint.RelativeToView(navbar, (parent, view) => navbar.Bounds.Bottom),
Constraint.RelativeToParent((parent) => parent.Width),
Constraint.RelativeToView(navbar, TestConstraint));
Content = mainLayout;
}
private double TestConstraint(RelativeLayout parent, View view) {
double result = parent.Height - view.Bounds.Height;
Console.WriteLine ("Lower stack height : {0}", result);
Console.WriteLine ("Scroll content size: {0}", _scroller.ContentSize);
return result;
}
}
}
One thing I notice is that you are adding a ScrollView (subLayout) to another ScrollView (_scroller).
Also, I ran into this same problem on iOS except all of my controls were within a Grid. Simply putting the Grid into a single ScrollView fixed the problem, without having to change content sizes or anything like that.
This question sat for a long time unanswered, here's what I did. I don't know that it's the 'answer', and I do appreciate hvaughan3's answer that's currently here and I will try it if I ever get the time.
My page behaved like I wanted on Android, so I didn't do anything specific for that.
So I wrote specific code for iOS that used the notifications UIKeyboardWillShow and UIKeyboardWillHide. These notifications provide information about the bounds the keyboard will take up. So when I get a 'show' notification, I manipulate my layout to allow room for an element of that size I place underneath the keyboard. When I get a 'hide' notification, I reset the layout.
It's janky and a little embarrassing, I hope to come back with news I tried another solution like hvaughan3's and it worked.

Silverlight 3 Button - Text + Image Alignment Problems

I'm trying to have a button contain both an image text. Ideally have the text left aligned, while the image is as far right as possible.
So i create a Grid, and add an Image and Textblock to it and set its alignment. For the life of me i cant get alignment to act as expected.
var gridPanel = new Grid();
gridPanel.ColumnDefinitions.Add(new ColumnDefinition());
var text = new TextBlock { Text = header, TextAlignment = TextAlignment.Left };
text.HorizontalAlignment = HorizontalAlignment.Left;
text.Margin = new Thickness(0);
text.SetValue(Grid.RowProperty, 0);
text.SetValue(Grid.ColumnProperty, 0);
var image = new Image();
image.Source = new BitmapImage(new Uri("../Images/Common/RedFlag.png", UriKind.Relative));
image.HorizontalAlignment = HorizontalAlignment.Right;
image.Height = 25;
image.Width = 25;
image.SetValue(Grid.RowProperty, 0);
image.SetValue(Grid.ColumnProperty, 1);
gridPanel.Children.Add(text);
gridPanel.Children.Add(image);
button.Content = gridPanel;
Both the Image and Text are center aligned for some reason...Is Grid the wrong way to go? I tried StackPanel and setting its orientation to Horizontal but it was the same thing..
by default the HorizontalContentAlignment of a button is set at "Center"... Just set it to "Stretch" like that :
button.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
Change the HorizontalContentAlignment from Centre to Stretch, like-
<Button Name="button" HorizontalContentAlignment="Stretch"/>
Edit-
I was originally doing this in the button style as I didn't know about the HorizontalContentAlignment property

Resizing Image from a decoded ByteArray

I am trying to display a bytearray as a resized image. The Image is displaying correctly, but the sizing is off. Let me explain.
First I have the image data encoded so I need to decode the image data
// Instantiate decoder
var decoder:Base64Decoder = new Base64Decoder();
// Decode image data
decoded.decode(picture.data);
// Export data as a byteArray
var byteArray:ByteArray = decoder.toByteArray();
// Display image
var img:Image = new Image();
img.load(byteArray);
This works. The image is displayed correctly. However, if I hardcode the image (img) height the resized image is shown correctly, but within a box with the original image's dimensions.
For example, if the original image has a height of 300px and a width of 200px and the img.height property is set to 75; the resized image with height of 75 is shown correctly. But the resized image is shown in the upper left corner of the img container that is still set to a height of 300px and a width of 200px. Why does it do that? And what is the fix?
The best way to illustrate the problem is by placing the image inside a VBox and show the borders of the VBox. From the code block above, if I change the image height and set the image to maintain aspect ratio (which by default is set to true but I add it here for completeness). the problem becomes clear.
// Display image
var img:Image = new Image();
img.height = 75; // Hardcode image height (thumbnail)
img.maintainAspectRatio = true;
img.load(byteArray);
// Encapsulate the image inside a VBox to illustrate the problem
var vb:VBox = new VBox();
vb.setStyle('borderStyle', 'solid');
vb.setStyle('borderColor', 'red');
vb.setStyle('borderThickness', 2);
vb.addChild(img);
I have been working on this problem for days and cannot come up with a solution. Any ideas? What am I missing?
The workaround I used is as follows:
I created an event listener for the img display object. Then after the img has loaded, I manually set the height and width of the image. I know what I want the height (preHeight) to be so that is hardcoded. I then calculate the width and set that as the image width. For some reason I had to use the explicitHeight and explicitWidth properties to finally get the sizing right.
I hope this helps someone.
img.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
private function onCreationComplete(event:FlexEvent) : void
{
img.addEventListener(Event.COMPLETE, onImageLoadComplete);
}
private function onImageLoadComplete(event:Event) : void
{
var image:Image = event.currentTarget as Image;
var preHeight:Number = 0;
var h:uint = Bitmap(image.content).bitmapData.height;
var w:uint = Bitmap(image.content).bitmapData.width;
// Check height
preHeight = h > 170 ? 170 : h;
// Set the width
img.explicitWidth = (preHeight * w)/h;
img.explicitHeight = preHeight;
}

Resources