I'm trying to mount a view using VerticalStackLayout, but the second (Border) disappears when I add another CollectionView sample element, how can I resolve this, if I comment out the first element(CollectionView) the second appears pasted at the top.
Yes, that is because the CollectionView automatically full fill the page. So you couldn't see the Border.
Some workaround here:
1.You could set the HeightRequest for the CollectionView:
<CollectionView x:Name="CollectionViewTransactions" ... HeightRequest="300">
2.if there is more item in CollectionView, you could put it in a ScrollView:
<ScrollView Orientation="Vertical">
<CollectionView x:Name="CollectionViewTransactions" ... HeightRequest="300">
</ScrollView>
3.You may also customize the HeightRequest for CollectionView depending on the count of item. The following code is just for example.
In xaml file,set binding for HeightRequest
<CollectionView x:Name="CollectionViewTransactions" HeightRequest="{Binding Height}" ...>
In ViewModel, define a property for height and update it when adding a new item:
public int Height
{
get
{
return height;
}
set
{
height = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Height)));
}
}
public void SetNewHeight()
{
Height = 70 * ItemCollection.Count; //
}
All of the above three ways can show border below the CollectionView. You could have a try based on your design
For more info, you could refer to CollectionView.
Hope it works for you.
Related
I have a CarouselView in which I call an object with individual and completely different Views.
The only elements my ContentPage has, are the CarouselView itself, and a bottombar with a gradient above it (notice gradient in following image).
I have done this in a way in which this gradient dissapears when the page's scrolling space becomes 0 (when I have scrolled to the end of the page).
The problem is that when I swipe between items in the CarouselView, the CarouselView always maintains the height of the very first View that is called in.
This means that, in a View with MORE height than the 1st one, when scrolling up (after being at the very bottom, and therefore not showing a gradient) the gradient will only show again once it hits the height value of the 1st page.
In a View with LESS height than the 1st one, the page will allow me to scroll down until I reach the height value of the 1st page, even if there are not enough elements on the page to even need a scroll.
Essentially, what I am asking for, is if there is a way in which I can, in some way, "refresh" the height of the Page every time a scroll is complete to another View in the CarouselView, resolving my height issues in smaller views, and my gradient issues in larger views.
Main ContentPage Code Behind (Gradient)
public double ScrollingSpace
{
get
{
return MainScrollView.ContentSize.Height - MainScrollView.Height;
}
set { }
}
// Removes gradient when scroll is complete
private void OnScrolled(object sender, ScrolledEventArgs e)
{
if (ScrollingSpace <= e.ScrollY) // Touched bottom
EndPageGradient.SetValue(IsVisibleProperty, false); // the view is GONE, not invisible
else
EndPageGradient.SetValue(IsVisibleProperty, true);
}
// Removes gradient if page is not large enough to need scroll
protected override void OnAppearing()
{
if (ScrollingSpace <= 0)
EndPageGradient.SetValue(IsVisibleProperty, false); // the view is GONE, not invisible
}
Main ContentPage CarouselView XAML
<CarouselView
ItemsSource="{Binding ViewList}"
Loop="False">
<CarouselView.ItemTemplate>
<DataTemplate>
<ContentView Content="{Binding .}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
Main ContentPage ViewModel (List with Views for CarouselView)
ViewList = new List<ContentView>()
{
new Step1(),
new Step2(),
new Step3(),
new Step4(),
new Step5(),
new Step6(),
new Step7(),
new Step8()
};
Thanks in advance!
Here is the XAML Code from the documentation:
<StackLayout>
<CarouselView ItemsSource="{Binding Monkeys}"
IndicatorView="indicatorView">
<CarouselView.ItemTemplate>
<!-- DataTemplate that defines item appearance -->
</CarouselView.ItemTemplate>
</CarouselView>
<IndicatorView x:Name="indicatorView"/>
</StackLayout>
I am using C# Markup so my code looks like this (styling code and some layout settings removed to keep simple for SO question):
CarouselView ScrollingImageView() => new CarouselView
{
ItemTemplate = new DataTemplate(() =>
{})
}
.Bind(ItemsView.ItemsSourceProperty, nameof(_vm.ListOfItems))
.Bind(CarouselView.PositionProperty, nameof(_vm.ScrollingImageViewerPosition));
IndicatorView IndicatorView() => new IndicatorView
{
IndicatorsShape = IndicatorShape.Circle,
};
Grid ButtonIndicatorGrid() => new Grid
{
Children = { IndicatorView().Row(0).Column(1) }
};
void Build() => Content =
new Grid
{
Children = {
ScrollingImageView().Row(0),
ButtonIndicatorGrid().Row(1),
}
};
In this example, the IndicatorView is rendered beneath the
CarouselView, with an indicator for each item in the CarouselView. The
IndicatorView is populated with data by setting the
CarouselView.IndicatorView property to the IndicatorView object. Each
indicator is a light gray circle, while the indicator that represents
the current item in the CarouselView is dark gray. Setting the
CarouselView.IndicatorView property results in the
IndicatorView.Position property binding to the CarouselView.Position
property, and the IndicatorView.ItemsSource property binding to the
CarouselView.ItemsSource property.
Now I cannot find an example of how to do this with C#. In XAML it works by setting the name but how can I do the same thing in C# as from what I understand (please correct me if wrong), I cannot set the name in C# in the same way as in XAML.
you need to maintain a reference to your UI objects when you create them
var iv = IndicatorView();
var cv = ScrollingImageView();
cv.IndicatorView = iv;
I have a implement syncfusion Carousel and binding items using ItemTemplate.When i load the items to Carousel all item appears in Carousel view.But i need to add a doted indicator for it.
When user swipe though the Carousel by the dots should indicate current position.
When reading from documentation of syncfusion rotator have this functionality.
I need to add this to carousel view.
Here you can find all the SfCarousel Class Members.
And there's no property for the dots you refered in the SfCarousel print.
In fact, I think you are confusing it with another component called SfRotator. (that has an identical example like your print). and the property you are looking for is called: DotPlacement.
And can have the following states:
None //No Dots
Default //Dots Inside the Rotator View
OutSide //Dots Outside the Rotator View
We have analyzed your query and currently we don’t have dots view support in CarouselView. However, we can fulfill this requirement by using Border or Button control as like below code snippet.
Custom DotsView:
XAML:
<border:SfBorder BorderColor="{Binding ThumbBorder}" HorizontalOptions="Center" VerticalOptions="Center" BorderWidth="5" CornerRadius="50" />
Carousel View:
XAML:
<carousel:SfCarousel x:Name="carousel" Grid.Row="0" Offset="0" ItemsSource="{Binding ImageCollection}" ItemTemplate="{StaticResource itemTemplate}"
ItemHeight="200" SelectionChanged="Carousel_SelectionChanged"
ItemWidth="200" />
<StackLayout Grid.Row="1" HorizontalOptions="Center" x:Name="rotatorThumb" BackgroundColor="Transparent" Orientation="Horizontal"/>
C#:
public MainPage()
{
InitializeComponent();
Command command = new Command((object thumb) =>
{
var thumbView = thumb as DotsView;
if (thumbView != null)
{
((rotatorThumb.Children[carousel.SelectedIndex] as DotsView).BindingContext as CarouselModel).
ThumbBorder = Color.LightGray;
carousel.SelectedIndex = thumbView.Index;
(thumbView.BindingContext as CarouselModel).ThumbBorder = Color.Red;
}
});
for (var i = 0; i < ImageCollection.Count; i++)
{
var itemView = new DotsView() { BindingContext = ImageCollection[i], Index = i };
if (carousel.SelectedIndex == i)
(itemView.BindingContext as CarouselModel).ThumbBorder = Color.Red;
TapGestureRecognizer thumbTap = new TapGestureRecognizer();
thumbTap.Command = command;
itemView.GestureRecognizers.Add(thumbTap);
thumbTap.CommandParameter = itemView;
rotatorThumb.Children.Add(itemView);
}
}
Output:
Sample
I have some XAML code that looks like this. It names four grids and then in the back end my C# does something based on the values. As I can not have duplicate names I created four names.
But I would like to simplify the back-end code so is it possible that I could bind back the value of the height from my XAML > ViewModel and then check that value in my C#
<Grid IsVisible="{Binding AVisible}" VerticalOptions="FillAndExpand">
<Grid x:Name="aWords" VerticalOptions="FillAndExpand" >
<Frame VerticalOptions="FillAndExpand">
<Frame.Content>
<Grid x:Name="aArea" VerticalOptions="CenterAndExpand">
<Label Text="{Binding Detail}"
</Grid>
</Frame.Content>
</Frame>
</Grid>
</Grid>
<Grid IsVisible="{Binding BVisible}" VerticalOptions="FillAndExpand">
<Grid x:Name="bWords" VerticalOptions="FillAndExpand" >
<Frame VerticalOptions="FillAndExpand">
<Frame.Content>
<Grid x:Name="bArea" VerticalOptions="CenterAndExpand">
<Label Text="{Binding Detail}"
</Grid>
</Frame.Content>
</Frame>
</Grid>
</Grid>
and in C#
var a = aWords.Height;
var b = aArea.Height;
if (b > a) doSomething();
var c = aWords.Height;
var d = aArea.Height;
if (d > c) doSomething();
What I would like to do is like this:
if (vm.AreaHeight > vm.WordsHeight) doSomething();
The way you can pass those values to your ViewModel is by using the binding context of your page for interacting with the ViewModel methods and properties.
First, create a couple of properties in your ViewModel to hold the Height of your objects: i.e.:
public int AreaHeight { get; set; }
public int WordsHeight { get; set; }
Then, on your page override the event: OnAppearing() in order to get the Height from your grids using the x:Name attribute, then you will call from your ViewModel the method: doSomething() passing the obtained values as parameters. i.e.:
protected override void OnAppearing()
{
base.OnAppearing();
// Get your height from your grids here ...
// int gridAHeight = aWords.Height;
// int gridBHeight = aArea.Height;
(BindingContext as YourViewModel).doSomething(gridAHeight, gridBHeight);
}
Finally, in your ViewModel, implement the method doSomething() and catch the parameters obtained from the view page by assigning them to your previously created properties. i.e:
public void DoSomething(int gridAHeight, int gridBHeight)
{
AreaHeight = gridAHeight;
WordsHeight = gridBHeight;
if (AreaHeight < WordsHeight)
{
// place your logic here...
}
}
That is all you need. It is just basic MVVM pattern.
Passing heights or binding heights to a ViewModel violates MVVM pattern. ViewModels shouldn't know anything about a specific view.
I would suggest refactoring your view and finding a different layout depending on the specifics needed for the page. A possible solution would be to use a ListView with labels and restricting the content in the ObservableCollection so you don't have duplicates.
If you have to have it set up that way, I would suggest using MaxLines and/or LineBreakMode to restrict the height of your Labels.
Issues with doing logic based on height:
FontSize could be different based on AccessibilitySettings specified in the users phone
Rotating the screen will change how much of the label is wrapped due to the label expanding. This would force you to do a recalculation again.
Basically, you can't. There isn't the height in XAML, but rather HeightRequest. The layout is a complex thing and the height may not end up being what is your HeightRequest, it is just a suggested value. So you can only change that suggested value from the view model that is not the problem, but that is not what you asked for.
I'm trying to make a ListView which has a DataTemplateSelector on ItemTemplate. Basically, it looks like an expandable cell, which shows more info when the item is selected.
My code on the DataTemplateSelector:
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
return ((ItemAjuda)item).Selecionado ? ExpandedTemplate : SimpleTemplate;
}
and this is my code on the view:
<ListView
x:Name = "listView"
SeparatorVisibility = "None"
RowHeight = "-1"
HasUnevenRows="true"
ItemsSource="{Binding Itens}"
SelectedItem="{Binding ItemSelecionado, Mode=TwoWay}"
BackgroundColor = "{StaticResource DefaultBackground}">
<ListView.ItemTemplate>
<local:AjudaTemplateSelector/>
</ListView.ItemTemplate>
</ListView>
It works perfectly on Android, but on IOS the cell got a strange behavior. The row height changes but it doesn't looks like an uneven row :(
Ps: I've already tried to ForceUpdateSize() on the cell, but unfortunately it doesn't change anything (maybe because of the DataTemplateSelector?)