PROWAREtech
Xamarin: Tutorial - Page 08
ObservableCollection Class
ObservableCollection
is defined in System.Collections.ObjectModel
. It implements the
INotifyCollectionChanged
interface, which is in System.Collections.Specialized
.
ObservableCollection
executes a CollectionChanged
event any time any of its data
changes. ListView
checks for this event to know when it must redraw itself.
Consider the following code.
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HelloXamarinForms"
x:Class="HelloXamarinForms.MainPageXaml">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="10, 20, 10, 0" Android="10, 0" />
</ContentPage.Padding>
<ListView x:Name="listView" />
</ContentPage>
using System;
using Xamarin.Forms;
namespace HelloXamarinForms
{
public partial class MainPageXaml : ContentPage
{
public MainPageXaml()
{
InitializeComponent();
System.Collections.Generic.List<DateTime> list =
new System.Collections.Generic.List<DateTime>();
list.Add(DateTime.Now);
listView.ItemsSource = list;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
list.Insert(0, DateTime.Now);
return true;
});
}
}
}
When the previous code is run, the ListView
does not update the screen because List
does not support the
INotifyCollectionChanged
interface. In other words, it does not execute a CollectionChanged
event.
Run the exact same code using an ObservableCollection
instead of a List
.
using System;
using Xamarin.Forms;
namespace HelloXamarinForms
{
public partial class MainPageXaml : ContentPage
{
public MainPageXaml()
{
InitializeComponent();
System.Collections.ObjectModel.ObservableCollection<DateTime> list =
new System.Collections.ObjectModel.ObservableCollection<DateTime>();
list.Add(DateTime.Now);
listView.ItemsSource = list;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
list.Insert(0, DateTime.Now);
return true;
});
}
}
}
Now the screen updates everytime a new item is inserted into the ObservableCollection
. ListView
and ObservableCollection
have a special relationship. This is why ListView
has
its own article
Triggers
Using the Trigger
class, change properties or execute events based on a trigger.
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HelloXamarinForms"
x:Class="HelloXamarinForms.MainPageXaml">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="20" Android="20, 0, 20, 20" />
</ContentPage.Padding>
<StackLayout Spacing="20">
<Entry Placeholder="enter your name" AnchorX="0">
<Entry.Triggers>
<Trigger TargetType="Entry" Property="IsFocused" Value="True">
<Setter Property="Scale" Value="1.25" />
</Trigger>
</Entry.Triggers>
</Entry>
<Entry Placeholder="enter your email" AnchorX="0">
<Entry.Triggers>
<Trigger TargetType="Entry" Property="IsFocused" Value="True">
<Setter Property="Scale" Value="1.25" />
</Trigger>
</Entry.Triggers>
</Entry>
<Entry Placeholder="enter your state" AnchorX="0">
<Entry.Triggers>
<Trigger TargetType="Entry" Property="IsFocused" Value="True">
<Setter Property="Scale" Value="1.25" />
</Trigger>
</Entry.Triggers>
</Entry>
</StackLayout>
</ContentPage>
Style
also has a Triggers
property allowing less XAML/code.
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HelloXamarinForms"
x:Class="HelloXamarinForms.MainPageXaml">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="20" Android="20, 0, 20, 20" />
</ContentPage.Padding>
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Entry">
<Setter Property="AnchorX" Value="0" />
<Style.Triggers>
<Trigger TargetType="Entry" Property="IsFocused" Value="True">
<Setter Property="Scale" Value="1.25" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Spacing="20">
<Entry Placeholder="enter name" />
<Entry Placeholder="enter email" />
<Entry Placeholder="enter state" />
</StackLayout>
</ContentPage>
Here, a class, BounceAction
, bounces a Button
but be warned because the code in the Clicked
event executes before the bounce animation is finished. See this
previous
page
in the tutorial for an example that waits for the animation to finish.
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HelloXamarinForms"
x:Class="HelloXamarinForms.MainPageXaml">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="20" Android="20, 0, 20, 20" />
</ContentPage.Padding>
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Style.Triggers>
<EventTrigger Event="Clicked">
<local:BounceAction />
</EventTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text="Bounce Button" Clicked="OnBounceButtonClick" />
</StackLayout>
</ContentPage>
// BounceAction.cs
using Xamarin.Forms;
namespace HelloXamarinForms
{
public class BounceAction : TriggerAction<VisualElement>
{
protected override async void Invoke(VisualElement ve)
{
await ve.ScaleTo(1.2, 100);
await ve.ScaleTo(0.9, 125);
await ve.ScaleTo(1.1, 150);
await ve.ScaleTo(1, 175);
}
}
}
using Xamarin.Forms;
namespace HelloXamarinForms
{
public partial class MainPageXaml : ContentPage
{
public MainPageXaml()
{
InitializeComponent();
}
void OnBounceButtonClick(object sender, System.EventArgs e)
{
DisplayAlert("BounceButton", "You clicked a bouncing button", "OK");
}
}
}