Binding a TextBox's Width to its parent container's ActualWidth - windows-phone-7

I'm loading a Textbox and a Button into a horizontal StackPanel programmatically. The size of the button (which only contains an Image) is fixed, but I can't get the textbox to fill the available width of its parent. This is what the code looks like:
StackPanel parent = new StackPanel() {
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Top,
};
TextBox textbox = new TextBox() {
HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Top,
//MinWidth = 375,
};
Button btn = new Button() {
Content = new Image() {
MaxHeight = 40,
MaxWidth = 40,
MinHeight = 40,
MinWidth = 40,
Margin = new Thickness( 0 ),
Source = new BitmapImage(
new Uri( "btnimage.png", UriKind.Relative ) ),
},
HorizontalAlignment = HorizontalAlignment.Right,
BorderBrush = new SolidColorBrush( Colors.Transparent ),
Margin = new Thickness( 0 ),
};
btn.Click += ( ( s, e ) => OnBtnClicked( s, e, textbox ) );
parent.Children.Add( textbox );
parent.Children.Add( btn );
If I uncomment the MinWidth setting for the textbox it is displayed as I want it to, but I'd like to not have to specify a width explicitly. I tried adding a binding as follows but that doesn't work at all (the textbox just disappears!)
Binding widthBinding = new Binding() {
Source = parent.ActualWidth,
};
passwdBox.SetBinding( TextBox.WidthProperty, widthBinding );
Thanks for your help in advance!

Instead of using StackPanel (which always tries to keep elements as small as it can such that they all fit), use a Grid. Then you could do something like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <!-- Note "*" means to use the rest of the space -->
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"/>
<Button Grid.Column="1">
<Button.Content>
<Image .../>
</Button.Content>
</Button>
</Grid>
You can convert this to code instead of XAML if you prefer, XAML's just easier to type here on-the-fly.

The right answer is don't do it. See my answer at this question and the same idea applies to Silverlight on Windows Phone.
In your example, you should be using a DockPanel.
<toolkit:DockPanel>
<Button toolkit:DockPanel.Dock="Right" />
<TextBox /> <!-- fill is implied -->
</toolkit:DockPanel>

Related

How to bind in Codebehind from a global variable in App class in Xamarin

I'm trying to create an NavigationPage TitleView with traditional Cart Icon saying (3) Items etc.
So the global CartCount variable is Declared in App class.
My CommonToolbarPage class Lays out the top of all 50 pages with Logo, search button and Cart icon like every shop on the internet. I'm using Prism.
What I'm after is what goes here to bind to CartCount so BadgeText updates when the CartCount updates.
BadgeText = App.CartCount , // Jiberish
Cut down class
public class CommonToolbarPage : ContentPage
{
public CommonToolbarPage()
{
//NavigationPage.SetHasBackButton(this, false);
ShowDefaultTitle();
//this.BindingContext= new CommonToolbarPageViewModel(navigationService);
}
private void ShowDefaultTitle()
{
SfButton sfButton = new SfButton
{
CornerRadius = 4,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center,
Style = (Style)Application.Current.Resources["IconButtonStyle"],
Text = (String)Application.Current.Resources["Cart"],
FontFamily = "UIFontIcons",
TextColor = Color.White,
// Command = new Command(GoCart)
};
var Endlayout = new StackLayout
{
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.EndAndExpand,
};
var imSfBadgeView = new SfBadgeView
{
BadgeText = App.Cart, // Jiberish
Margin = new Thickness(0, 0, 10, 0),
Padding = new Thickness(0),
WidthRequest = 40,
// Content = sfButton,
HorizontalOptions = LayoutOptions.EndAndExpand,
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Color.Transparent,
BadgeSettings = new BadgeSetting
{
BackgroundColor = (Color)Application.Current.Resources["PrimaryColor"],
BadgeType = BadgeType.None,
FontSize = 10,
Stroke = (Color)Application.Current.Resources["Gray-White"],
StrokeWidth = 1,
Offset = new Point(-10, 10)
}
};
Endlayout.Children.Add(imSfBadgeView);
NavigationPage.SetTitleView(this, Endlayout);
}
}
Each page then looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<local1:CommonToolbarPage xmlns:local1="clr-namespace:blaAppV1.Views.Templates"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:listview="clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
xmlns:local="clr-namespace:blaAppV1.Views;assembly=blaAppV1"
x:Class="blaAppV1.Views.Home">
<StackLayout VerticalOptions="StartAndExpand" HorizontalOptions="Center">
<Label Text="Home" VerticalOptions="Center" HorizontalOptions="Center" />
<Button Text="Appliances" Command="{Binding Path=NavigateCommand}" CommandParameter="Appliances" />
</StackLayout>
</local1:CommonToolbarPage>
you use SetBinding to create a binding in code
myLabel.SetBinding(Label.TextProperty, "CartCount");
myLabel.BindingContext = App;

xamarin rounded image that scales

I have a Xaml page with a grid with relative heights
<RowDefinition Height="1*">
<RowDefinition Height="2*">
<RowDefinition Height="3*">
Now on the middle row (And i don't know it's exact height since it scales with the display size) I want to have a circular image. Since I have not set a heightrequest / widthrequest for the image I think I need to bind it to actual height.
I tried a lot of things resulting in my 'latest effort which is the following code but still does not give the desired result
<!-- try 1 -->
<yummy:PancakeView BackgroundColor="Aqua" CornerRadius="{Binding Source={RelativeSource Self}, Path=ActualHeight, Converter={converters:PercentageConverter}, ConverterParameter='0,5'}" IsClippedToBounds="True" BorderColor="Black" BorderThickness="4">
<Image Source="{Binding NarrationImage}" ></Image>
</yummy:PancakeView>
<!-- try 2 -->
<Grid x:Name="RefGrid" WidthRequest="1"></Grid>
<Frame
HeightRequest="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
WidthRequest="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
CornerRadius="{Binding Path=ActualHeight, Source={x:Reference RefGrid}}"
IsClippedToBounds="True" Padding="0" VerticalOptions="CenterAndExpand">
<Image Source="{Binding NarrationImage}" Aspect="AspectFill"></Image>
</Frame>
Since you had set the Height of Row as * . The real size of Frame in runtime depend on the size of Grid . In your case , the Height of Frame equals 1/3 of the Grid and the Width equals the width of the Grid .
If you want to get the value of them .You could create a custom Frame .And rewrite the method OnSizeAllocated
using Xamarin.Forms;
namespace App10
{
public class MyFrame:Frame
{
protected override void OnSizeAllocated(double width, double height)
{
if(width>0&&height>0)
{
var size =width<height ? width: height ;
CornerRadius = (float)size / 2;
}
base.OnSizeAllocated(width, height);
}
public MyFrame()
{
SizeChanged += MyFrame_SizeChanged;
}
private void MyFrame_SizeChanged(object sender, EventArgs e)
{
var width = this.Width;
var height = this.Height;
if (width > 0 && height > 0)
{
var size = width < height ? width : height;
CornerRadius = (float)size / 2;
}
}
}
}
This method will been invoked multi times when it been first added to the parent view. The last time it will return the current size . You can do something you want .

Xamarin.Forms Sizing elements in a WrapLayout

Current:
Goal:
Attempting to implement a WrapLayout to allow for dynamic button addition in a clean horizontally adding format, as shown in the 'Goal' picture. However as seen in 'Current', the sizing of the buttons in the WrapLayout are far from ideal.
Have found through simple trial and error that using height and width requests in any of the elements (scrollView, wrapLayout, buttons) result in no change to the button formats.
HeightRequest = xx;
WidthRequest = xx;
The only way, I've found thus far, to change the sizing of the wrapLayout elements is to add a large amount of children, example:
As displayed, my understanding of how to format WrapLayout children is rather lacking. So, how to format the number of children allowed on each row and how to properly format the children of a WrapLayout?
Current implementation developed following the WrapLayout class shown in the Xamarin Developer Sample for WrapLayout
ScrollView scrollView = new ScrollView {
Margin = new Thickness(20, 20, 20, 20),
};
WrapLayout wrapLayout;
wrapLayout = new WrapLayout {
ColumnSpacing = 12,
};
scrollView.Content = wrapLayout;
wrapLayout.Children.Add(
new Button
{
Text = "9 ° (?)",
BackgroundColor = Color.Yellow,
BorderColor = Color.Black,
}
);
wrapLayout.Children.Add(
new Button
{
Text = "10.5 ° (?)",
BackgroundColor = Color.Gray,
BorderColor = Color.Black,
}
);
You should look at CollectionView Nuget Package
You can use the GridCollectionView, which is based on the WrapLayout.
There, you can either use:
UniformGrid
The number of columns arranged in each row is specified. Each column width becomes the width obtained by dividing the row width by that value. This number of columns can be set by PortraitColumns and LandscapeColumns properties.
"
AutoSpacingGrid
Once a column width is specified, each column is arranged until fitting in each row and adjusted automatically each spacing. A column width can be set by ColumnWidth property and Setting SpacingType property can change how to adjust the spacing.
An Example:
<ai:GridCollectionView
ItemsSource="{Binding ItemsSource}" TouchFeedbackColor="Yellow"
ColumnWidth="100" ColumnHeight="1.0" >
<ListView.ItemTemplate>
<DataTemplate>
<ai:ContentCell>
<Label Text="{Binding Name}" />
</ai:ContentCell>
</DataTemplate>
</ListView.ItemTemplate>
</ai:GridCollectionView>
I recommend using the AiForms.Layout package:
https://dev.to/muak_x/wraplayout-and-repeatablestacklayout-for-xamarinforms-1dck
here is the sample, and it works as we expected:
<aiforms:WrapLayout Spacing="4" UniformColumns="3" IsSquare="true" HorizontalOptions="FillAndExpand">
<BoxView Color="Red" />
<BoxView Color="Blue" />
<BoxView Color="Green" />
<BoxView Color="Black" />
<BoxView Color="Yellow" />
</aiforms:WrapLayout>
And there is a RepeatableWrapLayout with Itemtemplate and ItemsSource Binding.

Understanding WidthRequest

I want to change the WidthRequest. Thereby I noticed that this doesn't really set the width of an element. Rather it is kind of a proposal.
Example:
I have a ListView added as child to a StackLayout. I'm setting a WidthRequest for the ListView, but the result is not what I expect.
this.listView = new ListView
{
ItemsSource = new List<IconMenu>
{
// creation of some entries
// ...
},
ItemTemplate = new DataTemplate(typeof(IconMenuCell)),
RowHeight = 44,
// HERE is the problematic code!
WidthRequest = 10,
};
Content = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children = {
this.listView,
this.detailView,
},
};
This is the structure/layout of IconMenuCell:
public IconMenuCell()
{
var icon = new Image
{
Aspect = Aspect.AspectFit,
WidthRequest = 40,
};
icon.SetBinding(Image.SourceProperty, "IconSource");
this.textLabel = new Label {
TextColor = Color.Gray,
FontSize = 10,
VerticalOptions = LayoutOptions.Center,
};
this.textLabel.SetBinding(Label.TextProperty, "Text");
View = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
icon,
this.textLabel,
},
};
}
Setting the WidthRequest to 10 doesn't make sense, because the icon itself should take 40. But here I get the smallest width for the whole list view.
There is no difference if I set WidthRequest to 60 or 120. The resulting width is the same (and not what I want).
How does WidthRequest work here? Do I have to change some LayoutOptions?
You need to specify a HorizontalOptions such as "start" or "center". The default horizontalOptions for stackLayout is FillAndExpand, so child elements like a listview will fill the entire available horizontal area even though you specify a width. This was a bad call on behalf of Microsoft because the default behavior will ignore/override a width request.
Here is a visual example: I have a picker where I set the width request to 200, which should take up about 2/3 of the horizontal space.
<StackLayout Padding="10">
<Picker x:Name="pickerRanks" WidthRequest="200" />
</StackLayout>
As you can see the width request is overridden/ignored. Then if after setting the HorizontalOptions to "Start"...
<StackLayout Padding="10">
<Picker x:Name="pickerRanks" WidthRequest="200" HorizontalOptions="Start"/>
</StackLayout>
The width request is honored. Of course I'm setting the properties in the .xaml file here, which I usually prefer but you can also set the HorizontalOptions in C# like this
pickerRanks.HorizontalOptions = LayoutOptions.Start;
WidthRequest just describes an element's desired width during the next layout cycle.
For it to work as you'd expect, 2 conditions must be satisfied:
1) the requested width is consistent with all constraits (ex. parent's width) and
2) a layout cycle is triggered.
WidthRequest: https://developer.xamarin.com/api/property/Xamarin.Forms.VisualElement.WidthRequest/
But that's complicated. I'd recommend just replacing the stack layout with a grid, and putting each element in a column of the desired width.
Grid Example: https://developer.xamarin.com/api/type/Xamarin.Forms.Grid/

Xamarin Forms Resize Image inside Listview

How can I set the size of the image inside of a list view. Currently I have several lists that have icons but I don't see any options for changing the aspect ratio or size of the image so it just gets blown up to the height of the list item.
all the images are from the Drawable folder
var cell = new DataTemplate(typeof(MenuTextCell));
cell.SetBinding(TextCell.TextProperty, "Title");
cell.SetBinding(ImageCell.ImageSourceProperty, "IconSource");
cell.SetValue(BackgroundColorProperty, Color.Transparent);
cell.SetValue(TextCell.TextColorProperty, Color.FromHex("262626"));
I am using a custom renderer
public class MenuTextCellRenderer : ImageCellRenderer
{
protected override View GetCellCore (Cell item, View convertView, ViewGroup parent, Context context)
{
var cell = (LinearLayout)base.GetCellCore (item, convertView, parent, context);
cell.SetPadding(20, 10, 0, 10);
cell.DividerPadding = 50;
var div = new ShapeDrawable();
div.SetIntrinsicHeight(1);
div.Paint.Set(new Paint { Color = Color.FromHex("b7b7b7").ToAndroid() });
if (parent is ListView)
{
((ListView)parent).Divider = div;
((ListView)parent).DividerHeight = 1;
}
var icon = (ImageView)cell.GetChildAt(0);
var label = (TextView)((LinearLayout)cell.GetChildAt(1)).GetChildAt(0);
label.SetTextColor(Color.FromHex("262626").ToAndroid());
label.TextSize = Font.SystemFontOfSize(NamedSize.Large).ToScaledPixel();
label.TextAlignment = TextAlignment.Center;
label.Text = label.Text.ToUpper();
var secondaryLabel = (TextView)((LinearLayout)cell.GetChildAt(1)).GetChildAt(1);
secondaryLabel.SetTextColor(Color.FromHex("262626").ToAndroid());
secondaryLabel.TextSize = Font.SystemFontOfSize(NamedSize.Large).ToScaledPixel();
label.TextAlignment = TextAlignment.Center;
return cell;
}
You are now dealing with an Android ImageView.
You have your reference via var icon = (ImageView)cell.GetChildAt(0).
You can now customize it via methods such as .setScaleType() for aspect ratio related, and use .layout() to change the position / size.
Try something like this on your ListView.ItemTemplate, instead of using the imagecell. You can also write your custom cell.
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<Image Aspect="AspectFill" HeightRequest ="20" WidthRequest="20" Source="{Binding IconSource}" />
<Label Text="{Binding Title}" YAlign="Center" Font="Medium" />
</StackLayout>
</ViewCell>

Resources