How to assign the grid to a grid - xamarin.forms

I new a grid object without any settings in the xaml file.
After importing the data through 'Build_Grid()' in the 'OnAppearing()', I want to assign the 'gridview' to 'Grid_Info' to display on the screen, the code 'Grid_Info = gridview' does not work.
I am wondering how to achieve my needs?
<ContentPage.Content>
<StackLayout>
<Label Text="AAA" />
<Grid x:Name="Grid_Info">
</Grid>
</StackLayout>
</ContentPage.Content>
void Build_Grid(Data data)
{
Grid gridview = new Grid();
gridview.RowDefinitions.Add(new RowDefinition() { Height = 40 });
gridview.Children.Add(data[0],0,0);
...
Grid_Info = gridview; //it does not work...
}
protected override void OnAppearing()
{
Data data = new Data();
...
Build_Grid(data);
}

if you've already defined the Grid in XAML there is no need to do this
Grid gridview = new Grid();
instead just reference Grid_Info directly
Grid_Info.RowDefinitions.Add(new RowDefinition() { Height = 40 });
Grid_Info.Children.Add(data[0],0,0);

You could try the code below. I make a Data class with view to test. It work for me.
public partial class Page3 : ContentPage
{
Data[] data;
public Page3()
{
InitializeComponent();
data = new Data[2];
data[0] = new Data { view = new Button() { BackgroundColor = Color.Red } }; //data[0]
data[1] = new Data { view = new Label() { BackgroundColor = Color.Green, Text = "Label" } };//data[1]
}
void Build_Grid(Data[] data)
{
Grid gridview = new Grid();
Grid_Info.RowDefinitions.Add(new RowDefinition() { Height = 40 });
Grid_Info.Children.Add(data[0].view, 0, 0);//show the red button
Grid_Info = gridview; //it does not work...
}
protected override void OnAppearing()
{
base.OnAppearing();
Build_Grid(data);
}
}
public class Data
{
public View view { get; set; }
}

Related

CollectionView using DataTemplate is not updating ReactiveContentView BindingContext or ViewModel

I'm using ReactiveUI and trying to get a CollectionView to use a ReactiveContentPage as it's item template in code behind. This works okay until the ReactiveContentView is nested under another Element.
The problem I have is that after scrolling the collection view, the items get duplicated. After debugging, it would seem that the ReactiveContentView's BindingContext or ViewModel aren't updated.
The samples below have been simplified to highlight the issue:
This doesn't work:
public partial class SampleCollectionPage : ReactiveContentPage<SampleCollectionViewModel>
{
private void Build()
{
Content = new CollectionView
{
ItemTemplate = new DataTemplate(() =>
{
return new ContentView
{
Content = new ReactiveContentView<CollectionItemViewModel>
{
Content = new StackLayout
{
Children =
{
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Title)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Description)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.SomeTime)),
}
}
}
};
}),
}
.Bind(CollectionView.ItemsSourceProperty, path: nameof(SampleCollectionViewModel.Items));
}
}
This works:
public partial class SampleCollectionPage : ReactiveContentPage<SampleCollectionViewModel>
{
private void Build()
{
Content = new CollectionView
{
ItemTemplate = new DataTemplate(() =>
{
return new ReactiveContentView<CollectionItemViewModel>
{
Content = new StackLayout
{
Children =
{
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Title)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.Description)),
new Label()
.Bind(Label.TextProperty, nameof(CollectionItemViewModel.SomeTime)),
}
}
};
}),
}
.Bind(CollectionView.ItemsSourceProperty, path: nameof(SampleCollectionViewModel.Items));
}
}
The only difference being that the working version doesn't put the ReactiveContentView in a ContentView. I'm not sure if I'm missing something or whether this is an bug in Xamarin Forms or ReactiveUI?
(edited to add actual problem: doh)

Height Button in iOS (Xamarin Forms) dont resize when text label wrap word (I want dynamic size)

I have a ListView in Xamarin.Forms of this way :
this.listView = new ListView();
this.listView.HasUnevenRows = true;
var dataTemplate = new DataTemplate(() =>
{
return new ViewCell { View = new CustomButtonTemplate()};
});
this.listView.ItemTemplate = dataTemplate;
CustomButtonTemplate.xaml
<local:CustomButton
Margin="6"
Padding="0"
HeightRequest="-1"
WidthRequest="-1"
Style="{StaticResource Title_LabelStyle}"
Text="{Binding DisplayText}" />
I also got one button renderer but dont work (without HeightRequest,WidthRequest,Padding dont work either):
[assembly: ExportRenderer(typeof(CustomButton), typeof(CustomButtonMultilineRenderer))]
namespace SGUK.ClassAction.IOS.Renderers
{
public class CustomButtonMultilineRenderer : ButtonRenderer
{
public CustomButtonMultilineRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.TitleLabel.LineBreakMode = UILineBreakMode.WordWrap;
this.Control.TitleEdgeInsets = new UIEdgeInsets(0, 10, 0, 10);
this.Control.TitleLabel.TextAlignment = UITextAlignment.Center;
this.Control.HorizontalAlignment = UIControlContentHorizontalAlignment.Center;
}
}
}
}
(with MaterialButtonRenderer dont work either)
The auto height with HasUnevenRows=true works fine on iOS if not using a custom renderer. If using a custom renderer, then it is up to the renderer to set the height of the cell, you have to calculate your own row height in the GetHeightForRow method in the custom renderer.
[assembly: ExportRenderer(typeof(ListView), typeof(MyLVRenderer))]
namespace App79.iOS
{
public class MyLVRenderer : ListViewRenderer
{
//UITableViewSource originalSource;
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
UITableViewSource originalSource = (UIKit.UITableViewSource)Control.Source;
Control.Source = new MyLVSource(originalSource, e.NewElement);
}
}
public class MyLVSource : UITableViewSource
{
UITableViewSource originalSource;
ListView myListView;
public MyLVSource(UITableViewSource origSource, ListView myListV)
{
originalSource = origSource;
myListView = myListV;
}
public override nint RowsInSection(UITableView tableview, nint section)
{
return originalSource.RowsInSection(tableview, section);
}
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
return originalSource.GetCell(tableView, indexPath);
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
return originalSource.GetHeightForFooter(tableView, section);
}
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
nfloat origHeight = originalSource.GetHeightForRow(tableView, indexPath);
// calculate your own row height here
ObservableCollection<Employee> employees = myListView.ItemsSource as ObservableCollection<Employee>;
string displayName = employees[indexPath.Row].DisplayName;
nfloat height = MeasureTextSize(displayName,UIScreen.MainScreen.Bounds.Size.Width-50,UIFont.SystemFontSize,null);
return height;
}
public nfloat MeasureTextSize(string text, double width, double fontSize, string fontName = null)
{
var nsText = new NSString(text);
var boundSize = new SizeF((float)width, float.MaxValue);
var options = NSStringDrawingOptions.UsesFontLeading | NSStringDrawingOptions.UsesLineFragmentOrigin;
if (fontName == null)
{
fontName = "HelveticaNeue";
}
var attributes = new UIStringAttributes
{
Font = UIFont.FromName(fontName, (float)fontSize)
};
var sizeF = nsText.GetBoundingRect(boundSize, options, attributes, null).Size;
//return new Xamarin.Forms.Size((double)sizeF.Width, (double)sizeF.Height);
return sizeF.Height + 5;
}
}
}
Here is the result:
I uploaded a sample here and you can check.

how to access child elements in a collection view?

i am making frames in collection view and i want to change the background color of frames whenever i select the frames but the problem is that i cannot access my frames from the collection view and when i click on the frame it picks the default orange color.
i made small changes in my frame model class where i want to change text color of my label as like we did the color of frame here is the code but it is not working
this code is in frame model class
public Color LabelColor
{
set
{
if (txtcolor != value)
{
txtcolor = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("labelcolor"));
}
}
}
get
{
return firstFrameBackColor;
}
}
here i access this in my CS Class
FrameModel previous = (e.PreviousSelection.FirstOrDefault() as FrameModel);
FrameModel current = (e.CurrentSelection.FirstOrDefault() as FrameModel);
//Set the current to the color you want
try
{
current.FirstFrameBackColor = Color.FromRgb(74, 152, 247);
current.LabelColor = Color.White;
// current.SecondFrameBackColor = Color.Green;
}
catch (Exception ex)
{
ex.Message.ToString();
throw;
}
// current.SecondFrameBackColor = Color.Green;
if (previous != null)
{
//Reset the previous to defaulr color
previous.FirstFrameBackColor = Color.White;
current.LabelColor = Color.Black;
//previous.SecondFrameBackColor = Color.Purple;
}
when i give x:name property to my child views inside the collection
view i cant access them in my C# class
You can't access items in templates from the code behind by name because there may be 0 or 1000 copies of that template created at run time.
Refer this discussion: cannot-reach-control-x-name-inside-listview
In your case, instead of access the control in the template, you can set the background property binding to the property in the model, for example:
<Frame
WidthRequest="20"
HeightRequest="20"
Margin="0,-30,0,10"
HorizontalOptions="End"
CornerRadius="10"
Padding="5"
BackgroundColor="{Binding SecondFrameBackColor}">
<Label
Text="5"
TextColor="#FFFFFF"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
>
</Label>
</Frame>
Here is the model:
class myModel : INotifyPropertyChanged
{
Color firstFrameBackColor;
Color secondFrameBackColor;
public event PropertyChangedEventHandler PropertyChanged;
public myModel()
{
}
public Color FirstFrameBackColor
{
set
{
if (firstFrameBackColor != value)
{
firstFrameBackColor = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("FirstFrameBackColor"));
}
}
}
get
{
return firstFrameBackColor;
}
}
public Color SecondFrameBackColor
{
set
{
if (secondFrameBackColor != value)
{
secondFrameBackColor = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SecondFrameBackColor"));
}
}
}
get
{
return secondFrameBackColor;
}
}
}
The itemSoure:
public partial class MainPage : ContentPage
{
ObservableCollection<myModel> models = new ObservableCollection<myModel>();
public MainPage()
{
InitializeComponent();
myModel model1 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model2 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model3 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model4 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model5 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model6 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
myModel model7 = new myModel() { FirstFrameBackColor = Color.White, SecondFrameBackColor = Color.Purple };
models.Add(model1);
models.Add(model2);
models.Add(model3);
models.Add(model4);
models.Add(model5);
models.Add(model6);
models.Add(model7);
CNlist.ItemsSource = models;
}
And in the SelectionChanged event, change the background to what you want:
private void CNlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
myModel previous = (e.PreviousSelection.FirstOrDefault() as myModel) ;
myModel current = (e.CurrentSelection.FirstOrDefault() as myModel);
//Set the current to the color you want
current.FirstFrameBackColor = Color.Pink;
current.SecondFrameBackColor = Color.Green;
if (previous != null)
{
//Reset the previous to defaulr color
previous.FirstFrameBackColor = Color.White;
previous.SecondFrameBackColor = Color.Purple;
}
}
Here is the result:
I updated my sample here and you can check it. Let me know if it works to you!
From the CollectionView Spec:
The orange color is actually the value for state_activated in your
Android app's theme. So not everyone will see orange; that's just the
AppCompat default. This is the fallback value if nothing else has been
specified.
When an item in the CollectionView is selected, the VisualState for
the root Forms element of the item is changed to Selected. You can use
the VisualStateManager to manage what a selected item looks like. For
an example of this, take a look at the SelectionModeGallery in Control
Gallery. On that page, the background color for the selected item is
being set to LightSkyBlue. Any other Forms property can also be set;
for instance, try adding to
the tag.
At the moment, this is somewhat limited; you can really only modify
the root element in your ItemTemplate.
So, to answer your question, it will not work, since your Frame is not the Root element from your ItemTemplate. Either you apply that in your StackLAyout, or in the SelectedItem, you change manually the background color of your element.

How to find whether Listview scrolled up or down in xamarin forms?

I need to hide some view, when listview scrolled up and display that view again, when list view scrolled down.
I used
private void ScheduleServiceList_ItemAppearing(object sender, Xamarin.Forms.ItemVisibilityEventArgs e)
{
var currentItem = e.Item as VehicleService;
double listviewheight = ScheduleServiceListView.Height;
if (viewModel.VehicleServiceList[0].Id == currentItem.Id)
{
SearchEntryLayout.IsVisible = true;
HeadingGrid.Margin = new Thickness(0, -70, 0, 0);
}
}
What about:
var listView = new ListView(ListViewCachingStrategy.RecycleElement)
{
....
ItemTemplate = new DataTemplate(() =>
{
return new MyListViewCellView(new List<int> { ... /* ids to disable or other objects */ });
})
};

Multi-Line text Button Xamarin.Forms

theres is a way to set a multi-line text to a Xamarin.Forms Button??
I've tried Button.Text = "something \n xxjjjxx" But don't work.
A simple solution will use:
There is an excellent example on how to achieve this on Github. It is quite simple really. Just create your own control that inherits from ContentView and contains a grid.
[ContentProperty("Content")]
public class MultiLineButton : ContentView
{
public event EventHandler Clicked;
protected Grid ContentGrid;
protected ContentView ContentContainer;
protected Label TextContainer;
public String Text
{
get
{
return (String)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
OnPropertyChanged();
RaiseTextChanged();
}
}
public new View Content
{
get { return ContentContainer.Content; }
set
{
if (ContentGrid.Children.Contains(value))
return;
ContentContainer.Content = value;
}
}
public static BindableProperty TextProperty = BindableProperty.Create(
propertyName: "Text",
returnType: typeof(String),
declaringType: typeof(MultiLineButton),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: TextValueChanged);
private static void TextValueChanged(BindableObject bindable, object oldValue, object newValue)
{
((MultiLineButton)bindable).TextContainer.Text = (String)newValue;
}
public event EventHandler TextChanged;
private void RaiseTextChanged()
{
if (TextChanged != null)
TextChanged(this, EventArgs.Empty);
}
public MultiLineButton()
{
ContentGrid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand
};
ContentGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
ContentGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
ContentContainer = new ContentView
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
TextContainer = new Label
{
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
ContentContainer.Content = TextContainer;
ContentGrid.Children.Add(ContentContainer);
var button = new Button
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.FromHex("#01000000")
};
button.Clicked += (sender, e) => OnClicked();
ContentGrid.Children.Add(button);
base.Content = ContentGrid;
}
public void OnClicked()
{
if (Clicked != null)
Clicked(this, new EventArgs());
}
}
Then it can be used like this:
<local:MultiLineButton x:Name="AssessmentToolDetailButton"
WidthRequest="100" HeightRequest="60" BackgroundColor="Blue">
<StackLayout HorizontalOptions="Center" VerticalOptions="CenterAndExpand">
<Label Text="Hello" TextColor="White" Font="16"/>
<Label Text="World" TextColor="White" Font="16"/>
</StackLayout>
</local:MultiLineButton>
You can also place an image in the button by setting its content.
In my example I modified Dans original code in order to make the text bindable. Just set the Text value instead of the Content like this:
<local:MultiLineButton Text="{Binding Description}" />
All credit goes to Danvanderboom for his example:
ConentButton by Danvanderboom
This is mainly a problem with iOS because Android will wrap the text
by default. I tried the solution provided by Kasper and it worked
however the buttons do not have rounded corners and the appearance is
not consistent with other buttons in my app.
A simple solution is to use a custom renderer (ButtonRenderer) to set the LineBreakMode to WordWrap. If you then set the width of the button in the Xaml you get words to appear on different lines.
iOS
public class WrappedButtonRenderer: ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
Control.TitleEdgeInsets = new UIEdgeInsets(4, 4, 4, 4);
Control.TitleLabel.LineBreakMode = UILineBreakMode.WordWrap;
Control.TitleLabel.TextAlignment = UITextAlignment.Center;
}
}
Android does not require a custom renderer because it wraps by default.
This is a known issue with Xamarin Forms.
I don't think I've seen two lined buttons often. You have two options that I think might work:
Create a Custom Renderer and Extend the respective Button Class to do more on each native platform. Might be a harder
Create a Xamarin.Forms Class that extends a View that can contains a StackLayout and smaller elements such as multi-line labels, then you can use a TapGestureRecognizer to use with your view and treat it like a button.
Expanding on fireydude's answer, I created a MultilineButton control and renderer for iOS so I could add text alignment. This uses the Xamarin.Forms.TextAlignment enum.
MultilineButton.cs
using Xamarin.Forms;
namespace APP_NAMESPACE.Controls
{
public class MultilineButton : Button
{
public static readonly BindableProperty HorizontalTextAlignmentProperty = BindableProperty.Create(
propertyName: "HorizontalTextAlignment",
returnType: typeof(TextAlignment),
declaringType: typeof(MultilineButton),
defaultValue: TextAlignment.Start
);
public TextAlignment HorizontalTextAlignment
{
get { return (TextAlignment)GetValue(HorizontalTextAlignmentProperty); }
set { SetValue(HorizontalTextAlignmentProperty, value); }
}
}
}
MultilineButtonRenderer.cs
using APP_NAMESPACE.Controls;
using APP_NAMESPACE.iOS.Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MultilineButton), typeof(MultilineButtonRenderer))]
namespace APP_NAMESPACE.iOS.Renderers
{
public class MultilineButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control == null) { return; }
UIControlContentHorizontalAlignment horizontalAlignment;
UITextAlignment textAlignment;
// We have to use ButtonRenderer, so cast the Element to MultilineButton to get the HorizontalTextAlignment property
var button = (MultilineButton)Element;
if (button == null) { return; }
switch(button.HorizontalTextAlignment)
{
case TextAlignment.Center:
horizontalAlignment = UIControlContentHorizontalAlignment.Center;
textAlignment = UITextAlignment.Center;
break;
case TextAlignment.End:
horizontalAlignment = UIControlContentHorizontalAlignment.Right;
textAlignment = UITextAlignment.Right;
break;
default:
horizontalAlignment = UIControlContentHorizontalAlignment.Left;
textAlignment = UITextAlignment.Left;
break;
}
Control.HorizontalAlignment = horizontalAlignment;
Control.TitleLabel.LineBreakMode = UILineBreakMode.WordWrap;
Control.TitleLabel.TextAlignment = textAlignment;
}
}
}
Then use it within XAML:
<controls:MultilineButton Text="This Button is Centered!" HorizontalTextAlignment="Center" />

Resources