Objects in Binding not displayed in the ListView - c#

I have a ListView with items in Binding via the ItemsSource property. I cannot understand why the DataTemplate data is not displayed. They are still downloaded correctly from the internet but are not displayed. I've tried both inserting the ItemsSource property from c # and xaml, but the result doesn't change
xaml
<yummy:PancakeView x:Name="ViewFrasi" IsVisible="False" Grid.Row="1" CornerRadius="30,30,0,0" BackgroundColor="White" VerticalOptions="FillAndExpand">
<yummy:PancakeView.Border>
<yummy:Border Color="Blue" Thickness="4"/>
</yummy:PancakeView.Border>
<Grid Margin="15">
<ListView x:Name="Frasi"
ItemsSource="{Binding FrasiJsonOnline1}">
<ListView.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<BoxView CornerRadius="100" Grid.Row="0" HorizontalOptions="End" WidthRequest="40">
<BoxView.Background>
<LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="LightBlue" Offset="1.0" />
</LinearGradientBrush>
</BoxView.Background>
</BoxView>
<Image Source="checked.png" Grid.Row="0" HorizontalOptions="End" HeightRequest="20" Margin="0,0,10,0"/>
<Button x:Name="BtSave" IsVisible="False" Clicked="BtSave_Clicked" BackgroundColor="Transparent" Grid.Row="0" HorizontalOptions="End" HeightRequest="25"/>
<ImageButton Source="close.png" Grid.Row="0" HorizontalOptions="Start" HeightRequest="20" Margin="5,0,0,0" Clicked="Close_Clicked" BackgroundColor="Transparent"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<ImageButton Grid.Row="0" Grid.Column="0" Source="IconCopy.png" Clicked="CopyClipboard_Clicked"/>
<Label Grid.Row="0" Grid.Column="1" Text="Test" TextColor="Black" FontSize="15"/>
</Grid>
</ScrollView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</yummy:PancakeView>
c#
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = e.CurrentSelection;
ClassCategory model = e.CurrentSelection.FirstOrDefault() as ClassCategory;
WebClient clientw = new WebClient();
clientw.Credentials = new NetworkCredential("xxxxxxx", "xxxxxx");
string Frasi1 = "ftp://epiz_27426656#ftpupload.net/htdocs/" + model.Titolo + ".json";
string contents1 = await clientw.DownloadStringTaskAsync(Frasi1);
ObservableCollection<FraseClass> FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
ViewFrasi.IsVisible = true;
ViewFrasi.TranslationY = 600;
ViewFrasi.TranslateTo(0, 0, 500, Easing.SinInOut);
}

You are using a local variable, make it a public property outside the method and change its value inside the method as shown here:
public ObservableCollection<FraseClass> FrasiJsonOnline1 { get; set; }
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = e.CurrentSelection;
ClassCategory model = e.CurrentSelection.FirstOrDefault() as ClassCategory;
WebClient clientw = new WebClient();
clientw.Credentials = new NetworkCredential("xxxxxxx", "xxxxxx");
string Frasi1 = "ftp://epiz_27426656#ftpupload.net/htdocs/" + model.Titolo + ".json";
string contents1 = await clientw.DownloadStringTaskAsync(Frasi1);
FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
ViewFrasi.IsVisible = true;
ViewFrasi.TranslationY = 600;
ViewFrasi.TranslateTo(0, 0, 500, Easing.SinInOut);
}

When you bind the ListView's ItemsSource to FrasiJsonOnline1.
That means you are binding to the BindingContext.FrasiJsonOnline1.
ItemsSource="{Binding BindingContext.FrasiJsonOnline1}".
So FrasiJsonOnline1 should be a property of your BindingContext. For example, if you BindingContext is myViewModel, FrasiJsonOnline1 should be a property of myViewModel, then the binding will success:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new myViewModel();
}
}
public class myViewModel
{
public ObservableCollection<FraseClass> FrasiJsonOnline1 { get; set; }
public myViewModel()
{
FrasiJsonOnline1 = new ObservableCollection<FraseClass>();
//...your codes
}
}
If you define the FrasiJsonOnline1 like the way you do:
public class myViewModel
{
public myViewModel()
{
}
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ObservableCollection<FraseClass> FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
}
}
FrasiJsonOnline1 is defined inside the method and it is a local variable. The BindingContext(myViewModel) does not have a property called FrasiJsonOnline1 and the binding won't work.

Related

How to change Label color on Entry GotFocus from CollectionView

Is there a way to bind the state of FontColor of Label in Xamarin so that it highlights the Label when Entry(textbox) gets focus?
<CollectionView x:Name="documentsListView" ItemsSource="{Binding DocumentsList}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0"
Margin="0,10,0,0"
Text="{Binding Name}" FontSize="Body"/>
<Entry Grid.Column="1" Grid.RowSpan="1"
IsPassword="False"
Keyboard="Numeric"
Placeholder="{Binding Count}"
Text="{Binding Count, Mode=OneWayToSource}"
Unfocused="{Binding OnTextboxLostFocus}"
Focused="{Binding OnTextboxGotFocus}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I need to highlight the element which is going to be changed when the user enters data to the Entry(textbox), and because the space between elements in CollectionView should be less so that I can show as much data per scroll as possible it might confuse the user which element exactly he is changing. I thought about passing a label as a parameter to the Events but could not find out how to bind the label.
Bind the textColor of label to a property in model and update that textColor when the entry focused/unfocused.
Here is an example I use:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new DncMvvmPageModel();
}
}
public class DncMvvmPageModel
{
public ObservableCollection<Document> DocumentsList { get; set; }
public Command OnTextboxLostFocus { get; }
public Command OnTextboxGotFocus { get; }
public DncMvvmPageModel()
{
OnTextboxLostFocus = new Command(OnTextboxLostFocusMethod);
OnTextboxGotFocus = new Command(OnTextboxGotFocusMethod);
DocumentsList = new ObservableCollection<Document>();
DocumentsList.Add(new Document() {TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
}
public void OnTextboxLostFocusMethod(object sender) {
FocusEventArgs args = sender as FocusEventArgs;
Entry entry = args.VisualElement as Entry;
Document docu = entry.BindingContext as Document;
docu.TextColor = Color.Red;
}
public void OnTextboxGotFocusMethod(object sender)
{
FocusEventArgs args = sender as FocusEventArgs;
Entry entry = args.VisualElement as Entry;
Document docu = entry.BindingContext as Document;
docu.TextColor = Color.Blue;
}
}
public class Document : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
Color textColor;
public Color TextColor
{
set
{
if (textColor != value)
{
textColor = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TextColor"));
}
}
}
get
{
return textColor;
}
}
}
And in Xaml:
<CollectionView x:Name="documentsListView" ItemsSource="{Binding DocumentsList}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0"
Margin="0,10,0,0"
Text="Name" FontSize="Body" TextColor="{Binding TextColor}"/>
<Entry Grid.Column="1" Grid.RowSpan="1"
IsPassword="False"
Keyboard="Numeric"
Placeholder="placeholder"
Text="Count">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior EventName="Focused"
Command="{Binding BindingContext.OnTextboxGotFocus, Source={x:Reference MyPage}}" />
<behaviors:EventToCommandBehavior EventName="Unfocused"
Command="{Binding BindingContext.OnTextboxLostFocus, Source={x:Reference MyPage}}" />
</Entry.Behaviors>
</Entry>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Result:
Feel free to ask me any question if you have:).

CollectionView, DataBinding, MVVM

Hi everyone,
I am trying to implement MVVM in my application but i am failing to load data.
I have a main page and it works fine, but once I want to navigate to the detail of my Ad and then the detail page is blank. There are my controls so i know that the pages is loaded just my data are missing. I cant seem to figure out where i have made the mistake. I am new to coding so i sometimes tend to make very silly mistakes. I been trying to solve this for past few hours otherwise would not post it over here. Thank you of any opinion.
My HomePage
<Grid Grid.Row="1" Margin="0,25,0,0">
<CollectionView x:Name="ads"
ItemsSource="{Binding AdLogEntries}"
ItemTemplate="{StaticResource HomePageTemplate}"
SelectionMode="Single"
SelectedItem="{Binding SelectedAd, Mode=TwoWay}"
SelectionChanged="CollectionView_SelectionChanged"
Margin="12,0">
</CollectionView>
</Grid>
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
BindingContext = new HomePage ViewModel();
}
async void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedAd = (AdLogEntry)e.CurrentSelection.FirstOrDefault();
if (selectedAd != null)
{
await Navigation.PushAsync(new AdDetail(selectedAd));
}
}
}
namespace Baltazar.ViewModel
{
public class HomePageViewModel : BaseViewModel
{
ObservableCollection<AdLogEntry> _adLogEntries;
public ObservableCollection<AdLogEntry> AdLogEntries { get => _adLogEntries; set { _adLogEntries = value; OnPropertyChanged(); } }
public AdLogEntry selectedAd;
public HomePageViewModel()
{
AdLogEntries = new ObservableCollection<AdLogEntry>()
{
new AdLogEntry (){Id = 0, Image = "cat.jpg", Name = "Kocka" , Description = "seda kocka na prodej, mayliva, hrava, spolecenska " ,Price = 120, },
new AdLogEntry (){Id = 1, Image = "cat2.jpg", Name = "Kocka", Description = "seda kocka na prodej, mayliva, hrava, spolecenska " ,Price = 120, },
new AdLogEntry (){Id = 2, Image = "bobtailjpg.jpg", Name = "Kocka",Description = "seda kocka na prodej, mayliva, hrava, spolecenska ", Price = 120, },
};
}
}
}
//Detail
public class AdDetailViewModel : BaseViewModel
{
AdLogEntry _adDetail;
public AdLogEntry AdDetail { get => _adDetail;set { _adDetail = value;OnPropertyChanged(); } }
public AdDetailViewModel(AdLogEntry adDetail)
{
AdDetail = adDetail;
}
}
public partial class AdDetail : ContentPage
{
//private AdLogEntry selectedAd;
public AdDetail(AdLogEntry adDetail)
{
InitializeComponent();
BindingContext = new AdDetailViewModel(adDetail);
}
}
//BaseViewModel
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected BaseViewModel()
{
}
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ffimageloading:CachedImage
x:Name="HeaderView"
Grid.Row="0"
Grid.RowSpan="2"
Aspect="AspectFill"
Source="{Binding AdLogEntries.Image}"/>
<controls:Parallax Control
x:Name="Parallax"
Grid.Row="0"
Grid.RowSpan="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
<yummy:Pancake View
Grid.Row="1"
CornerRadius="24, 24, 0, 0"
BackgroundColor="{StaticResource BackgroundColor}"
Margin="0, 20, 0, 0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Animal NAME -->
<Label
Grid.Row="0"
Text="{Binding AdLogEntries.Name}"
/>
<!-- QUANTITY -->
<Grid
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Text="{Binding AdLogEntries.Price, StringFormat='{0:C0}'}"
/>
</Grid>
<!-- ABOUT -->
<Label
Grid.Row="2"
Text="Popis"
/>
<!-- DESCRIPTION -->
<Label
Grid.Row="3"
Text="{Binding AdLogEntries.Description}"
/>
<Grid
Grid.Row="4"
Margin="0, 12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- BUY NOW BUTTON -->
<yummy:PancakeView
Grid.Column="1"
HeightRequest="48"
CornerRadius="24, 0, 24, 0"
Background Color="Accent"
Margin="24, 0, 0, 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label
Grid.Column="1"
Text="Buy now"
/>
<Label
Grid.Column="2"
Text=">"
/>
</Grid>
</yummy:Pancake View>
</Grid>
</Grid>
</yummy:Pancake View>
</Grid>
</controls:Parallax Control>
</Grid>
</ContentPage.Content>
Your SelectedItem never changes because you are not trigger OnPropertyChange. You can change your view model logic like this:
private AdLogEntry selectedAd;
public AdLogEntry SelectedAd { get => selectedAd; set { selectedAd = value; OnPropertyChanged("SelectedAd"); } }
public ICommand AdSelectionChangedCommand => new Command(AdSelectionChanged);
private void AdSelectionChanged()
{
OnPropertyChanged("SelectedAd");
Application.Current.MainPage.Navigation.PushModalAsync(new DetailPage(SelectedAd));
}
And in your XAML:
<CollectionView x:Name="ads"
ItemsSource="{Binding AdLogEntries}"
SelectionMode="Single"
SelectionChangedCommand="{Binding AdSelectionChangedCommand}"
SelectedItem="{Binding SelectedAd, Mode=TwoWay}"
Margin="12,0">
Your detail page:
public DetailPage(AdLogEntry detail)
{
this.BindingContext = new AdDetailViewModel(adDetail);
InitializeComponent();
}
In Detail XAML:
<Label Text="{Binding AdDetail.Name}"/>
your BindingContext is AdDetailViewModel
BindingContext = new AdDetailViewModel(adDetail);
and your binding paths are like
<Label Grid.Row="3" Text="{Binding AdLogEntries.Description}"
AdDetailViewModel does not have a AdLogEntries property. It does have a AdDetail, so presumably you should use this as a binding path
<Label Grid.Row="3" Text="{Binding AdDetail.Description}"
The problem is in your detail page XAML. To be precise, the problem is in your XAML Binding Expressions. All the bindings in the detail page has wrong path set. For example, for image control it should be Source="{Binding AdDetail.Image}" instead of Source="{Binding AdLogEntries.Image}". It happens all the time and you catch these kind of errors in the output log when you debug the application.

How to databind a class object to a ViewCell?

I need to databind a class object to a custom ViewCell. I used the {Binding propName} for the fields on the controls inside the ViewCell. I'm not sure if I am adding the ViewCell correctly since it shows as [AppName.GridView] and does not show the control I built. What is the correct way to bind this control with the custom class?
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Height="50">
<Frame HeightRequest="50" Padding="0" Margin="1" BorderColor="blue" CornerRadius="4" BackgroundColor="lightBlue">
<Grid BindingContext="Binding local:di" >
<Grid.RowDefinitions >
<RowDefinition Height="34"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="Transparent" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Button x:Name="btnItem" Text="{Binding itemName}" Clicked="BtnClicked" Padding="0" Grid.Column="1" FontSize="20" />
<Label x:Name="lbAmt" Text="{Binding amt}" Grid.Column="2" Margin="2" VerticalTextAlignment="Center" HorizontalTextAlignment="End" FontSize="20" BackgroundColor="lightBlue" />
<Label x:Name="lbType" Text="{Binding amtType}" Grid.Column="3" Margin="2" VerticalTextAlignment="Center" FontSize="20" BackgroundColor="lightBlue" />
</Grid>
...
</Grid>
</Frame>
The code to add the object.
DItem di = new DItem() { itemName = "someName", amt = 1, amtType = xf };
GridItems gi = new GridItems() { di = di };
ObservableCollection<GridItems> lv = new ObservableCollection<GridItems>();
lv.Add(gi);
lvItems.ItemsSource = lv;
If you want to achieve the databinding with listview(MVVM),
Here is running screenshot.
First of all, you can create a model DItem.I achieve the INotifyPropertyChanged interface, layout will changed when the value was changed
public class DItem: INotifyPropertyChanged
{
string _itemName;
public string itemName
{
set
{
if (_itemName != value)
{
_itemName = value;
OnPropertyChanged("itemName");
}
}
get
{
return _itemName;
}
}
int _amt;
public int amt
{
set
{
if (_amt != value)
{
_amt = value;
OnPropertyChanged("amt");
}
}
get
{
return _amt;
}
}
string _amtType;
public string amtType
{
set
{
if (_amtType != value)
{
_amtType = value;
OnPropertyChanged("amtType");
}
}
get
{
return _amtType;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then, achieve the ModelView.DItemViewModel, We do not need create GridItems just add the DItem to the ObservableCollection<DItem>
public class DItemViewModel
{
public ObservableCollection<DItem> lv { get; set; }
public DItemViewModel()
{
DItem di = new DItem() { itemName = "someName", amt = 1, amtType = "xf" };
lv = new ObservableCollection<DItem>();
lv.Add(di);
}
}
Here is layout of listview. add the ItemsSource="{Binding lv}" for listview.
<ListView ItemsSource="{Binding lv}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame HeightRequest="50" Padding="0" Margin="1" BorderColor="blue" CornerRadius="4" BackgroundColor="lightBlue">
<Grid >
<Grid.RowDefinitions >
<RowDefinition Height="34"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="Transparent" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Button x:Name="btnItem" Text="{Binding itemName}" Clicked="btnItem_Clicked" Padding="0" Grid.Column="1" FontSize="20" />
<Label x:Name="lbAmt" Text="{Binding amt}" Grid.Column="2" Margin="2" VerticalTextAlignment="Center" HorizontalTextAlignment="End" FontSize="20" BackgroundColor="lightBlue" />
<Label x:Name="lbType" Text="{Binding amtType}" Grid.Column="3" Margin="2" VerticalTextAlignment="Center" FontSize="20" BackgroundColor="lightBlue" />
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is background code for the listview.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new DItemViewModel();
}
private void btnItem_Clicked(object sender, EventArgs e)
{
}
}
Here are helpful articles about MVVM in Xamarin forms.
https://almirvuk.blogspot.com/2017/02/xamarinforms-listview-simple-mvvm.html
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm

Cannot change the VirtualizationMode attached property on an ItemsControl after Measure is called on the ItemsHost panel

When i try to set VirtualizationMode on my ListView to Recycling I get the error from the title:
Cannot change the VirtualizationMode attached property on an
ItemsControl after Measure is called on the ItemsHost panel.
I am trying to set the attached property programmatically but when I try to define the VirtualizationMode in XAML designer throws the same error from the title. Anyone had similar problems like this?
My view in XAML is:
<Window x:Class="FinalVirtualizationApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FinalVirtualizationApp"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="800"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GroupBox Grid.Row="0" Header="UI virtualization options">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Content="UI virtualization" VerticalAlignment="Center" IsChecked="{Binding IsUIVirtualization}"/>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Container Recycling:" VerticalAlignment="Center"/>
<ComboBox Grid.Column="1" VerticalAlignment="Center" SelectedValue="{Binding ContainerRecyclingType}" SelectedValuePath="Content">
<ComboBoxItem>Recycling</ComboBoxItem>
<ComboBoxItem>Standard</ComboBoxItem>
</ComboBox>
</Grid>
<CheckBox Content="Deferred scrolling" Grid.Row="1" VerticalAlignment="Center" IsChecked="{Binding IsDeferredScrolling}">
</CheckBox>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="74*"/>
<ColumnDefinition Width="253*"/>
</Grid.ColumnDefinitions>
<Label Content="Scroll unit" VerticalAlignment="Center" Margin="0,0,0,1"/>
<ComboBox Grid.Column="1" VerticalAlignment="Center" SelectedValue="{Binding ScrollUnitType}" SelectedValuePath="Content" Grid.ColumnSpan="2" Margin="0,2,0,3">
<ComboBoxItem>Item</ComboBoxItem>
<ComboBoxItem>Pixel</ComboBoxItem>
</ComboBox>
</Grid>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Data virtualization">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Header="ItemsProvider">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Label Content="Number of Items: "/>
<TextBox Width="60" Text="{Binding NumberOfItems}"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Label Content="Fetch delay(ms): "/>
<TextBox Width="60" Text="{Binding FetchDelay}"/>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Collection">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<TextBlock Text="Type:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<RadioButton x:Name="rbNormal" GroupName="rbGroup" Margin="5" Content="List(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="List"/>
<RadioButton x:Name="rbVirtualizing" GroupName="rbGroup" Margin="5" Content="VirtualizingList(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="VirtualizingList"/>
<RadioButton x:Name="rbAsync" GroupName="rbGroup" Margin="5" Content="AsyncVirtualizingList(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="AsyncVirtualizingList"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<TextBlock Text="Page size:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<TextBox x:Name="tbPageSize" Margin="5" Text="{Binding PageSize}" Width="60" VerticalAlignment="Center"/>
<TextBlock Text="Page timeout (s):" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<TextBox x:Name="tbPageTimeout" Margin="5" Text="{Binding PageTimeout}" Width="60" VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</GroupBox>
</Grid>
</GroupBox>
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBlock Text="Memory Usage:" Margin="5" VerticalAlignment="Center"/>
<TextBlock x:Name="tbMemory" Margin="5" Width="80" Text="{Binding MemoryUsage}" VerticalAlignment="Center"/>
<Button Content="Refresh" Margin="5" Width="100" VerticalAlignment="Center" Command="{Binding RefreshCommand}"/>
</StackPanel>
<ListView Grid.Row="3" Name="lvItems">
<ListView.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding DeviceName}">
<StackPanel>
</StackPanel>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
And i try to set the VirtualizationMode here:
public void SetUIVirtualizationOptions()
{
listView.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, IsUIVirtualization);
listView.SetValue(ScrollViewer.IsDeferredScrollingEnabledProperty, IsDeferredScrolling);
if(ContainerRecyclingType == "Recycling")
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
else
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Standard);
if (ScrollUnitType == "Item")
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Item);
else
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Pixel);
}
Edit: My Window code behind where i pass the listview to viewmodel is:
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(lvItems);
}
}
And the viewmodel part of the code (minus getters and setters for less space taken):
public class MainWindowViewModel : BindableBase
{
#region private fields
private ListView listView;
private bool isUIVirtualization;
private bool isDeferredScrolling;
private string containerRecyclingType;
private string scrollUnitType;
private int numberOfItems;
private int fetchDelay;
private string collectionType;
private int pageSize;
private int pageTimeout;
private string memoryUsage;
private DemoItemProvider itemsProvider;
#endregion
#region commands
public RelayCommand<string> CollectionTypeChangeCommand { get; set; }
public RelayCommand RefreshCommand { get; set; }
public RelayCommand<string> ContainerRecyclingTypeChangeCommand { get; set; }
public RelayCommand<string> ScrollUnitTypeChangeCommand { get; set; }
#endregion
public MainWindowViewModel(ListView lvItems)
{
this.listView = lvItems;
PageSize = 100;
PageTimeout = 30;
NumberOfItems = 1000000;
FetchDelay = 1000;
CollectionTypeChangeCommand = new RelayCommand<string>(CollectionTypeChangeFunc);
RefreshCommand = new RelayCommand(RefreshFunc);
ContainerRecyclingTypeChangeCommand = new RelayCommand<string>(ContainerRecyclingTypeChangeFunc);
ScrollUnitTypeChangeCommand = new RelayCommand<string>(ScrollUnitTypeChangeFunc);
// use a timer to periodically update the memory usage
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += timer_Tick;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
MemoryUsage = string.Format("{0:0.00} MB", GC.GetTotalMemory(true) / 1024.0 / 1024.0);
}
#region command methods
public void CollectionTypeChangeFunc(string type)
{
CollectionType = type;
}
public void RefreshFunc()
{
SetUIVirtualizationOptions();
itemsProvider = new DemoItemProvider(NumberOfItems, FetchDelay);
if (collectionType == "List")
{
listView.ItemsSource = new List<DataItem>(itemsProvider.FetchRange(0, itemsProvider.FetchCount()));
}
else if (collectionType == "VirtualizingList")
{
listView.ItemsSource = new VirtualizingCollection<DataItem>(itemsProvider, pageSize);
}
else if (collectionType == "AsyncVirtualizingList")
{
listView.ItemsSource = new AsyncVirtualizingCollection<DataItem>(itemsProvider, pageSize, pageTimeout * 1000);
}
}
public void ContainerRecyclingTypeChangeFunc(string type)
{
ContainerRecyclingType = type;
}
public void ScrollUnitTypeChangeFunc(string type)
{
ScrollUnitType = type;
}
public void SetUIVirtualizationOptions()
{
listView.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, IsUIVirtualization);
listView.SetValue(ScrollViewer.IsDeferredScrollingEnabledProperty, IsDeferredScrolling);
if(ContainerRecyclingType == "Recycling")
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
else
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Standard);
if (ScrollUnitType == "Item")
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Item);
else
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Pixel);
}
#endregion
}
The code source for VirtualizingStackPanel indicates that this property can only be set before initialization of the panel:
/// <summary>
/// Attached property for use on the ItemsControl that is the host for the items being
/// presented by this panel. Use this property to modify the virtualization mode.
///
/// Note that this property can only be set before the panel has been initialized
/// </summary>
public static readonly DependencyProperty VirtualizationModeProperty =
DependencyProperty.RegisterAttached("VirtualizationMode", typeof(VirtualizationMode), typeof(VirtualizingStackPanel),
new FrameworkPropertyMetadata(VirtualizationMode.Standard));
You can indeed check this behavior later in the file:
//
// Set up info on first measure
//
if (HasMeasured)
{
VirtualizationMode oldVirtualizationMode = InRecyclingMode ? VirtualizationMode.Recycling : VirtualizationMode.Standard;
if (oldVirtualizationMode != virtualizationMode)
{
throw new InvalidOperationException(SR.Get(SRID.CantSwitchVirtualizationModePostMeasure));
}
}
else
{
HasMeasured = true;
}
and there is no way (according to source code) to set this HasMeasured property back to False unless you destroy and recreate the ListView.
It is so as the message says:
Your not allowed to _change the VirtualizationMode attached property
on an ItemsControl after Measure is called on the ItemsHost panel.
This means, that if ListView is already shown virtualizing mechanism being used and you are not allowed to change it.
If you set Visibility of ListView in XAML to Collapsed and set it only later to the Visible, then you can set the VirtualizationMode in code behind to wished value, but only once(so you can't change it after ListView become visible)!
private void Button_Click(object sender, RoutedEventArgs e)
{
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
listView.Visibility = Visibility.Visible;
}
XAML:
<ListView x:Name="listView" ... Visibility="Collapsed">

Binding a list of object to grid

Trying to render a ListView with a grid into it. The grid contains two columns. The first one, with a button. The second one, with a Label.
Model contains two attributes. First one, a List of specific object. Second one, a string.
Finally, the label of the grid inside of listview will be binded to the one random attribute of list of object of the Model.
My best approach is:
<ListView x:Name="intervectionList"
ItemsSource="{Binding .}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="1"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Text="Play" Grid.Row="0" Grid.Column="0" Clicked="OnLogginClicked"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding random_attribute}"/>
<BoxView Color="Navy" HeightRequest="1" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CODE-BEHIND:
public Intervection()
{
InitializeComponent();
var obj= new Model();
this.BindingContext = prueba.List_of_object;
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseOnPropertyChange([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ViewModel()
{
_viewmodel = new ViewModel();
}
private ViewModel _viewmodel;
private string _attribute1;
public string Attribute1
{
get { return _attribute1; }
set { _attribute1= value; RaiseOnPropertyChange(); }
}
.............
}
public class Model
{
public List<obj> Intervencion;
public string attribute2;
// Helpers...
}
That is not rendering anything.
I tried successive approaches. Coming from Basic ListView with string, ListView of object,... and so on. The problem is coming from when I insert the grid.
After check Stackoverflow. I found this link Create Grid from Code-Behind, but this is not serving to my purpose because I can´t re-write the view model. (I, even, tried to coded it).
As usual, thanks mates.
I have added the VerticalOptions to the Listview and Grid.
I have created a viewmodel in which I have one property, the list of items.
The viewmodel also implements INotifyPropertyChanged.
You can read about INotifyPropertyChanged here.
The INotifyPropertyChanged interface is used to notify clients,
typically binding clients, that a property value has changed.
XAML :
<ListView ItemsSource="{Binding MyObservableCollection}" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5" VerticalOptions="Fill">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="1"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Text="Play" Grid.Row="0" Grid.Column="0"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding .}"/>
<BoxView Color="Navy" HeightRequest="1" Grid.Row="1"
Grid.Column="0" Grid.ColumnSpan="2"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel :
public class ListViewWithGridViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _myObservableCollection;
public ListViewWithGridViewModel()
{
MyObservableCollection = new ObservableCollection<string>(new List<string> { "abc", "xyz", "pqr", "aaa", "abc", "xyz", "pqr", "aaa", "abc", "xyz", "pqr", "aaa" });
}
public ObservableCollection<string> MyObservableCollection
{
get { return _myObservableCollection; }
set
{
_myObservableCollection = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
XAML.cs :
public partial class ListViewWithGrid : ContentPage
{
public ListViewWithGrid()
{
InitializeComponent();
BindingContext = new ListViewWithGridViewModel();
}
}
Finally I got it. The xaml needed for it is the next one (you can complet all code coming from the code´s question). Posted just in case you all want to use another approach. Proper answer (more elegant) is #Rohit´s answer.
<ContentPage.Resources>
<ResourceDictionary>
<Color x:FactoryMethod="FromHex" x:Key="fondoBlancoPalido">
<x:Arguments>
<x:String>#F2F2F2</x:String>
</x:Arguments>
</Color>
</ResourceDictionary>
</ContentPage.Resources>
<ListView x:Name="listView" ItemsSource="{Binding .}" BackgroundColor="{StaticResource fondoBlancoPalido}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
<RowDefinition Height="60"></RowDefinition>
<RowDefinition Height="10"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Clicked="OnStartClicked" Image="play.png" BackgroundColor="Transparent" HorizontalOptions="Center" Grid.RowSpan="2"/>
<Label Grid.Row="0" Grid.Column="1" Text="Hora de Inicio: " XAlign="Center" YAlign="Center" TextColor="Black" FontAttributes="Bold"/>
<Label Grid.Row="0" Grid.Column="2" Text="{ Binding attribute3 }" XAlign="Center" YAlign="Center" TextColor="Black"/>
<Label Grid.Row="1" Grid.Column="1" Text="Encargado de la Tarea: " XAlign="Center" YAlign="Center" TextColor="Black" FontAttributes="Bold"/>
<Label Grid.Row="1" Grid.Column="2" Text="{ Binding attribute4 }" XAlign="Center" YAlign="Center" TextColor="Black"/>
<BoxView Color="Navy" HeightRequest="2" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"/>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Categories

Resources