需求:使用 CollectionView 呈現(xiàn)數(shù)據(jù)列表和按鈕動(dòng)作
項(xiàng)目開(kāi)發(fā)中不可避免地會(huì)遇到在一個(gè)頁(yè)面中呈現(xiàn)列表的情況,使用 CollectionView 作為容器是很方便的。CollectionView 中顯示的數(shù)據(jù)對(duì)應(yīng)于后臺(tái)的一個(gè) IEnumerable 派生的列表,常用的是 List<T> 和 Vector<T>,我習(xí)慣于使用 List<T> 作為后臺(tái)的數(shù)據(jù)表。
CollectionView 的每一項(xiàng)對(duì)應(yīng)后臺(tái)的 List<T> 的一條記錄。在網(wǎng)關(guān)應(yīng)用中,有一個(gè)頁(yè)面要列出所有的場(chǎng)景,單擊(不論是鼠標(biāo)還是手指單點(diǎn)一下)執(zhí)行這個(gè)場(chǎng)景,單擊條目右側(cè)的“配置...”按鈕對(duì)這個(gè)場(chǎng)景進(jìn)行配置。
CollectionView 的 SelectionMode=“Single”,SelectionChanged 事件響應(yīng)對(duì)這個(gè)條目的單擊。在這個(gè)頁(yè)面中,CollectionView 的每一條用一個(gè) Grid 包裝,包括了一個(gè)引導(dǎo)圖標(biāo),一個(gè)主條目 Label 顯示這個(gè)場(chǎng)景的名稱(chēng),一個(gè)付條目 Labe 顯示這個(gè)場(chǎng)景的類(lèi)型,右側(cè)的裝填了一個(gè)“配置”按鈕。 兩個(gè) Label 的 Text 可以在 XAML 中用顯示綁定的方式顯示對(duì)應(yīng)的屬性,但問(wèn)題來(lái)了,“配置”按鈕應(yīng)該綁定什么呢?也就是說(shuō),對(duì)這個(gè)條目中包含的無(wú)綁定控件,怎么判斷是哪一個(gè)條目的“配置”按鈕被點(diǎn)擊了呢?
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="I2oT.Views.ScenesPage"
Title="場(chǎng)景">
<ContentPage.Resources>
<Style TargetType="Button">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" >
<VisualState.Setters>
<Setter Property="Scale" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale" Value="0.9" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
<Setter Property="TextColor" Value="{StaticResource AppForegroundColor}"/>
<Setter Property="BackgroundColor" Value="{StaticResource AppBackgroundColor}"/>
<Setter Property="FontSize" Value="Caption"/>
<Setter Property="HeightRequest" Value="32"/>
<Setter Property="MinimumHeightRequest" Value="10"/>
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="HorizontalOptions" Value="Start"/>
</Style>
<Style x:Key="ItemButtonStyle" TargetType="Button">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" >
<VisualState.Setters>
<Setter Property="Scale" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<VisualState.Setters>
<Setter Property="Scale" Value="0.8" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
<Setter Property="TextColor" Value="{StaticResource AppTextCommonColor}"/>
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="FontSize" Value="Caption"/>
<Setter Property="HeightRequest" Value="32"/>
<Setter Property="MinimumHeightRequest" Value="10"/>
<Setter Property="BorderColor" Value="{StaticResource AppTextCommonColor}"/>
<Setter Property="BorderWidth" Value="0.5"/>
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="VerticalOptions" Value="Center"/>
<Setter Property="HorizontalOptions" Value="Start"/>
<Setter Property="Margin" Value="4,0"/>
<Setter Property="CharacterSpacing" Value="1"/>
</Style>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="刷新" Clicked="RefreshSubsetList"/>
<ToolbarItem Text="添加" Clicked="OnAddSceneClicked"/>
</ContentPage.ToolbarItems>
<CollectionView x:Name="collectionView"
Margin="{StaticResource PageMargin}"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged">
<CollectionView.Header>
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal" >
<Button x:Name="btnInstantScene" Text="即時(shí)場(chǎng)景" Clicked="DisplayInstantScenes"/>
<Button x:Name="btnTimingScene" Text="定時(shí)場(chǎng)景" Clicked="DisplayTimingScenes"/>
<Button x:Name="btnSensorScene" Text="自動(dòng)化場(chǎng)景" Clicked="DisplaySensorScenes"/>
</StackLayout>
</ScrollView>
</CollectionView.Header>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="8" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Grid ColumnDefinitions="0.15*,*,0.4*">
<Image Grid.RowSpan="2"
Source="scene.png"
Aspect="AspectFit"
VerticalOptions="Start"
HeightRequest="20"
BackgroundColor="Transparent"/>
<Label Grid.Row ="0"
Grid.Column="1"
Text="{Binding Name}"
FontSize="Small"
TextColor="{Binding ViewColor}"
BackgroundColor="Transparent"/>
<Label Grid.Row ="1"
Grid.Column="1"
Text="{Binding Descriptive}"
TextColor="{StaticResource DescriptiveTextColor}"
FontSize="Caption"
BackgroundColor="Transparent"/>
<Button Grid.RowSpan="2" Grid.Row="0" Grid.Column="2"
Text="配置..." Style="{StaticResource ItemButtonStyle}"
Clicked="OnDefineScene"/>
</Grid>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Label x:Name="lbMessage"
Text="Status"
FontSize="Caption"
TextColor="{StaticResource AppTipIconColor}"
VerticalOptions="EndAndExpand"
HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="Center"/>
</CollectionView.Footer>
</CollectionView>
</ContentPage>
Xamarin.Forms 的 CollectionView 中的子控件的 BindingContext
一開(kāi)始我也對(duì)這個(gè)“綁定”感到手足無(wú)措,后來(lái)突然想到了一個(gè)辦法:使用 Debug 模式,斷點(diǎn)運(yùn)行到 OnDefineScene 函數(shù)中,用 Shift+F9 查看一下是否有可用的線索。果然找到了!原來(lái),在 CollectionView 條目中定義的子控件,不論是否顯示地使用 {Binding xxxProperty} 進(jìn)行綁定,這些子控件的 BindingContext 竟然就是被綁定列表的對(duì)應(yīng)記錄!
cs 代碼
using I2oT.Data;
using I2oT.Models;
using I2oT.Views.Scenes;
using I2oT.Views.Subsets;
using I2oT.Views.SystemSettings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace I2oT.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ScenesPage : ContentPage
{
private List<SceneModel> sceneList = null;
private List<SceneModel> instantSceneList = null;
private List<SceneModel> timingSceneList = null;
private List<SceneModel> sensorSceneList = null;
public ScenesPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
RefreshSceneList(this, new EventArgs());
lbMessage.Text = "";
}
private void RefreshSceneList(object sender, EventArgs e)
{
collectionView.ItemsSource = null;
sceneList = (new SceneModel()).GetAll();
collectionView.ItemsSource = sceneList;
instantSceneList = new List<SceneModel>();
timingSceneList = new List<SceneModel>();
sensorSceneList = new List<SceneModel>();
foreach (var sx in sceneList)
{
if (sx.Type == 1)
{
instantSceneList.Add(sx);
}
else if (sx.Type == 2)
{
timingSceneList.Add(sx);
}
else if (sx.Type == 3)
{
sensorSceneList.Add(sx);
}
}
btnInstantScene.Text = "即時(shí)場(chǎng)景 " + instantSceneList.Count().ToString();
btnTimingScene.Text = "定時(shí)場(chǎng)景 " + timingSceneList.Count().ToString();
btnSensorScene.Text = "自動(dòng)化場(chǎng)景 " + sensorSceneList.Count().ToString();
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender == null || e == null) return;
SceneModel scene = (SceneModel)e.CurrentSelection.FirstOrDefault();
if (scene == null) return;
if (scene.Type != 1) return;
// Only instant scene can be performed directly.
if (scene.Type == 1)
{
App.Gateway.PerformScene(scene.ID);
RefreshSubsetList(null, new EventArgs());
}
}
private void OnDefineScene(object sender, EventArgs e)
{
var sx = (SceneModel)(((Button)sender).BindingContext);
switch (sx.Type)
{
case 1:
case 3:
Shell.Current.GoToAsync($"{nameof(InstantSceneDefinePage)}?{nameof(InstantSceneDefinePage.SceneID)}={sx.ID}");
break;
case 2:
string uri = "";
uri += $"{nameof(TimingSceneDefinePage)}?";
uri += $"{nameof(TimingSceneDefinePage.SceneID)}={sx.ID}&";
uri += $"{nameof(TimingSceneDefinePage.SceneName)}={sx.Name}";
Shell.Current.GoToAsync(uri);
break;
default:
break;
}
}
private void OnAddSceneClicked(object sender, EventArgs e)
{
Shell.Current.GoToAsync($"{nameof(AddNewScenePage)}");
}
private void DisplayInstantScenes(object sender, EventArgs e)
{
collectionView.ItemsSource = instantSceneList;
}
private void DisplayTimingScenes(object sender, EventArgs e)
{
collectionView.ItemsSource = timingSceneList;
}
private void DisplaySensorScenes(object sender, EventArgs e)
{
collectionView.ItemsSource = sensorSceneList;
}
}
}
上述代碼中,在 OnAppearing 方法中調(diào)用 RefreshSceneList 方法獲取已定義的場(chǎng)景列表,列表中的每一個(gè)元素是一個(gè) SceneModel (場(chǎng)景的數(shù)據(jù)模型),默認(rèn)將全部場(chǎng)景列出,通過(guò) ItemsSource 屬性將 sceneList 綁定到 CollectionView。
斷點(diǎn)觀察
在 OnDefineScene 事件的第一條語(yǔ)句上設(shè)置斷點(diǎn),運(yùn)行到此處暫停,然后 Shift+F9 打開(kāi)快速監(jiān)視,輸入sender,(Button)sender,再輸入((Button)sender).BindingContext,得到的計(jì)算值如下圖所示。也就是說(shuō),這個(gè)配置按鈕的 BindingContext 是 CollectionView 綁定的列表的當(dāng)前元素!
哦吼,這下好辦啦!直接將這個(gè) SceneModel 的 ID 傳遞給下級(jí)頁(yè)面就可以啦~
總結(jié)
一旦 CollectionView 的 ItemsSource 被賦值為一個(gè)類(lèi)的列表,那么這個(gè) CollectionView 的每一個(gè)條目中的任何控件的默認(rèn) BindingContext 就是這個(gè)列表的當(dāng)前元素。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-817500.html
Xamarin.Forms 的 CollectionView 真真良心。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-817500.html
到了這里,關(guān)于PLC-IoT 網(wǎng)關(guān)開(kāi)發(fā)札記(6): Xamarin.Forms 的 CollectionView 綁定了什么?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!