Xamarin.Forms Reorderable WrapLayout - c#

I have a group of cells (of type StackLayout) and they are all in a WrapLayout. I can't seem to figure out how to make it reorderable, similar to Drag & Drop ListView.
Here is the WrapLayout:
WrapLayout layout = new WrapLayout
{
Spacing = 5,
Orientation = StackOrientation.Horizontal,
Padding = new Thickness(5, Device.OnPlatform(20, 5, 5), 5, 5),
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
};
And here is one cell:
// Configure cell
var cell = new StackLayout
{
WidthRequest = Device.Idiom == TargetIdiom.Phone ? 60 : 140,
HeightRequest = Device.Idiom == TargetIdiom.Phone ? 60 : 50,
BackgroundColor = Color.FromRgb(60, 56, 72),
Children = {
new Label {
Text = Device.Idiom == TargetIdiom.Phone ? book.Contains("1") || book.Contains("2") || book.Contains("3") ? book.Substring(0,4).Replace(" ", "") : book.Substring(0,2) : book,
FontSize = 18,
TextColor = Color.White,
LineBreakMode = LineBreakMode.TailTruncation,
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalTextAlignment = TextAlignment.Center,
HorizontalTextAlignment = TextAlignment.Center
}
}
};
layout.Children.Add(cell);
And here is what the app actually looks like as of now:
Any help in making this possible is appreciated.

Well the WrapLayout that you're referring to is a custom layout that was created as one of them demos by the devs I believe. To have drag and drop functionality, you would have to implement it manually yourself.
Personally I would attach an event handler to the Tapped event of the cell:
var cell = new StackLayout
{
Tapped += Cell_Tapped;
WidthRequest = Device.Idiom == TargetIdiom.Phone ? 60 : 140,
HeightRequest = Device.Idiom == TargetIdiom.Phone ? 60 : 50,
BackgroundColor = Color.FromRgb(60, 56, 72),
Children = {
new Label {
Text = Device.Idiom == TargetIdiom.Phone ? book.Contains("1") || book.Contains("2") || book.Contains("3") ? book.Substring(0,4).Replace(" ", "") : book.Substring(0,2) : book,
FontSize = 18,
TextColor = Color.White,
LineBreakMode = LineBreakMode.TailTruncation,
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalTextAlignment = TextAlignment.Center,
HorizontalTextAlignment = TextAlignment.Center
}
}
};
layout.Children.Add(cell);
and then create an event handler:
void Cell_Tapped(object sender, EventArgs e)
{
// Stuff you want to do with that cell
// Make the current selected cell invisible, attach it to a boxview that is being moved by the tap event
}
This would however only work for a tap and not a true drag and drop implementation, and Xamarin.Forms doesn't currently give you access to touch events. You could write out Custom Renderers for the cells on iOS and Android that would allow you to access the TouchesBegan and TouchesEnded type events on both platforms.
This Xamarin link gives walkthroughs on integrating touch on both iOS and Android.
I hope this helps, I'm sure there are different implementations of this.

Related

ChartView within StackLayout cuts off Chart

Here is my code:
List<Entry> entries = new List<Entry>
{
new Entry(7)
{
Color = SKColor.Parse("#166DA3"),
},
new Entry(3)
{
Color = SKColors.Transparent,
}
};
public RoundScore2()
{
Content = _contentLayout; // layout inherited from a different class
Label congrats = new Label
{
Text = "Congratulations!",
FontAttributes = FontAttributes.Bold,
FontSize = 30,
HorizontalOptions = LayoutOptions.CenterAndExpand,
VerticalOptions = LayoutOptions.Start,
TextColor = Color.Black
};
_contentStack.Children.Add(congrats); // _contentStack inherited from same class, _contentStack is added to _contentLayout
ChartView Chart1 = new ChartView
{
VerticalOptions = LayoutOptions.CenterAndExpand,
HeightRequest = 80
};
_contentStack.Children.Add(Chart1);
Chart1.Chart = new DonutChart() { Entries = entries, HoleRadius = 5 };
Button nextRound = new Button
{
Text = "Start Round " + roundCounter.ToString(),
Margin = new Thickness(10, 20, 10, 10),
TextColor = Color.Black,
BackgroundColor = Color.FromHex("48AADF"),
FontFamily = "Source Sans Pro",
FontSize = 20,
FontAttributes = FontAttributes.Bold,
CornerRadius = 8,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.StartAndExpand,
WidthRequest = 180
};
_contentStack.Children.Add(nextRound);
}
Stack overflow is not letting me add the photo right now ("Failed to upload image; couldn't reach imgur")...but basically, I can only see the horizontal middle section of the pie chart. There is enough space for the chart to show, but it is just cut off. I have tried setting VerticalOptions = LayoutOptions.FillAndExpand along with LayoutOptions.CenterAndExpand and no luck. Does anyone know why this is happening?
Edit: even with the Label and Button taken out, the view is still cut off.
but basically, I can only see the horizontal middle section of the pie chart.
From shared code of ChartView, I'm guessing that whether the width of chart view is too small.
ChartView Chart1 = new ChartView
{
VerticalOptions = LayoutOptions.CenterAndExpand,
HeightRequest = 80
};
If so , you can add HorizontalOptions as follow:
ChartView Chart1 = new ChartView
{
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
HeightRequest = 80
};
In addition , also can add HorizontalOptions for StackLayout:
_contentStack.HorizontalOptions = LayoutOptions.FillAndExpand;
_contentStack.VerticalOptions = LayoutOptions.FillAndExpand;
It worked after changing VerticalOptions = LayoutOptions.Center and HorizontalOptions = LayoutOptions.Fill as well as changing HoleRadius = .5f.

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 :)

How to implement method of sub item of DataTemplate of ListView in Xamarin.Forms

I have ListView and I assigned DataTemplate to it. Now I have some controls in it like Button, Images and Labels. Now I want some actions on click event of that controls.
My ListView is like this-
listof_ingredients_and_lifestyleDiets = new ListView
{
BackgroundColor = Color.FromHex ("#CDBA96"),
ItemTemplate = preferenceTableCell,
SeparatorColor =Color.FromHex("#CDBA96"),
RowHeight=40,
HeightRequest=200
};
My DataTemplate is like this-
DataTemplate preferenceTableCell = new DataTemplate (() => {
var itemName = new Label {
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
WidthRequest=80,
FontSize=14,
TextColor=ColorResources.TextColor,
};
itemName.SetBinding (Label.TextProperty, "Name");
var radiobtn_allergen = new CircleImage {
BorderColor = ColorResources.commonButtonBackgroundColor,
HeightRequest = 25,
WidthRequest = 25,
Aspect = Aspect.AspectFill,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Source="radio_Check.png"
};
radiobtn_allergen.SetBinding(CircleImage.IsVisibleProperty,"isExcluded");
var radiobtn_preference = new CircleImage {
BorderColor = ColorResources.commonButtonBackgroundColor,
HeightRequest = 25,
WidthRequest = 25,
Aspect = Aspect.AspectFill,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
Source="radio_uncheck.png",
};radiobtn_preference.SetBinding (CircleImage.IsVisibleProperty, "isExcluded");
var btnDelete=new Button{
Image="deleteBtn.png",
HorizontalOptions=LayoutOptions.EndAndExpand,
HeightRequest=50,
WidthRequest=30
};
StackLayout stacklayout = new StackLayout {
Spacing = 60,
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { itemName,radiobtn_allergen,radiobtn_preference,btnDelete }
};
return new ViewCell { View = stacklayout };
});
Now My question is, I want implement tap gesture for image tap and button click event for button control. How to do this?
Click handler for a button
btnDelete.Clicked += ((sender, args) => {
// perform actions here
});
attach a gesture recognizer to an Image
TapGestureRecognizer tapped = new TapGestureRecognizer();
tapped.Tapped += ((o2, e2) =>
{
// perform actions here
});
img.GestureRecognizers.Add(tapped);

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.

How to wrap a button click event to the right ViewCell

I've created a CustomCell and puts a button on it.
http://i1068.photobucket.com/albums/u451/tasknick/Captura%20de%20Tela%202015-06-15%20as%2010.05.10_zpsit980vqh.png
public class CustomCell : ViewCell
{
public CustomCell ()
{
var Name = new Label {
TextColor = Color.Black,
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.Start,
FontFamily = Device.OnPlatform ("GillSans", "Quattrocento Sans", "Comic Sans MS")
};
Name.SetBinding (Label.TextProperty, "FirstName", BindingMode.TwoWay);
var LastName = new Label {
TextColor = Color.Gray,
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
VerticalOptions = LayoutOptions.End, HorizontalOptions = LayoutOptions.Start
, FontFamily = Device.OnPlatform ("GillSans", "Quattrocento Sans", "Comic Sans MS")
};
LastName.SetBinding (Label.TextProperty, "LastName", BindingMode.TwoWay);
var ActionButton = new Button {
Image = Images.ActionButton,
Style = Styles.DefaultButtonStyle,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.End
};
ActionButton.SetBinding (Button.CommandProperty, "commandActionButton", BindingMode.TwoWay);
StackLayout stack = new StackLayout {
Padding = new Thickness (20, 0, 0, 0),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.StartAndExpand,
Children = { Name, LastName, ActionButton }
}
{CODE}
View = layout;
ActionButton.Clicked += (object sender, System.EventArgs e) => Debug.WriteLine ("asdjhadjsad");
}
}
The click event from button works. But how I know what cell this event comes?
For exemple: When I click on the button on the first cell I wanna show the text from the first cell;
Why do you handle ActionButton.Clicked instead of handling Command?
You've defined binding here
ActionButton.SetBinding (Button.CommandProperty, "commandActionButton", BindingMode.TwoWay);
So you could handle click in your view-model.
If you want to handle this event - sender should be your button's instance.
Try to use the FrameworkElement.Parent Property and cast it as the custom cell. This will give you a handle on the cell which holds the button. A rough example would be "(CustomCell)sender.Parent"

Categories

Resources