Black Belt XAML Data Binding - Reflection IT Belt XAML...•XAML (Silverlight, Windows Phone,...
Transcript of Black Belt XAML Data Binding - Reflection IT Belt XAML...•XAML (Silverlight, Windows Phone,...
Black Belt XAML Data
Binding
Fons Sonnemans
Trainer
@fonssonnemans
Reflection IT
Fons Sonnemans • In-company trainingen
– Programming Languages
• Visual C#, Visual Basic
– Platforms
• ASP.NET (Web Forms, MVC)
• XAML (Silverlight, Windows Phone, Windows 8, Blend)
– Databases
• SQL, SQL Server, Entity Framework
• www.reflectionit.nl
My Apps Windows 8
Windows Phone 7 & 8
Topics
• Data Binding
• GridView
Data Binding • Data binding is the process that establishes a connection, or binding, between the
UI and the business object which allows data to flow between the two
• Enable clean view/model separation and binding
– Change UI presentation without code-behind modifications
• Every binding has a source and a target
– Source is the business object
– Target is the UI element
• Binding Expressions can be one way or two way and supports validation &
converters
Binding class
Binding b = new Binding {
ElementName = "SliderFontSize",
Path = new PropertyPath("Value"),
};
textBlock1.SetBinding( TextBlock.FontSizeProperty, b);
<TextBlock Text="HelloWorld" FontSize="{Binding Path=Value, ElementName=SliderFontSize}" />
<TextBlock Text="HelloWorld"> <TextBlock.FontSize>
<Binding ElementName="SliderFontSize" Path="Value" />
</TextBlock.FontSize>
</TextBlock>
Binding Source
• Element to Element Binding
• StaticResource Binding
DependencyObject
DependencyProperty Binding Object
Value Converter
DependencyObject
DependencyProperty
Binding Target Binding Source
ElementName
DependencyObject
DependencyProperty Binding Object
Value Converter
CLR Object
Property
Binding Target Binding Source
Source
Binding Source
• DataContext Binding
• RelativeSource Binding
DependencyObject
DependencyProperty Binding Object
Value Converter
CLR Object
Property
Binding Target Binding Source
DependencyObject
DependencyProperty Binding Object
Value Converter
DependencyObject
DependencyProperty
Binding Target Binding Source
RelativeSource
Element to Element Binding
• Element binding is performed in the same manner as Data Binding
with one addition: the ElementName property. ElementName defines
the name of the binding source element.
• The following code snippet shows the basic syntax for element
binding.
<Grid> <Slider x:Name="slider" Value="40" Minimum="10" /> <TextBlock Text="{Binding Value, ElementName=slider}" FontSize="32"/> </Grid>
Element to Element Binding
Converters
<TextBox FontSize="64" Text="{Binding Value, ElementName=slider, Mode=TwoWay}" Visibility="{Binding IsChecked, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=checkBox}"/> <Slider x:Name="slider" /> <CheckBox x:Name="checkBox" Content="CheckBox" />
Employee class • Public properties, not fields
namespace DataBindingDemo.Models { public class Employee { public string Name { get; set; } public double Salary { get; set; } public Employee() { } public Employee(string name, double salary) { this.Name = name; this.Salary = salary; } public void RaiseSalary(double percentage) { this.Salary += Salary * (percentage / 100); } } }
StaticResource Binding • Register xmlns:models
• models:Employee
– Class must have a public default (parameter less) constructor
<Page x:Class="DataBindingDemo.MainPage" IsTabStop="false" xmlns:models="using:DataBindingDemo.Models" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:DataBindingDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <models:Employee x:Key="emp1" Name="Fons" Salary="2000" /> </Page.Resources> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <TextBlock FontSize="40" Margin="100" Text="{Binding Name, Source={StaticResource emp1}}" /> </Grid> </Page>
StaticResource Binding
CLR DataContext Binding • Default!
– Used when a Binding has no Source, RelativeSource or ElementName
• DataContext is inherited from parent
<Page x:Class="DataBindingDemo.MainPage" IsTabStop="false" xmlns:models="using:DataBindingDemo.Models" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:DataBindingDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <models:Employee x:Key="emp1" Name="Fons" Salary="2000" /> </Page.Resources> <Grid DataContext="{StaticResource emp1}" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <TextBlock FontSize="40" Margin="100" Text="{Binding Name}" /> <TextBlock FontSize="40" Margin="100,200" Text="{Binding Salary}" /> </Grid> </Page>
TwoWay DataBinding to CLR Object • Implement INotifyPropertyChanged and notify (raise) PropertyChanged event
class Employee : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } private string _name; public string Name { get { return _name; } set { if (_name != value) { _name = value; OnPropertyChanged("Name"); } } } <Grid DataContext="{StaticResource emp1}"
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <TextBox FontSize="40" Margin="100" Text="{Binding Name, Mode=TwoWay}" <TextBlock FontSize="40" Margin="100,200" Text="{Binding Name}" /> </Grid>
Binding to Collections • Works with any object that implements IEnumerable
– Arrays, Lists, Collections, etc.
• Bind to the ItemsSource of Data Controls – ItemsControl
– ListBox
– ComboBox
– ListView
– GridView
– FlipView
ItemSource <Page x:Class="DataBindingDemo.MainPage" IsTabStop="false" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:DataBindingDemo" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> <ListView Margin="50" x:Name="listEmployees" /> </Grid> </Page>
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); var l = new List<Employee> { new Employee("Fons", 2000), new Employee("Jim", 4000), new Employee("Ellen", 3000), }; this.listEmployees.ItemsSource = l; }
ItemTemplate <ListView Margin="40" x:Name="listEmployees" > <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Width="100" FontSize="20" Text="{Binding Path=Name}" /> <TextBlock Width="100" FontSize="20" Text="{Binding Salary}" /> <TextBlock Width="100" FontSize="20" Text="{Binding Name.Length}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
GridView
Windows Phone Panorama Control
GridView •Tiles
•Different
Content
•Different
Sizes
•Grouping
•Semantic
Zoom
namespace PanoramaDemo.Models { class FeaturedApp { public string Name { get; set; } public string Price { get; set; } public string Color { get; set; } public string ImageUrl { get { return "http://lorempixel.com/300/150/sports/" + Name; } } } }
namespace PanoramaDemo.ViewModels { class MainViewModel { public List<FeaturedApp> Apps { get; set; } public MainViewModel() { this.Apps = new List<Models.FeaturedApp>() { new FeaturedApp { Name = "App1", Color = "#FF0000", Price = "Free" }, new FeaturedApp { Name = "App2", Color = "#00FF00", Price = "$1.69" }, new FeaturedApp { Name = "App3", Color = "#0000FF", Price = "Free" }, new FeaturedApp { Name = "App4", Color = "#FF00FF", Price = "$4.99" }, }; } } }
<Application x:Class="PanoramaDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:PanoramaDemo.ViewModels" xmlns:local="using:PanoramaDemo"> <Application.Resources> <ResourceDictionary> <vm:MainViewModel x:Name="MainViewModel1" /> <DataTemplate x:Name="FeaturedAppDataTemplate"> <Grid Width="300" Height="200"> <Image Source="{Binding ImageUrl}" VerticalAlignment="Top" /> <StackPanel Background="{Binding Color}" Height="50" VerticalAlignment="Bottom"> <TextBlock Text="{Binding Name}" Margin="10,3" Foreground="White" FontWeight="Bold" /> <TextBlock Text="{Binding Price}" Margin="10,0" Foreground="White" /> </StackPanel> </Grid> </DataTemplate> </ResourceDictionary> </Application.Resources> </Application>
<GridView Grid.RowSpan="2" ItemTemplate="{StaticResource FeaturedAppDataTemplate}" ItemsSource="{Binding Apps}" SelectionMode="None" Padding="116,137,40,46" />
GridView •Tiles
•Different
Content
•Different
Sizes
•Grouping
•Semantic
Zoom
abstract class MenuItem { public string Name { get; set; } } class NavigationItem : MenuItem { public Type Page { get; set; } } class FeaturedApp : MenuItem { public string Price { get; set; } public string Color { get; set; } public string ImageUrl { get { return "http://lorempixel.com/300/150/sports/" + Name; } } }
class MainViewModel { public List<MenuItem> Items { get; set; } public MainViewModel() { this.Items = new List<MenuItem>() { new FeaturedApp { Name = "App1", Color = "#FF0000", Price = "Free" }, new FeaturedApp { Name = "App2", Color = "#00FF00", Price = "$1.69" }, new FeaturedApp { Name = "App3", Color = "#0000FF", Price = "Free" }, new FeaturedApp { Name = "App4", Color = "#FF00FF", Price = "$4.99" }, new NavigationItem { Name = "Free Apps" }, new NavigationItem { Name = "Paid Apps" }, }; } }
namespace PanoramaDemo.Selectors { class MenuItemDataTemplateSelector : DataTemplateSelector { protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { if (item is Models.FeaturedApp) { return App.Current.Resources["FeaturedAppDataTemplate"] as DataTemplate; } else { return App.Current.Resources["NavigationItemDataTemplate"] as DataTemplate; } } } }
<GridView Grid.RowSpan="2" ItemsSource="{Binding Items}" ItemTemplateSelector="{StaticResource MenuItemDataTemplateSelector1}" SelectionMode="None" Padding="116,137,40,46"> <GridView.ItemContainerStyle> <Style TargetType="GridViewItem"> <Setter Property="VerticalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> </Style> </GridView.ItemContainerStyle> </GridView>
<sel:MenuItemDataTemplateSelector x:Name="MenuItemDataTemplateSelector1" /> <DataTemplate x:Name="NavigationItemDataTemplate"> <Grid Background="Orange" Width="300" Height="200"> <TextBlock Text="{Binding Name}" Foreground="White" FontSize="32" Margin="10" /> </Grid> </DataTemplate> <DataTemplate x:Name="FeaturedAppDataTemplate"> <Grid Width="300" Height="200"> <Image Source="{Binding ImageUrl}" VerticalAlignment="Top" /> <StackPanel Background="{Binding Color}" Height="50" VerticalAlignment="Bottom"> <TextBlock Text="{Binding Name}" Margin="10,3" Foreground="White" FontWeight="Bold" /> <TextBlock Text="{Binding Price}" Margin="10,0" Foreground="White" /> </StackPanel> </Grid> </DataTemplate>
GridView •Tiles
•Different
Content
•Different
Sizes
•Grouping
•Semantic
Zoom
class FeaturedApp : MenuItem { public string Price { get; set; } public string Color { get; set; } public int RowSpan { get; set; } public FeaturedApp() { this.RowSpan = 1; } public string ImageUrl { get { if (RowSpan == 2) { return "http://lorempixel.com/300/360/sports/" + Name; } return "http://lorempixel.com/300/150/sports/" + Name; } } }
public MainViewModel() { this.Items = new List<MenuItem>() { new FeaturedApp { Name = "App1", Color = "#FF0000", Price = "Free" }, new FeaturedApp { Name = "App2", Color = "#00FF00", Price = "$1.69" }, new FeaturedApp { Name = "App3", Color = "#0000FF", Price = "Free", RowSpan = 2 }, new FeaturedApp { Name = "App4", Color = "#FF00FF", Price = "$4.99" }, new NavigationItem { Name = "Free Apps" }, new NavigationItem { Name = "Paid Apps" }, }; }
<!-- Remove Grid.Width and Grid.Height--> <DataTemplate x:Name="NavigationItemDataTemplate"> <Grid Background="Orange"> <TextBlock Text="{Binding Name}" Foreground="White" FontSize="32" Margin="10" /> </Grid> </DataTemplate> <DataTemplate x:Name="FeaturedAppDataTemplate"> <Grid> <Image Source="{Binding ImageUrl}" VerticalAlignment="Top" /> ...
class MenuItemDataTemplateSelector : DataTemplateSelector { protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { if (item is Models.FeaturedApp) { VariableSizedWrapGrid.SetRowSpan(container as UIElement, (item as Models.FeaturedApp).RowSpan); return App.Current.Resources["FeaturedAppDataTemplate"] as DataTemplate; } else { return App.Current.Resources["NavigationItemDataTemplate"] as DataTemplate; } } }
<GridView Grid.RowSpan="2" ItemsSource="{Binding Items}" ItemTemplateSelector="{StaticResource MenuItemDataTemplateSelector1}" SelectionMode="None" Padding="116,137,40,46"> <GridView.ItemContainerStyle> <Style TargetType="GridViewItem"> <Setter Property="VerticalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> </Style> </GridView.ItemContainerStyle> <GridView.ItemsPanel> <ItemsPanelTemplate> <VariableSizedWrapGrid ItemWidth="300" ItemHeight="200" /> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView>
GridView •Tiles
•Different
Content
•Different
Sizes
•Grouping
•Semantic
Zoom
class MainViewModel { public List<MenuGroup> Groups { get; set; } public MainViewModel() { this.Groups = new List<MenuGroup>() { new MenuGroup { Title = "Spotlight", Items = new List<MenuItem> { new FeaturedApp { Name = "App1", Color = "#FF0000", Price = "Free" }, new FeaturedApp { Name = "App2", Color = "#00FF00", Price = "$1.69" }, new FeaturedApp { Name = "App3", Color = "#0000FF", Price = "Free", RowSpan = 2 }, new FeaturedApp { Name = "App4", Color = "#FF00FF", Price = "$4.99" }, new NavigationItem { Name = "Free Apps" }, new NavigationItem { Name = "Paid Apps" }, } }, new MenuGroup { Title = "Games", Items = new List<MenuItem> { new FeaturedApp { Name = "Game1", Color = "#FF0000", Price = "Free", RowSpan = 2 }, new FeaturedApp { Name = "Game2", Color = "#00FF00", Price = "$2.99" }, new FeaturedApp { Name = "Game3", Color = "#0000FF", Price = "Free" }, new NavigationItem { Name = "Free Games" }, new NavigationItem { Name = "Paid Games" }, } } }; } }
class MenuGroup { public string Title { get; set; } public List<MenuItem> Items { get; set; } }
<common:LayoutAwarePage x:Name="pageRoot" x:Class="PanoramaDemo.Views.MainPage" DataContext="{StaticResource MainViewModel1}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:PanoramaDemo.Views" xmlns:common="using:PanoramaDemo.Common" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <!-- Collection of grouped items displayed by this page, bound to a subset of the complete item list because items in groups cannot be virtualized --> <CollectionViewSource x:Name="groupedItemsViewSource" Source="{Binding Groups}" IsSourceGrouped="true" ItemsPath="Items" /> </Page.Resources>
<GridView Grid.RowSpan="2" ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}" ItemTemplateSelector="{StaticResource MenuItemDataTemplateSelector1}" SelectionMode="None" Padding="116,137,40,46"> <GridView.ItemContainerStyle>...</GridView.ItemContainerStyle> <GridView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </GridView.ItemsPanel> <GridView.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <Grid Margin="1,0,0,6"> <TextBlock Text="{Binding Title}" Margin="3,-7,10,10" Style="{StaticResource GroupHeaderTextStyle}" /> </Grid> </DataTemplate> </GroupStyle.HeaderTemplate> <GroupStyle.Panel> <ItemsPanelTemplate> <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0" ItemWidth="300" ItemHeight="200" /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </GridView.GroupStyle> </GridView>
GridView •Tiles
•Different
Content
•Different
Sizes
•Grouping
•Semantic
Zoom
<SemanticZoom x:Name="semanticZoom" Grid.RowSpan="2"> <SemanticZoom.ZoomedOutView> <GridView x:Name="GridZoomOut" SelectionMode="None" ScrollViewer.IsHorizontalScrollChainingEnabled="False" Padding="116,137,40,46" IsSwipeEnabled="True" Foreground="White"> <GridView.ItemTemplate> <DataTemplate> <Grid Background="White" Width="200" Height="200"> <TextBlock Foreground="Black" HorizontalAlignment="Left" VerticalAlignment="Bottom" Text="{Binding Group.Title}" Margin="10" FontFamily="Segoe UI Light" FontSize="36" /> </Grid> </DataTemplate> </GridView.ItemTemplate> <GridView.ItemsPanel> <ItemsPanelTemplate> <WrapGrid MaximumRowsOrColumns="5" VerticalChildrenAlignment="Top" /> </ItemsPanelTemplate> </GridView.ItemsPanel> </GridView> </SemanticZoom.ZoomedOutView> <SemanticZoom.ZoomedInView> <GridView ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}“ ...
public MainPage() { this.InitializeComponent(); (semanticZoom.ZoomedOutView as GridView).ItemsSource = groupedItemsViewSource.View.CollectionGroups; }
Recap & Questions
@fonssonnemans
fonssonnemans
reflectionit.nl/blog