800 likes | 990 Views
Controls and Data Binding. Charles Petzold www.charlespetzold.com. Agenda. Built-in controls Styles and templates Data binding Model-View- ViewModel (MVVM) Items controls The WebBrowser control Application bars Toolkit controls. Controls. SWP contains assortment of basic controls
E N D
Controls and Data Binding Charles Petzold www.charlespetzold.com
Agenda • Built-in controls • Styles and templates • Data binding • Model-View-ViewModel (MVVM) • Items controls • The WebBrowser control • Application bars • Toolkit controls
Controls • SWP contains assortment of basic controls • TextBox and PasswordBox • Button, CheckBox, RadioButton, and HyperlinkButton • Slider and ProgressBar • MediaElement and MultiScaleImage • ListBox, Pivot, and Panorama • WebBrowser and other controls • Touch support built in • More in Silverlight for Windows Phone Toolkit
Button Control <Button Width="400" Height="240" FontSize="{StaticResourcePhoneFontSizeExtraLarge}" Content="Click Me" Click="OnClick"/> private void OnClick(object sender, RoutedEventArgs e) { ... }
Button with Custom Content <Button Width="400" Height="240" Click="OnClick"> <Button.Background> <LinearGradientBrushStartPoint="0.5,0.0" EndPoint="0.5,1.0"> <GradientStop Offset="0.0" Color="#FF808080" /> <GradientStop Offset="1.0" Color="#FF202020" /> </LinearGradientBrush> </Button.Background> <Button.Content> <!-- Insert penguin XAML here --> </Button.Content> </Button>
TextBox Control // XAML <TextBox x:Name="Input" FontSize="36" /> // C# string input = Input.Text; // What the user typed
SIPs and Input Scopes • SIP = Software Input Panel • InputScope property of text-input elements permits SIP to be tailored to input type • More than 60 input scopes available InputScope="Default" InputScope="TelephoneNumber"
Specifying an Input Scope // Short form (XAML) <TextBox x:Name="Input" InputScope="TelephoneNumber" /> // Long form (XAML with IntelliSense) <TextBox x:Name="Input"> <TextBox.InputScope> <InputScope> <InputScopeNameNameValue="TelephoneNumber" /> </InputScope> </TextBox.InputScope> </TextBox> // C# Input.InputScope= new InputScope() { Names = { new InputScopeName() { NameValue = "TelephoneNumber" } } };
ProgressBar Control <!-- ProgressBar with default min and max (0 and 100) --> <ProgressBarValue="40" /> <!-- ProgressBar with custom min and max --> <ProgressBarMinimum="1" Maximum="1000" /> <!-- IndeterminantProgressBar --> <ProgressBarIsIndeterminant="true" /> ProgressBar showing 40% complete IndeterminantProgressBar
ProgressBar Performance // Never do this! <ProgressBarx:Name="Progress" IsIndeterminant="true" /> // Do this instead <ProgressBar x:Name="Progress" /> ... Progress.IsIndeterminant = true; // Operation begins ... Progress.IsIndeterminant= false; // Operation completes
Styles • Allow look and feel to be factored from content • Provide level of indirection between visual properties and their values • Style = Collection of property values • Define style as XAML resource • Apply style using {StaticResource} markup extension • Or apply it programmatically • Global scope or local scope
Defining a Global Style // App.xaml <Application.Resources> <Style x:Key="ButtonStyle" TargetType="Button"> <Setter Property="Width" Value="400" /> <Setter Property="Height" Value="240" /> </Style> </Application.Resources>
Defining a Local Style // MainPage.xaml <Grid.Resources> <Style x:Key="ButtonStyle" TargetType="Button"> <Setter Property="Width" Value="400" /> <Setter Property="Height" Value="240" /> </Style> </Grid.Resources>
Applying Styles <Button Style="{StaticResourceButtonStyle}" ... /> <Button Style="{StaticResourceButtonStyle}" ... /> <Button Style="{StaticResourceButtonStyle}" ... /> <Button Style="{StaticResourceButtonStyle}" Width="100" ... /> Explicit property value overrides style property value
BasedOn Styles <Style x:Key="ButtonStyle" TargetType="Button"> <Setter Property="Width" Value="400" /> <Setter Property="Height" Value="240" /> </Style> <Style x:Key="TranslucentButtonStyle" TargetType="Button" BasedOn="{StaticResourceButtonStyle}"> <Setter Property="Opacity" Value="0.5" /> </Style>
Control Templates • Redefine a control’s entire visual tree • Perform deep customization while retaining basic behavior of control • Exposed through control's Template property • Inherited from Control base class • Use {TemplateBinding} to flow property values from control to template • Use ContentPresenter and ItemsPresenter to flow content and items to template
Elliptical Button <Button Foreground="Black"> <Button.Template> <ControlTemplateTargetType="Button"> <Grid> <Ellipse Width="256" Height="128"> <Ellipse.Fill> <RadialGradientBrushGradientOrigin="0.25,0.25"> <GradientStop Offset="0.25" Color="White" /> <GradientStop Offset="1.0" Color="Red" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> <TextBlockFontSize="24" Text="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Button.Template> </Button>
{TemplateBinding} <Button Width="256" Height="128" FontSize="24" Content="Click Me" Foreground="Black"> <Button.Template> <ControlTemplateTargetType="Button"> <Grid> <Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> <Ellipse.Fill> <RadialGradientBrushGradientOrigin="0.25,0.25"> <GradientStop Offset="0.25" Color="White" /> <GradientStop Offset="1.0" Color="Red" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> <TextBlockFontSize="24" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Button.Template> </Button>
ContentPresenter <Button Width="256" Height="128" FontSize="24" Content="Click Me" Foreground="Black"> <Button.Template> <ControlTemplateTargetType="Button"> <Grid> <Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> <Ellipse.Fill> <RadialGradientBrushGradientOrigin="0.25,0.25"> <GradientStop Offset="0.25" Color="White" /> <GradientStop Offset="1.0" Color="Red" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> <ContentPresenterHorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Button.Template> </Button>
Combining Styles and Templates <Style x:Key="EllipticalButton" TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplateTargetType="Button"> <Grid> <Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"> <Ellipse.Fill> <RadialGradientBrushGradientOrigin="0.25,0.25"> <GradientStop Offset="0.25" Color="White" /> <GradientStop Offset="1.0" Color="Red" /> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> <ContentPresenterHorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
Data Binding • Permits properties of one object to be bound to properties of another • Target property must be DependencyProperty • Source property can be any type of property • Source can be a collection if target is items control • OneTime, OneWay, and TwoWay bindings • {Binding} markup extension provides declarative support for data binding
Data Binding Schema Flows data between source and target Target Source Binding Object Dependency Property Property Consumes data and optionally sends it (two-way binding) Provides data and optionally receives it (two-way binding) Value Converter Converts data format and optionally validates it in two-way data binding scenarios
{Binding} • Creates Binding objects declaratively <TextBox x:Name="Input" FontSize="36" /> <TextBlock x:Name="Output" Text="{Binding Text}" FontSize="36" /> TextBlock TextBox Binding Object Text Property Text Property
Specifying the Data Source (1) // XAML <TextBox x:Name="Input" FontSize="36" /> <TextBlock x:Name="Output" Text="{Binding Text}" FontSize="36" /> // C# Output.DataContext = Input;
Specifying the Data Source (2) <TextBox x:Name="Input" Height="24" FontSize="36" /> <TextBlock x:Name="Output" FontSize="36" Text="{Binding Text, ElementName=Input}" /> ElementName identifies another XAML element as the data source
Two-Way Data Binding <TextBox x:Name="Input" Text="40" FontSize="36" /> <Slider Minimum="0" Maximum="100" Value="{Binding Text, ElementName=Input, Mode=TwoWay}" /> Mode identifies the binding mode
INotifyPropertyChanged • Infrastructural interface used in data classes • Notifies binding object when data changes • Essential for one-way data binding! Data Class INotifyPropertyChanged Property Property Property
Implementing INotifyPropertyChanged public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandlerPropertyChanged; private decimal _salary; public decimal Salary { get { return _salary; } set { _salary = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Salary")); } } }
INotifyCollectionChanged • Infrastructural interface used in collection classes • Notifies binding object when collection changes Collection Class INotifyCollectionChanged Item IList ICollection Item IEnumerable Item
ObservableCollection • BCL collection class that implements INotifyCollectionChanged • System.Collections.ObjectModel namespace • Fires CollectionChanged events when items are added or removed (or collection is refreshed) // Ordinary collection List<string> names = new List<string>(); // Observable collection ObservableCollection<string> names = new ObservableCollection<string>();
Value Converters • Objects that convert values involved in data binding from one type to another • Implement IValueConverter • Convert – Convert from type A to type B • ConvertBack – Convert from type B to type A • Specified with Converter attribute in {Binding} expression
ImageConverter public class ImageConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { BitmapImage bi = new BitmapImage(); bi.SetSource(new MemoryStream((Byte[])value)); return bi; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Using ImageConverter <Grid.Resources> <local:ImageConverter x:Key="ImageConverter" /> </Grid.Resources> . . . <DataTemplate> <Image Source="{Binding ThumbnailImage, Converter={StaticResourceImageConverter}}" /> </DataTemplate>
Items Controls • Controls that display collections of items • Generally acquire items through data binding • Data templates control how items are rendered Panorama ListBox Pivot
ListBox Control // XAML <ListBoxFontSize="{StaticResourcePhoneFontSizeExtraLarge}" SelectionChanged="OnSelectionChanged"> <ListBoxItem Content="Item One" /> <ListBoxItem Content="Item Two" /> <ListBoxItem Content="Item Three" /> </ListBox> // C# private void OnSelectionChanged(object sender, SelectionChangedEventArgse) { int index = (sender as ListBox).SelectedIndex; ... }
ListBox with Rich Content <ListBox> <StackPanel Orientation="Horizontal"> <Image Source="Item.png" Margin="8" /> <StackPanel Orientation="Vertical"> <TextBlock Text="Item One" Style="{StaticResourcePhoneTextExtraLargeStyle}" /> <TextBlock Text="Convallisdapibus non utjusto" Style="{StaticResourcePhoneTextAccentStyle}" /> </StackPanel> </StackPanel> . . . </ListBox>
ListBox Data Binding // XAML <ListBox x:Name="List" FontSize="{StaticResourcePhoneFontSizeExtraLarge}" /> // C# string[] items = { "Item One", "Item Two", "Item Three" }; List.ItemsSource= items;
ListBox Data Templates <ListBox x:Name="List"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Source}" Margin="8" /> <StackPanel Orientation="Vertical"> <TextBlock Text="{Binding Title}" Style="..." /> <TextBlock Text="{Binding Description}" Style="..." /> </StackPanel> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
Providing a Data Source public class Item { public string Source { get; set; } public string Title { get; set; } public string Description { get; set; } } ObservableCollection<Item> items = new ObservableCollection<Item>(); items.Add(new Item { Source = "Item.png", Title = "Item One", Description = "Convallisdapibus non utjusto" }); items.Add(new Item { Source = "Item.png", Title = "Item Two", Description = "Convallisdapibus non utjusto" }); items.Add(new Item { Source = "Item.png", Title = "Item Three", Description = "Convallisdapibus non utjusto" }); List.ItemsSource = items;
The SelectedItem Property • SelectedIndex gets and sets index of selected item • -1 means no item is currently selected • SelectedItem property gets reference to item that is currently selected – with strong typing • null means no item is currently selected // SelectedItem returns an Item reference because ListBox // is bound to collection of Item objects Item item = List.SelectedItem as Item; String title = item.Title; // e.g., "Item One"
MVVM • Model-View-ViewModel • Popular design pattern used by teams doing WPF, Silverlight, and phone development • Loose coupling; separation of concerns • Enhanced testability and "Blendability" • Based on Martin Fowler's Presentation Model pattern • Devised in 2005 by Microsoft's John Gossman • WP7 lacks rich MVVM support (commanding)
Model-View-ViewModel Model ViewModel View Model Class Property Control Model Class Data Binding Model Class Property Control
ViewModel Example public class ViewModel { public ObservableCollection<Item> Items { get; set; } public ViewModel() { Items = new ObservableCollection<Item>(); Items.Add(new Item { Source = "Item.png", Title = "Item One", Description = "..." }); Items.Add(new Item { Source = "Item.png", Title = "Item Two", Description = "..." }); Items.Add(new Item { Source = "Item.png", Title = "Item Three", Description = "..." }); } }
Binding to a ViewModel <Grid ...> <Grid.Resources> <local:ViewModel x:Key="ItemsViewModel" /> </Grid.Resources> <ListBox x:Name="List" ItemsSource="{Binding Items}" DataContext="{StaticResourceItemsViewModel}"> <ListBox.ItemTemplate> <DataTemplate> ... </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>