Display elements in the center of the CollectionView - xaml

I have a CollectionView with 5 items per row. In all I have 12 elements and I would like to insert the last 2 elements of the third row in the center, and not in the left side.
<CollectionView
x:Name="CollectionIconDiary"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="5" VerticalItemSpacing="3" HorizontalItemSpacing="3"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
//OTHER CODE
Example

In all I have 12 elements and I would like to insert the last 2 elements of the third row in the center, and not in the left side.
There is one simple workaround, you can use one CollectionView and one StackLayout to display all datas. Using the StackLayout to display the last items.
<StackLayout Spacing="0">
<CollectionView
x:Name="CollectionIconDiary"
HeightRequest="240"
ItemsSource="{Binding items}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout
HorizontalItemSpacing="3"
Orientation="Vertical"
Span="5"
VerticalItemSpacing="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Image Source="{Binding imagesource}" />
<Label Text="{Binding name}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<StackLayout
x:Name="list"
BindableLayout.ItemsSource="{Binding lastitem}"
HorizontalOptions="CenterAndExpand"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout>
<Image HeightRequest="80" Source="{Binding imagesource}" />
<Label Text="{Binding name}" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
public partial class Page6 : ContentPage
{
public itemviewmodel1 source { get; set; }
public Page6()
{
InitializeComponent();
source = new itemviewmodel1();
this.BindingContext = source;
}
}
public class itemviewmodel1
{
public ObservableCollection<itemmodel> items { get; set; }
public ObservableCollection<itemmodel> lastitem { get; set; }
public itemviewmodel1()
{
items = new ObservableCollection<itemmodel>();
lastitem = new ObservableCollection<itemmodel>();
for(int i=0;i<13;i++)
{
itemmodel model = new itemmodel();
model.name = "image "+i;
model.imagesource = "c1.png";
items.Add(model);
}
func();
}
private void func()
{
int value = (items.Count)% 5;
int count = items.Count;
if(value>0)
{
for(int i=count-1;i>=count-value;i--)
{
lastitem.Add(items[i]);
items.Remove(items[i]);
}
}
}
}
public class itemmodel
{
public string imagesource { get; set; }
public string name { get; set; }
}
You need to set HeightRequest for CollectionView, to prevent the large space between CollectionView and StackLayout, you can take a look the following thread to adjust HeightRequest according to items.
How to change CollectionView size according sizes of its items
You also need to set Image HeightRequest to make StackLayout item size.
The screenshot:

Related

Saving with SQL

Hi all I have a page that I'm trying to ADD to a list and save it with viewmodel and SQL. but my list is empty Can you tell me where am I wrong??
on my Xaml (Page1):
<Entry Margin="5" Text="{Binding Xname}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" FontSize="16" x:Name="entry1" Placeholder="Enter X-rays (6 MV)" PlaceholderColor="White" BackgroundColor="Gray"/>
<Button Grid.Column="0"
Grid.Row="2"
FontAttributes="Bold"
Command="{Binding AddXrayCommand}"
Text="Add" />
<ListView Grid.Row="3" Grid.ColumnSpan="2" HasUnevenRows="True" Margin="40"
ItemsSource="{Binding energyX}" SelectedItem="{Binding selecteditemX}"
HorizontalOptions="Start" WidthRequest="150" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="5"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" BackgroundColor="White" Text="{Binding xray}" FontSize="Large" HorizontalTextAlignment="Center" TextColor="Black" WidthRequest="35"/>
<Frame Grid.Row="1" BackgroundColor="Gray"></Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
my Model (Energypage):
public class EnergyX
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string xray { get; set; }
}
And on my EnergyViewModel:
public class EnergyViewModel
{
public SQLiteConnection conn;
public ObservableCollection<EnergyX> energyX { get; set; } = new ObservableCollection<EnergyX>();
public string Xname { get; set; }
public ICommand AddXrayCommand => new Command(AddX);
public SQLiteConnection GetSQLiteConnection()
{
var fileName = "Energys.db";
var documentPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
var path = Path.Combine(documentPath, fileName);
var connection = new SQLiteConnection(path);
return connection;
}
public void AddX()
{
if (Xname != null && Xname.Length > 0)
{
EnergyX XX = new EnergyX();
XX.xray = Xname;
conn = GetSQLiteConnection();
conn.CreateTable<EnergyX>();
var dataX = conn.Table<EnergyX>();
var resultX = conn.Insert(XX);
energyX = new ObservableCollection<EnergyX>(conn.Table<EnergyX>().ToList());
}
}
}
}
I did bond my page1 to Energyviewmodel, and it works find when I didn't use SQL service, I think my SQL has problem...
I have debug the viewmodel and program run to the end of the Viewmodel but my xaml page list is empty.
this line creates a completely new instance of energyX when your ListView is bound to the old instance
energyX = new ObservableCollection(conn.Table().ToList());
there are two ways you could fix this
option 1, use INotifyPropertyChanged and raise a PropertyChanged event in the setter of energyX
option 2, don't create a new instance of energyX. Instead just add the new item to the existing instance
energyX.Add(XX);

Xamarin Forms multiply CollectionViews Horizontal and Vertical

I try to use a CollectionView in a other CollectionView like this. My problem is there wont display me the second CV like there dont have a source.
I guess the BindingContex can be the problem but i dont have a idea how i fix it.
<CollectionView
SelectionMode="Single"
ItemsSource="{Binding MainInfo}">
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="10" Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding UserName}"/>
<!--Horizontal CV-->
<CollectionView
SelectionMode="Single"
ItemsSource="{Binding MainInfo}"
>
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="10" Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding ImageUrl}"/>
<Label Text="{Binding FullName}"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I guess the BindingContext can be the problem but i don't have a idea how i fix it.
In your case the ItemsSource and model should like the following
public class MyDetails
{
public string ImageUrl { get; set; }
public string FullName { get; set; }
}
public class MyObject
{
public string UserName { get; set; }
public ObservableCollection<MyDetails> MainInfo { get; set; }
}
public class MyViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<MyObject> MainInfo { get; set; }
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In addition , agree with #Jason . In your case if want to display an collection in cell, you could use Bindable Layouts instead of putting a CollectionView in another . In this way you just need to set the Viewmodel and model like the above code .

Xamarin ListView with auto fit row height

I need to co create listView that set row height depends of its content. Another problem is when is set HasUnevenRows="true" then it makes a lot of empty space under my components in listView.
<ListView HasUnevenRows="true">
...
</ListView>
You need to use BindableLayout
<StackLayout
BindableLayout.ItemsSource="{Binding Item}" Padding="30,10">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label
Text="{Binding .}"
TextColor="Black" />
</DataTemplate>
</BindableLayout.ItemTemplate>
Code Behind
public ObservableCollection<string> Item { get; set; } = new ObservableCollection<string>();
public MainPage()
{
InitializeComponent();
BindingContext = this;
for (int i = 0; i < 5; i++)
{
Item.Add($"Abc{i}");
}
}

How to bind image source to dynamic changable property?

I'm trzuying to show diferent images in ListView frames, depending of ServiceName in my model.
So here is my model:
public class IrrigNetModelItem
{
public int ID { get; set; }
public string Message { get; set; }
public DateTime Date { get; set; }
public string DateText { get; set; }
public int StationId { get; set; }
public string StationName { get; set; }
public float StationLongitude { get; set; }
public float StationLatitude { get; set; }
public int ServiceId { get; set; }
public string ServiceName { get; set; }
}
And here is part of ViewModel
public ObservableCollection<IrrigNetModelItem> IrrigNetCollection { get; set; } = new ObservableCollection<IrrigNetModelItem>();
public async void GetData()
{
base.OnAppearing();
dialog.Show();
var token = LocalData.Token;
var data = await IrrigNetService.GetServices(token, "en");
var irrigNetModel = new IrrigNetModelItem();
foreach (var d in data.items)
{
IrrigNetCollection.Add(d);
if (d.ServiceName == "irrigNET")
{
IrrigCounter++;
//FrameImage = "service_irrig_img.png";
//FrameHeaderColor = Color.FromHex("#33A8F7");
}
else
{
AlertCounter++;
//FrameImage = "alert.png";
//FrameHeaderColor = Color.FromHex("#2BB24B");
}
}
dialog.Hide();
}
For now, Service name could be "irrigNET" and "alertNET" and depending of that I want to set diferent image source in my ListView in View.
Here is View:
<ListView
ItemsSource="{Binding IrrigNetCollection}"
IsVisible="{Binding IsListVisible}"
ItemSelected="FrameList_ItemSelected"
HasUnevenRows="False"
x:Name="FrameList"
Grid.Row="2"
RowHeight="190"
Margin="0,0,0,20"
SeparatorVisibility="None"
HeightRequest="0">
<ListView.ItemTemplate Padding="0">
<DataTemplate>
<ViewCell>
<Frame
HasShadow="True"
Grid.ColumnSpan="5"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
BackgroundColor="#f4f4f4"
BorderColor="LightGray"
CornerRadius="10"
Margin="25,10,25,10"
Padding="0">
<Grid
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
IsEnabled="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="13"/>
<RowDefinition Height="35"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<BoxView Color="{Binding Path=BindingContext.FrameHeaderColor, Source={x:Reference FrameList}}"
Grid.Row="0"
Grid.ColumnSpan="6"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"/>
<Image Source="{Binding Path=BindingContext.FrameImage, Source={x:Reference FrameList}}"
Grid.Column="1"
Grid.Row="1"
Grid.RowSpan="3"
Margin="0,20,0,0"/>
<Image Source="{Binding Path=BindingContext.FrameIcon, Source={x:Reference FrameList}}"
Grid.Column="2"
Grid.Row="1"
HorizontalOptions="Start"
VerticalOptions="Center"
HeightRequest="17"
WidthRequest="17"/>
<Label
Text="{Binding StationName}"
VerticalTextAlignment="Start"
HorizontalTextAlignment="Start"
HorizontalOptions="Start"
VerticalOptions="Center"
Margin="3,3,0,0"
FontSize="18"
Grid.Column="3"
Grid.Row="1"
FontFamily="{StaticResource BalooBhai}"
TextColor="#262f41"/>
<Image Source="service_arrow.png"
Grid.Column="4"
Grid.Row="2"
VerticalOptions="Center"
HorizontalOptions="Center"
HeightRequest="18"
WidthRequest="18"/>
<Image Source="clock.png"
Grid.Column="2"
Grid.Row="3"
HorizontalOptions="Start"
VerticalOptions="Center"/>
<Label Text="{Binding DateText}"
FontSize="14"
FontFamily="{StaticResource SegoeUIB}"
Grid.Column="3"
Grid.Row="3"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Start"
Margin="3,0,0,0"
TextColor="#262f41"/>
<Label Text="{Binding Message}"
Grid.Column="3"
Grid.Row="2"
VerticalTextAlignment="Start"
HorizontalOptions="Center"
VerticalOptions="Center"
FontFamily="{StaticResource SegoeUI}" FontSize="13"
TextColor="#262f41"
Margin="0,0,10,0"/>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="40">
<StackLayout Orientation="Horizontal"
BackgroundColor="#3498DB"
VerticalOptions="FillAndExpand">
<Label Text="TeStIrAnjE"
TextColor="White"
VerticalOptions="Center" />
<Button Text="Edit"
TextColor="White"
FontSize="20"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
You'll se what I tried in Image tags, but for now any image ar not show.
This is proporties that I try to use in my ViewModel:
//public string FrameIrrigImage { get; set; } //= "service_irrig_img.png";
//public Color FrameIrrigHeaderColor { get; set; } //= Color.FromHex("#33A8F7");
//public string FrameAlertImage { get; set; } //= "alert.png";
//public Color FrameAlertHeaderColor { get; set; } // = Color.FromHex("#2BB24B");
//public string typeNet { get; set; }
//public Color typeNETcolortext { get; set; }
//public Color allertNETcolortext { get; set; }
This is what I want to achive:
But this is what I get:
(As you can see BoxView heder is also diferent color blue/color but I will get it how to change it on Image example)
Also I tried to achive it using properties:
public string FrameImage { get; set; }
public Color FrameHeaderColor { get; set; }
Set them values in for loop and Bind them in xaml, but then it's all set (image and color) as for the last element in ListView
As Jason said, there are two way to do this, one is use IValueConverter by ServiceName, another is add FrameImage in model, then change FrameImage by serviceName.
I do one simple that you can take a look,if you add FrameImage in model, please implement INotifyPropertyChanged interface.
<ContentPage
x:Class="App4.Page9"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:App4">
<ContentPage.Resources>
<converter:convert1 x:Key="converterimage" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding model2s}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding firstname}" />
<Image Source="{Binding imagepath}" />
<Image Source="{Binding Id, Converter={StaticResource converterimage}}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
public partial class Page9 : ContentPage
{
public ObservableCollection<model2> model2s { get; set; }
public Page9 ()
{
InitializeComponent ();
model2s = new ObservableCollection<model2>()
{
new model2(){Id=1,firstname="cherry"},
new model2(){Id=2,firstname="mattew"},
new model2(){Id=3,firstname="annine"},
new model2(){Id=4,firstname="barry"}
};
foreach(model2 m in model2s)
{
if(m.Id%2==0)
{
m.imagepath = "a1.jpg";
}
else
{
m.imagepath = "a2.jpg";
}
}
this.BindingContext = this;
}
}
public class model2:ViewModelBase
{
public int Id { get; set; }
public string firstname { get; set; }
private string _imagepath;
public string imagepath
{
get { return _imagepath; }
set
{
_imagepath = value;
RaisePropertyChanged("imagepath");
}
}
}
Converter:
class convert1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int id = (int)value;
if(id%2==0)
{
return "a1.jpg";
}
else
{
return "a2.jpg";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

How to associate view with viewmodel or multiple DataTemplates for ViewModel?

Given I have a GridView and I want to navigate to a different page by clicking each item.
How can navigate to a view associated to the viewmodel?
In WPF there is a way to set multiple Datatemplates for the viewmodel.
<TabControl Grid.Row="1" Margin="0" ItemsSource="{Binding Tabs}" SelectedIndex="0" SelectedItem="{Binding SelectedTab}">
<TabControl.Resources>
<DataTemplate DataType="{x:Type dashboard:DashboardViewModel}">
<dashboard:DashboardView/>
</DataTemplate>
<DataTemplate DataType="{x:Type controls:ExchangeViewModel}">
<controls:ExchangeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type request:RequestViewModel}">
<request:RequestView/>
</DataTemplate>
<DataTemplate DataType="{x:Type addresses:AddressViewModel}">
<addresses:AddressView/>
</DataTemplate>
<DataTemplate DataType="{x:Type settings:ExchangeSettingsViewModel}">
<settings:ExchangeSettingsView/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="vm:ViewModelBase">
<TextBlock Text="{Binding Header}" FontSize="14"></TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
This is what I tried in UWP in my particular case:
<Frame Grid.Row="1" DataContext="{x:Bind ViewModel.Value}">
<Frame.Resources>
<DataTemplate x:DataType="viewModels:ExampleViewModel1">
<views:ExampleView1></views:ExampleView1>
</DataTemplate>
<DataTemplate x:DataType="viewModels:ExampleViewModel2">
<views:ExampleView2></views:ExampleView2>
</DataTemplate>
</Frame.Resources>
</Frame>
The Frame is part of a page and I want to show the corresponding view based on the Value of the ViewModel.
Visual Studio tells me DataTemplate has to have a key attribute, but even then it doesn't work as it would in WPF, since it's not creating the view.
I know DataType was replaced with x:DataType and x:Type seems to be gone. Is there a way to achieve similar results?
In WPF, the DataType is a dependency property which can be retrieved in runtime.
In UWP, the x:DataType is compile-time property, you cannot get the value in runtime.
I created a simple demo about how to map the datatype and data template in UWP through DataTemplateSelector.
DataTemplateSelector:
namespace UWPApp
{
public class Template
{
public string DataType { get; set; }
public DataTemplate DataTemplate { get; set; }
}
public class TemplateCollection2 : System.Collections.ObjectModel.Collection<Template>
{
}
public class MyDataTemplateSelector : DataTemplateSelector
{
public TemplateCollection2 Templates { get; set; }
private IList<Template> _templateCache { get; set; }
public MyDataTemplateSelector()
{
}
private void InitTemplateCollection()
{
_templateCache = Templates.ToList();
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (_templateCache == null)
{
InitTemplateCollection();
}
if(item != null)
{
var dataType = item.GetType().ToString();
var match = _templateCache.Where(m => m.DataType == dataType).FirstOrDefault();
if(match != null)
{
return match.DataTemplate;
}
}
return base.SelectTemplateCore(item, container);
}
}
}
ViewModel:
namespace UWPApp
{
public class ViewModel1
{
public string Text1 { get; set; }
}
public class ViewModel2
{
public string Text2 { get; set; }
}
}
XAML:
<Grid
x:Name="container"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<local:TemplateCollection2 x:Key="templates">
<local:Template DataType="UWPApp.ViewModel1">
<local:Template.DataTemplate>
<DataTemplate x:DataType="local:ViewModel1">
<StackPanel>
<TextBlock Text="{Binding Text1}"></TextBlock>
<TextBlock Text="From template1"></TextBlock>
</StackPanel>
</DataTemplate>
</local:Template.DataTemplate>
</local:Template>
<local:Template DataType="UWPApp.ViewModel2">
<local:Template.DataTemplate>
<DataTemplate x:DataType="local:ViewModel2">
<StackPanel>
<TextBlock Text="{Binding Text2}"></TextBlock>
<TextBlock Text="From template2"></TextBlock>
</StackPanel>
</DataTemplate>
</local:Template.DataTemplate>
</local:Template>
</local:TemplateCollection2>
<local:MyDataTemplateSelector
x:Key="myDataTemplateSelector" Templates="{StaticResource templates}">
</local:MyDataTemplateSelector>
</Grid.Resources>
<StackPanel>
<Button x:Name="button" Click="button_Click">Click Me</Button>
<ContentControl x:Name="stage" ContentTemplateSelector="{StaticResource myDataTemplateSelector}">
</ContentControl>
</StackPanel>
</Grid>