Is there a way to combine grid column definitions in Xamarin C# - xamarin

Here's what I have:
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
Is there a way I can combine any of these lines together?

Not that I know of at present but if it is something you are doing often then you could make it easier via an extension method...
public static class GridExtensions
{
public static Grid Add(this Grid grid, ColumnDefinition columnDefinition)
{
grid.ColumnDefinitions.Add(columnDefinition);
return grid;
}
public static Grid AddColumnDefinition(this Grid grid, GridLength gridLength) =>
grid.Add(new ColumnDefinition {Width = gridLength});
}
Then you can call it like:
grid.AddColumnDefinition(new GridLength(1, GridUnitType.Star))
.AddColumnDefinition(new GridLength(1))
.AddColumnDefinition(new GridLength(1, GridUnitType.Star));
You could even take it a step further and accept the parameters for constructing GridLength and do that inside your extension method.
I am happy to accept that this is not providing fewer lines but it is less verbose and easier to read.

Create an extension method
Usage
//<Grid.ColumnDefinitions>
// <ColumnDefinition Width="2*"/>
// <ColumnDefinition Width="*"/>
// <ColumnDefinition Width="Auto"/>
// <ColumnDefinition Width="50"/>
//</Grid.ColumnDefinitions>
// above Xaml equivalent
grid.AddColumnDefinitions("2*", "*", "Auto", "50");
grid.AddColumnDefinitions(); //equivalent to adding a column with 1*
Code
write sanity checks for syntax etc. to ensure code doesn't break.
public static class GridExtensions
{
public static Grid AddColumnDefinitions(this Grid grid, params string[] columndefinitions)
{
if (columndefinitions == null || columndefinitions.Length < 1)
{
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star });
return grid;
}
foreach (var columnDef in columndefinitions)
{
if (columnDef.Equals("Auto"))
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
else if (columnDef.Contains("*"))
{
string columnWidth = columnDef.Replace("*", string.Empty);
if (columnWidth.Equals(string.Empty))
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Star });
else if (int.TryParse(columnWidth, out int colWidth))
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(colWidth, GridUnitType.Star) });
}
else if (int.TryParse(columnDef, out int colDef))
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(colDef, GridUnitType.Absolute) });
}
return grid;
}
}
P.S. Credit: A little spin on the answer by Bijington.

You could write
var grid = new Grid(){ ColumnDefinitions = {
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
new ColumnDefinition() { Width = new GridLength(1) },
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }}};

Related

Xamarin Forms - TapGestureRecognizer.CommandParameterProperty not working

I'm creating a Grid programmatically, and i want to add a Tap Gesture Recognizer to each layout, and passing some parameters with it. I'm using MVVM pattern.
Here is how i'm doing this :
public SelectProfileViewModel()
{
_userGrid = new Grid();
_userGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
_userGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
_userGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = 150 });
_userGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = 150 });
_userGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = 150 });
_userGrid.Children.Add(createUserView("A"), 0, 0);
_userGrid.Children.Add(createUserView("B"), 1, 0);
_userGrid.Children.Add(createUserView("C"), 0, 1);
_userGrid.Children.Add(createUserView("D"), 1, 1);
_userGrid.Children.Add(createUserView("E"), 2, 0);
}
private View createUserView(string name)
{
var parentView = new StackLayout { HorizontalOptions = LayoutOptions.Center };
parentView.Children.Add(new Image { Source = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ1Gevu4Tk803Ydc4VywH_ANoJSf3B6rnuI64IChMJSdw9qfR7s", HorizontalOptions = LayoutOptions.Center } );
parentView.Children.Add(new Label { Text = name, HorizontalOptions = LayoutOptions.Center } );
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandProperty, "SelectProfileCommand");
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, name);
parentView.GestureRecognizers.Add(tapGestureRecognizer);
return parentView;
}
When i click on the child view, the command is well called. But the parameter is null.
Here is the command and parameter part :
public ICommand SelectProfileCommand => new Command<string>(async (name) => await SelectProfileAsync(name));
private async Task SelectProfileAsync(string name)
{
await NavigationService.NavigateToAsync<LoginViewModel>(name);
}
Anyone has an idea of what's happening ?
Thanks.
Binding refers to a property. So you are trying to bind to properties called A, B, C, .... If this should work, you have to have properties with these names in your binding context.
If you just want to set the parameter to a constant value you can do it by changing
tapGestureRecognizer.SetBinding(TapGestureRecognizer.CommandParameterProperty, name);
// to
tapGestureRecognizer.CommandParameter = name;

How can I access objects from a xaml that I create in cs code?

I'm trying to access some objects that I create with cs (carousel pages with labels, listview and buttons) but I have not had success.
Here reference this by saying that you can work with classId or styleId, but it does not make it very clear how to get the same:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/getting_started_with_xaml/
Here you can see the carousel pages
This is my cs code:
private readonly List<AreaModel> _are = new List<AreaModel>();
public AreasModal()
{
Lista();
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
var index = Children.IndexOf(CurrentPage);
}
private async void Lista()
{
var listareas = await App.NaturalezaManager.GetAreas();
foreach (var Area in listareas) {
var areas = new AreaModel
{
IdArea = Area.IdArea,
NombreArea = Area.NombreArea
};
_are.Add(areas);
Button boton = new Button
{
Text = "Listo",
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
};
Children.Add(new ContentPage
{
Content = new StackLayout
{
Children =
{
new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
new Label
{
Text = Area.NombreArea,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand
}
}
},
new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
}
}
},
new ListView
{
ItemsSource = listareas,
ItemTemplate = new DataTemplate(() =>
{
Label nameLabel = new Label
{
HorizontalOptions = LayoutOptions.FillAndExpand
};
nameLabel.SetBinding(Label.TextProperty,"NombreArea");
Switch switcher = new Switch
{
HorizontalOptions = LayoutOptions.EndAndExpand
};
return new ViewCell
{
View = new StackLayout
{
Orientation = StackOrientation.Horizontal,
Children =
{
new StackLayout
{
Orientation = StackOrientation.Vertical,
Children =
{
nameLabel
}
},
switcher
}
}
};
})
},
boton
}
}
});
}
}
}
I have to access these objects from the "OnCurrentPageChanged" method but I have no idea how to do it, since if it were xaml code could be obtained with the property x:Name, in advance thanks for the help.

ScrollView Overlap

I try to put a ScrollView with a StackLayout between two StackLayouts but when I scroll the elements inside the ScrollView, the items always overlap the superior StackLayout as seen in the image:
The code I use is:
stackPrinc = new StackLayout()
{
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = {
ly_sup,
new ScrollView() { Content = ly_main, IsClippedToBounds = true, BackgroundColor = Color.Olive },
ly_inf },
};
I solved it, its a bug happends when the scrollview got a elemen over and under him. My new code is;
stackPrinc = new StackLayout()
{
Spacing = 0,
Children = {
ly_sup,
new StackLayout() { Children = { new ScrollView() { Content = ly_main, IsClippedToBounds = true, BackgroundColor = Color.Aqua, } } },
ly_inf,
},
};

Xamarin forms animation does not repeat

I am trying to animate an Image in Xamarin.Forms (version 2.3.3.168). The animation is working, but it does not repeat.
public class WelcomePage : ContentPage
{
Image img;
public WelcomePage()
{
img = new Image
{
Source = "img.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
img
}
};
}
protected override void OnAppearing()
{
base.OnAppearing();
var a = new Animation();
a.Add(0, 0.5, new Animation((v) =>
{
img.Scale = v;
}, 1.0, 1.2, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION A"); }));
a.Add(0.5, 1, new Animation((v) =>
{
img.Scale = v;
}, 1.2, 1.0, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION B"); }));
a.Commit(img, "animation", 16, 2000, Easing.Linear, (d, f) => img.Scale = 1.0, () =>
{
System.Diagnostics.Debug.WriteLine("ANIMATION ALL");
return true;
});
}
}
After running the app for a few seconds, the following debug output is printed:
ANIMATION A
ANIMATION B
ANIMATION ALL
ANIMATION ALL
ANIMATION ALL
I am testing this as a UWP.
According to this thread on the Xamarin forums, others seem to be have the same issue. It seems related to a private property being set in each of the sub-animations.
A workaround is to recreate the animation chain each time the chain completes:
public class WelcomePage : ContentPage
{
Image img;
public WelcomePage()
{
img = new Image
{
Source = "circle_plus.png",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
};
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
img
}
};
}
protected override void OnAppearing()
{
base.OnAppearing();
animate();
}
void animate()
{
var a = new Animation();
a.Add(0, 0.5, new Animation((v) =>
{
img.Scale = v;
}, 1.0, 1.2, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION A"); }));
a.Add(0.5, 1, new Animation((v) =>
{
img.Scale = v;
}, 1.2, 1.0, Easing.CubicInOut, () => { System.Diagnostics.Debug.WriteLine("ANIMATION B"); }));
a.Commit(img, "animation", 16, 2000, null, (d, f) =>
{
img.Scale = 1.0;
System.Diagnostics.Debug.WriteLine("ANIMATION ALL");
animate();
});
}
}

Grid not resizing when child's visibility is changed

I have a Xamarin app with a Grid, where there is a Label and an Entry.
When I change the Entry’s text, I want the Label to be visible, and if it is empty, it shall not show the Label.
I have created a demo-app to verify, that it does not have anything to do with my code. But the following code also fails
MainPage = new ContentPage
{
Content = new Grid
{
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = new GridLength(1,GridUnitType.Auto) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
}
}
};
Label label;
Entry entry;
Grid mainGrid = ((Grid)((ContentPage)MainPage).Content);
mainGrid.Add(btnDelete = new MR.Gestures.StackLayout
{
Children = {
new ExtendedLabel { Style = Styles.LargeLabelIcon, Text = "\ue634", TextColor = Color.Black }
},
WidthRequest = 50
}, 0, 0);
mainGrid.Children.Add(label = new Label { Text = "TEST", IsVisible = false }, 0, 0);
mainGrid.Children.Add(entry = new Entry { }, 1, 0);
entry.TextChanged += (sender, arg) => label.IsVisible = entry.Text.Length > 0;
Does anybody have an idea of how to fix it?
I am not sure if this is what your are trying to achieve:
MainPage = new ContentPage
{
Content = new Grid
{
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = new GridLength(1,GridUnitType.Auto) },
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
}
}
};
Label label;
Entry entry;
Grid mainGrid = ((Grid)((ContentPage)MainPage).Content);
mainGrid.Children.Add(label = new Label {
Text = "TEST",
IsVisible = false,
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Start,
}, 0, 0);
mainGrid.Children.Add(entry = new Entry {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.Black,
TextColor = Color.White
}, 1, 0);
entry.TextChanged += (sender, arg) => label.IsVisible = entry.Text.Length > 0;
I have added vertical and horizontal options to the controllers.
I just had a very similar issue. After the text on my child component was changed, it would be cut-off.
The problem turned out to be that you can't mix a ColumnDefinition of * on the grid with a HorizontalOptions on the child. Having both seems to break something. Replacing it with HorizontalTextAlignment fixed it for me.
Maybe this will help someone else someday.

Resources