Win 10 apps - Data Binding

本文使用VS 2015,创建基于MVVM模式的Universal Windows Platform(UWP) app,实现了几种Data Binding:UI (control) <-->single item, UI(control)<-->a collection of items, 控制Items的呈现, 实现显示选择项详情,以及数据的格式转换。更多详情见 Data binding in depth.

前提

在VS2015中创建UWP app。命名为:Demo_UWP_DataBinding

实例创建

所有的绑定都包括Binding target和Binding resource,通常的:

  • Binding target:control中的一个属性或者一个UI element.

  • Binding resource:class 实例的(model或viewmodel)一个属性。

下面例子包括绑定一个简单的item,绑定多个item(ObservableCollection),控制items呈现,显示选择项详情和数据格式转换。

1. 首先添加Model(Recording.cs)和一个ModelView(RecordingViewModel.cs)

public class Recording
    {
        public string ArtistName { get; set; }
        public string CompositionName { get; set; }
        public DateTime ReleaseDateTime { get; set; }

        public Recording()
        {
            this.ArtistName = "Demi Dai";
            this.CompositionName = "Super Demi Dai";
            this.ReleaseDateTime = new DateTime(2016, 1, 1);
        }

        public string OneLineSummary
        {
            get
            {
                return $"{this.CompositionName} by {this.ArtistName}, released: "
                    + this.ReleaseDateTime.ToString("d");
            }
        }
    }

public class RecordingViewModel
    {
        // Binding one single item Start  
        private Recording defaultRecording = new Recording();
        public Recording DefaultRecording { get { return this.defaultRecording; } }
        // Binding one single item End

        //Binding a collection of items Start
        private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();// implements the INotifyPropertyChanged and INotifyCollectionChanged interfaces. Interface provided change notification to bindings when items are added|removed|the list itself changes
        public ObservableCollection<Recording> Recordings { get { return this.recordings; } }
        public RecordingViewModel()
        {
            this.recordings.Add(new Recording()
            {
                ArtistName = "Johann Sebastian Bach",
                CompositionName = "Mass in B minor",
                ReleaseDateTime = new DateTime(2015, 7, 8)
            });
            this.recordings.Add(new Recording()
            {
                ArtistName = "Ludwig van Beethoven",
                CompositionName = "Third Symphony",
                ReleaseDateTime = new DateTime(2015, 2, 11)
            });
            this.recordings.Add(new Recording()
            {
                ArtistName = "George Frideric Handel",
                CompositionName = "Serse",
                ReleaseDateTime = new DateTime(1737, 12, 3)
            });
        }
        //Binding a collection of items End
    }

值得注意的是:在Binding to a collection of items的时候,我们使用了 ObservableCollection<T>来进行集合绑定,这个一个非常好的方法,因为这个类实现了INotifyPropertyChanged 和INotityCollectionChanged接口,这两个接口当集合中的对象的增加,删除,或者list本身更新的时候,为binding提供了改变提醒作用。也可以实现这个接口绑定control与items同步更新。更多 Data binding in depth.

2. 暴露Binding source.

在MainPage中添加RecordingsViewModel 属性

public sealed partial class BindingPage : Page //Partial class of MainPage
    {
        public BindingPage()
        {
            this.InitializeComponent();
            this.ViewModel = new RecordingViewModel(); //Binding Source
        }

        public RecordingViewModel ViewModel { get; set; }
    }

3. 转换item显示格式

添加StringFormatter.cs用来转换日期显示格式。其实还有其他的方法实现:比如在Recording.cs中添加一返回值是this.ReleaseDateTime.ToString("d")string属性,名字为ReleaseDate表示返回的是日期,不是日期和时间,或命名为ReleaseDataAsString表示返回的是一个string类型。当然下面这个方法更加的灵活。

public class StringFormatter : Windows.UI.Xaml.Data.IValueConverter
{
    // This converts the value object to the string to display.
    // This will work with most simple types.
    public object Convert(object value, Type targetType,
        object parameter, string language)
    {
        // Retrieve the format string and use it to format the value.
        string formatString = parameter as string;
        if (!string.IsNullOrEmpty(formatString))
        {
            return string.Format(formatString, value);
        }

        // If the format string is null or empty, simply
        // call ToString() on the value.
        return value.ToString();
    }

    // No need to implement converting back on a one-way binding
    public object ConvertBack(object value, Type targetType,
        object parameter, string language)
    {
        throw new NotImplementedException();
    }
}


4. View中添加Binding Target,与Binding Source绑定

<Page
    x:Class="Demo_UWP_DataBinding.BindingPage"
    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:models="using:Demo_UWP_DataBinding.Models"
    xmlns:tools ="using:Demo_UWP_DataBinding.Tools"
    mc:Ignorable="d">
    <Page.Resources>
        <CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
        <tools:StringFormatter x:Key="StringFormatterValueConverter"/>
    </Page.Resources>
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="129*"/>
            <ColumnDefinition Width="127*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="115*"/>
            <RowDefinition Height="159*"/>
            <RowDefinition Height="139*"/>
            <RowDefinition Height="355*"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" x:Name="textBlock" HorizontalAlignment="Left"  
                   TextWrapping="Wrap" Text="Binding to a single item: " VerticalAlignment="Top"
                   Margin="5,25,0,0" Height="20" Width="160" FontWeight="Bold" Foreground="#FF14891E"/>
        <!--Binding Target:TextBox; Binding Source: RecordingViewModel.DefaultRecording.OneLineSummary-->
        <TextBox  Grid.Row="0" Grid.Column="0" Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"
        VerticalAlignment="Center" Margin="5,82,5,0.8" Height="32" Background="{x:Null}" BorderBrush="{x:Null}" Grid.ColumnSpan="2"
                  />

        <TextBlock Grid.Row="1" Grid.Column="0" 
                   x:Name="textBlock2" HorizontalAlignment="Left"  
                   TextWrapping="Wrap" Text="Binding to a collection of items: " VerticalAlignment="Top"
                   Margin="5,15.2,0,0" Height="20" Width="160" FontWeight="Bold" Foreground="#FF14891E"/>
        <!--Binding Target:TextBox; Binding Source: RecordingViewModel.Recordings-->
        <ListView  Grid.Row="1" Grid.Column="0" 
                   ItemsSource="{x:Bind ViewModel.Recordings}"
        HorizontalAlignment="Center" VerticalAlignment="Center" Height="132" Grid.ColumnSpan="2" >
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="models:Recording">
                    <TextBlock Text="{x:Bind OneLineSummary}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <ListView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
                  ItemsSource="{x:Bind ViewModel.Recordings}"
    HorizontalAlignment="Center" Height="160" >
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="models:Recording">
                    <StackPanel Orientation="Horizontal" Margin="6">
                        <SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
                        <StackPanel>
                            <TextBlock Text="{x:Bind ArtistName}" FontWeight="Bold"/>
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <TextBlock Grid.Row="3" Grid.Column="0" x:Name="textBlock3" HorizontalAlignment="Left"  
                   TextWrapping="Wrap" Text="Adding a details view: " VerticalAlignment="Top"
                   Margin="5,5,0,0" Height="20" Width="160" FontWeight="Bold" Foreground="#FF14891E"/>
        <!--Means one: SelectedItem-->
        <TextBlock Grid.Row="3" Grid.Column="0" x:Name="textBlock4" HorizontalAlignment="Center"  
                   TextWrapping="Wrap" Text="Means one: SelectedItem" VerticalAlignment="Top"
                   Margin="0,25,0,0" Height="20" FontWeight="Bold"/>
        <StackPanel Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center" Margin="0,40,0,0">
            <ListView x:Name="RecordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}" Margin="0,10,0,0">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="models:Recording">
                        <StackPanel Orientation="Horizontal" Margin="2">
                            <SymbolIcon Symbol="Audio"></SymbolIcon>
                            <StackPanel>
                                <TextBlock Text="{x:Bind ArtistName}"/>
                                <TextBlock Text="{x:Bind CompositionName}"/>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackPanel Margin="8" DataContext="{Binding SelectedItem, ElementName=RecordingsListView}">
                <TextBlock Text="{Binding ArtistName}"/>
                <TextBlock Text="{Binding CompositionName}"/>
                <TextBlock Text="{Binding ReleaseDateTime}"/>
            </StackPanel>
        </StackPanel>
        <!--Means two: CollectionViewSource(Page.Resource)-->
        <TextBlock Grid.Row="3" Grid.Column="1" x:Name="textBlock5" HorizontalAlignment="Center"  
                   TextWrapping="Wrap" Text="Means two: CollectionViewSource" VerticalAlignment="Top"
                   Margin="0,25.2,-89,0" Height="20" FontWeight="Bold"  />
        <StackPanel Grid.Row="3" Grid.Column="1" HorizontalAlignment="Center" Margin="0,40,0,0">
            <ListView ItemsSource="{Binding Source={StaticResource RecordingsCollection}}" Margin="0,10,0,0">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="models:Recording">
                        <StackPanel Orientation="Horizontal" Margin="2">
                            <SymbolIcon Symbol="Audio"></SymbolIcon>
                            <StackPanel>
                                <TextBlock Text="{x:Bind ArtistName}"/>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackPanel Margin="8" DataContext="{Binding Source={StaticResource RecordingsCollection}}">
                <TextBlock Text="{Binding ArtistName}"/>
                <TextBlock Text="{Binding CompositionName}"/>
                <!--Formatting or converting data values for display(Page.Resource)-->
                <TextBlock Text="{Binding ReleaseDateTime,
                    Converter={StaticResource StringFormatterValueConverter},
                    ConverterParameter=Released: \{0:d\}}"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Page>
需要注意是:
  • Binding a single item - 使用了TextBlock来绑定DefaultRecording.OneLineSummary。
  • Binding items Collection - 使用LIstView来绑定Recordings 集合,首先要添加CollectionViewSource作为page resource。ListView绑定 x:Bind ViewModel.Recordings默认显示的是类型名,所以要么将OneLineSummary作为值返回,要么就使用Data template来显示每个item。指定DateTemplate方法有ContentTemplate(Content control)和ItemTemplate(item control)两种方法,上面也分别给出了ItemTemplate两种实现代码。
  • Show selectedItem details - 有两种方法:SelectedItem 和 CollectionViewSource(LIstView<ItemSource>和Details View<DataContext>都是绑定到 RecordingsCollection( CollectionViewSource),会处理当前选定项,不需要指定CurrentItem,当然如果有需要你也可以指定,以便有歧义的地方), 详情见上面代码。
  • {x:Bind} vs {Binding}-  前者Wins 10 新增扩展标记,可替代{Binding},但是性能更好,占用内存更小,并更好的支持debugging。在XAML 加载的过程中,{x:Bind}将转换成你认为的binding object,并且该binding object从data source属性中获取值。两种绑定实现的功能差不多,但是{x:Bind}能执行在编译时间生成的特殊目的的代码,{Binding}是runtime object的通用目的检测。
  • Format item - 首先也要将StringFormatter作为page resource来进行绑定,使用Converter标记来实现最终的灵活格式化。

更多xaml语法见Quickstart: Creating a user interface with XAML 。更多控件布局见Quickstart: Defining layouts. 

运行效果




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值