1. Trang chủ
  2. » Công Nghệ Thông Tin

programming windows phone 7 phần 8 potx

102 263 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 102
Dung lượng 1,52 MB

Nội dung

studentDisplay.Visibility = Visibility.Visible; else if (touchPoint.Action == TouchAction.Up) studentDisplay.Visibility = Visibility.Collapsed; } } } As you run your fingers across the bars, you can see the student that each bar represents: A Card File Metaphor With the previous GpiBarChart program, in a sense we’ve managed to fit all the students onto a single screen, but the information is limited. Is there a way to get more information on the screen? One popular metaphor for displaying data is the card file. Normally only part of each card is visible but such a program also includes a facility for viewing an entire card. In preparation for this job, I created a new panel in the Petzold.Phone.Silverlight library. In some ways this panel is similar to the UniformStack panel that I described in Chapter 9. Like UniformStack, this new panel gives all of its children an equal amount of space. But unlike UniformStack, this new panel actually overlaps its children if necessary to fit them all in the available space. For that reason, it’s called OverlapPanel. OverlapPanel defines an Orientation property and arranges its children either horizontally or vertically. If OverlapPanel is arranging its children horizontally, each child is positioned slightly to the right of the child before it, leaving the left-most sliver of the previous child visible. For a vertical orientation, the top of each child is visible. If there are very many children, then that visible sliver will become very small. To make OverlapPanel more useful, it should be possible to specify that the sliver be at least a minimum height or width, even if that causes the contents of the panel to overrun the available space. In possibly going beyond the space available for it, the OverlapPanel behaves much like a regular StackPanel. A ScrollViewer will be necessary to view all the items. 699 OverlapPanel defines two properties, Orientation and MinimumOverlap: Silverlight Project: Petzold.Phone.Silverlight File: OverlapPanel.cs (excerpt) public class OverlapPanel : Panel { Size maxChildSize = new Size(); public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(OverlapPanel), new PropertyMetadata(Orientation.Horizontal, OnAffectsMeasure)); public static readonly DependencyProperty MinimumOverlapProperty = DependencyProperty.Register("MinimumOverlap", typeof(double), typeof(OverlapPanel), new PropertyMetadata(0.0, OnAffectsMeasure)); public Orientation Orientation { set { SetValue(OrientationProperty, value); } get { return (Orientation)GetValue(OrientationProperty); } } public double MinimumOverlap { set { SetValue(MinimumOverlapProperty, value); } get { return (double)GetValue(MinimumOverlapProperty); } } static void OnAffectsMeasure(DependencyObject obj, DependencyPropertyChangedEventArgs args) { (obj as OverlapPanel).InvalidateMeasure(); } … } Changes to either of these two properties causes a call to InvalidateMeasure, which initiates a new layout pass. The MeasureOverride method first enumerates through all its children to obtain the maximum child size. Of course, when you use OverlapPanel with an ItemsControl or ListBox, all the children will probably have the same size. 700 Silverlight Project: Petzold.Phone.Silverlight File: OverlapPanel.cs (excerpt) protected override Size MeasureOverride(Size availableSize) { if (Children.Count == 0) return new Size(0, 0); maxChildSize = new Size(); foreach (UIElement child in Children) { if (Orientation == Orientation.Horizontal) child.Measure(new Size(Double.PositiveInfinity, availableSize.Height)); else child.Measure(new Size(availableSize.Width, Double.PositiveInfinity)); maxChildSize.Width = Math.Max(maxChildSize.Width, child.DesiredSize.Width); maxChildSize.Height = Math.Max(maxChildSize.Height, child.DesiredSize.Height); } if (Orientation == Orientation.Horizontal) { double maxTotalWidth = maxChildSize.Width * Children.Count; double minTotalWidth = maxChildSize.Width + MinimumOverlap * (Children.Count - 1); if (Double.IsPositiveInfinity(availableSize.Width)) return new Size(minTotalWidth, maxChildSize.Height); if (maxTotalWidth < availableSize.Width) return new Size(maxTotalWidth, maxChildSize.Height); else if (minTotalWidth < availableSize.Width) return new Size(availableSize.Width, maxChildSize.Height); return new Size(minTotalWidth, maxChildSize.Height); } // Orientation = Vertical double maxTotalHeight = maxChildSize.Height * Children.Count; double minTotalHeight = maxChildSize.Height + MinimumOverlap * (Children.Count - 1); if (Double.IsPositiveInfinity(availableSize.Height)) return new Size(maxChildSize.Width, minTotalHeight); if (maxTotalHeight < availableSize.Height) return new Size(maxChildSize.Width, maxTotalHeight); else if (minTotalHeight < availableSize.Height) 701 return new Size(maxChildSize.Width, availableSize.Height); return new Size(maxChildSize.Width, minTotalHeight); } The method then splits into two different sections depending on the Orientation property. For example, for the vertical orientation (which I’ll be using in the example below), the method calculates a maxTotalHeight, when all the children are side-by-side without overlap, and a minTotalHeight, when the children are overlapped to the maximum extent. If the available height is not infinite (a possibility handled separately), then the available height is either greater than maxTotalHeight or between minTotalHeight and maxTotalHeight, or less than minTotalHeight. If all the children can fit side-by-side in the available space, then that’s the space requested. But the method never requests less height than it needs to display all the children. The ArrangeOverride method is somewhat simpler. The increment value is the width or height of the sliver of each child that will always be visible: Silverlight Project: Petzold.Phone.Silverlight File: OverlapPanel.cs (excerpt) protected override Size ArrangeOverride(Size finalSize) { if (Children.Count == 0) return finalSize; double increment = 0; if (Orientation == Orientation.Horizontal) increment = Math.Max(MinimumOverlap, (finalSize.Width - maxChildSize.Width) / (Children.Count - 1)); else increment = Math.Max(MinimumOverlap, (finalSize.Height - maxChildSize.Height) / (Children.Count - 1)); Point ptChild = new Point(); foreach (UIElement child in Children) { child.Arrange(new Rect(ptChild, maxChildSize)); if (Orientation == Orientation.Horizontal) ptChild.X += increment; else ptChild.Y += increment; } return finalSize; } 702 The StudentCardFile project has references to the Petzold.Phone.Silverlight and ElPasoHighSchool libraries. The MainPage.xaml file includes the StudentBodyPresenter in the Resources collection: Silverlight Project: StudentCardFile File: MainPage.xaml (excerpt) <phone:PhoneApplicationPage.Resources> <elpaso:StudentBodyPresenter x:Key="studentBodyPresenter" /> </phone:PhoneApplicationPage.Resources> The content area is rather simple, containing only a ScrollViewer and an ItemsControl. The ItemsPanel property of the ItemsControl references the OverlapPanel with two properties set: Silverlight Project: StudentCardFile File: MainPage.xaml (excerpt) <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" DataContext="{Binding Source={StaticResource studentBodyPresenter}, Path=StudentBody}"> <ScrollViewer> <ItemsControl ItemsSource="{Binding Students}"> <ItemsControl.ItemTemplate> <DataTemplate> <local:StudentCard /> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <petzold:OverlapPanel Orientation="Vertical" MinimumOverlap="24" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </ScrollViewer> </Grid> The simplicity of the markup here is mostly a result of the DataTemplate property of the ItemsControl being set to another control named StudentCard. StudentCard derives from UserControl. Deriving from UserControl is a common technique for creating a control to serve as a DataTemplate. If you ignore the ellipses (…) below, this is a very straightforward assemblage of a TextBlock and Image elements, with a collapsed Rectangle used as a dividing line: 703 Silverlight Project: StudentCardFile File: StudentCard.xaml (excerpt) <UserControl x:Class="StudentCardFile.StudentCard" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" Width="240" Height="240"> … <Border BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="1" Background="{StaticResource PhoneChromeBrush}" CornerRadius="12" Padding="6 0"> … <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="{Binding FullName}" /> <Rectangle Grid.Row="1" Fill="{StaticResource PhoneAccentBrush}" Height="1" Margin="0 0 0 4" /> <Image Grid.Row="2" Source="{Binding PhotoFilename}" /> <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center"> <TextBlock Text="GPA = " /> <TextBlock Text="{Binding GradePointAverage}" /> </StackPanel> </Grid> </Border> </UserControl> 704 The cards are listed down the left side of the display but only the top of each card is visible. Conveniently, the top of each card is a TextBlock displaying the student’s name: I set MinimumOverlap to a value sufficient to display this TextBlock. As you scroll down to the bottom, you’ll see that the bottom card is entirely visible: 705 That’s great if you want to look at the very last card, but rather deficient otherwise. What we need is a way to selectively bring a particular card into view. One approach might be to change the Canvas.ZIndex attached property of a particular card. Or, the whole deck of cards might be re-ordered to move a particular card to the topmost position. I decided I wanted a selected card to slide out of the deck when it’s touched, and then slide back when the card is touched again, or when another card is touched. As you start integrating other code with ScrollViewer, you’ll discover that ScrollViewer tends to hog the Manipulation events. Obviously ScrollViewer needs these Manipulation events for its own scrolling logic. But that makes it difficult for visual descendents of the ScrollViewer (such as these StudentCard elements) to process Manipulation events of their own for sliding in and out of the deck For that reason, I decided that StudentCard would install a handler for the low-level Touch.FrameReported event, and to use that to toggle a dependency property named IsOpen. Here’s that property in the StudentCard code-behind file: Silverlight Project: StudentCardFile File: StudentCard.xaml.cs (excerpt) public partial class StudentCard : UserControl { … public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", 706 typeof(bool), typeof(StudentCard), new PropertyMetadata(false, OnIsOpenChanged)); … bool IsOpen { set { SetValue(IsOpenProperty, value); } get { return (bool)GetValue(IsOpenProperty); } } … } I’ll show you the property-changed handler for IsOpen shortly. When you touch one instance of StudentCard, it is supposed to slide out of the deck, but if another card is currently exposed, that card should slide back into the deck. If the CardFile class is to handle this logic on its own, each instance of CardFile needs access to all the other instances. For that reason, I defined a static field of type List to maintain these instances: Silverlight Project: StudentCardFile File: StudentCard.xaml.cs (excerpt) public partial class StudentCard : UserControl { static List<StudentCard> studentCards = new List<StudentCard>(); … public StudentCard() { InitializeComponent(); studentCards.Add(this); } … } Each new instance simply adds itself to the collection. It also became apparent to me that each individual StudentCard instance does not need its own handler for the Touch.FrameReported event. All instances could share the same static handler installed in the static constructor and referencing static fields: Silverlight Project: StudentCardFile File: StudentCard.xaml.cs (excerpt) public partial class StudentCard : UserControl { … static int contactTime; static Point contactPoint; … static StudentCard() { 707 Touch.FrameReported += OnTouchFrameReported; } … static void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { TouchPoint touchPoint = args.GetPrimaryTouchPoint(null); if (touchPoint != null && touchPoint.Action == TouchAction.Down) { contactPoint = touchPoint.Position; contactTime = args.Timestamp; } else if (touchPoint != null && touchPoint.Action == TouchAction.Up) { // Check if finger is directly over StudentCard or child DependencyObject element = touchPoint.TouchDevice.DirectlyOver; while (element != null && !(element is StudentCard)) element = VisualTreeHelper.GetParent(element); if (element == null) return; // Get lift point and calculate difference Point liftPoint = touchPoint.Position; double distance = Math.Sqrt(Math.Pow(contactPoint.X - liftPoint.X, 2) + Math.Pow(contactPoint.Y - liftPoint.Y, 2)); // Qualify as a Tap if distance < 12 pixels within 1/4th second if (distance < 12 && args.Timestamp - contactTime < 250) { // Enumerate StudentCard objects and set IsOpen property foreach (StudentCard studentCard in studentCards) studentCard.IsOpen = (element == studentCard && !studentCard.IsOpen); } } } … } With a little experimentation, I determined that I wanted a tap to qualify as a touch and release with ¼ second where the touch point moves less than 12 pixels. That seemed to be about right and still allow flicks to be recognized by the ScrollViewer. At the bottom of this method a foreach loop enumerates through all the StudentCard objects and sets the IsOpen property on each one. IsOpen is always set to false if the StudentCard is not the touched element, and IsOpen is also set to false if IsOpen is currently true. Otherwise, if the StudentCard object is the touched element, and IsOpen is currently false, then it’s set to true. Of course, as a dependency property, IsOpen property-changed handlers will only be called if the property is truly changing. 708 [...]... xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns :phone= "clr-namespace:Microsoft .Phone. Controls;assembly=Microsoft .Phone" xmlns:shell="clr-namespace:Microsoft .Phone. Shell;assembly=Microsoft .Phone" xmlns:controls="clr­ namespace:Microsoft .Phone. Controls;assembly=Microsoft .Phone. Controls" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:d="http://schemas.microsoft.com/expression/blend/20 08" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"... and you’ll be a happier and better Silverlight and Windows Phone 7 programmer 71 1 Chapter 18 Pivot and Panorama Silverlight applications that need to present large amounts of information to the user have traditionally used a page-oriented navigation structure On the phone, however, a division of your program into pages might not be the best approach The phone s portrait form factor, the ease of multi-touch,... paragraph from a well-known novel: Silverlight Project: PivotDemonstration File: MainPage.xaml (excerpt) /71 78 /71 78- 8.txt > For a long time I used to go to bed early Sometimes, when I had put out my candle, my eyes would close so quickly that I had not even time to say "I'm going... of layout Two such alternatives are available in Windows Phone 7 in new controls named Pivot and Panorama Both Pivot and Panorama are in the Microsoft .Phone. Controls library and any program that uses these controls will need a reference to that DLL The controls are defined in the Microsoft .Phone. Controls namespace with subsidiary components in Microsoft .Phone. Controls.Primitives, but it’s unlikely you’ll... xmlns:d="http://schemas.microsoft.com/expression/blend/20 08" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth=" 480 " d:DesignHeight= "76 8" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="PortraitOrLandscape" Orientation="Portrait" shell:SystemTray.IsVisible="True">... Studio allows you to create an project of type Windows Phone Pivot Application or Windows Phone Panorama Application, and you can surely experiment with those For the demonstration programs in this chapter I took a different approach Here’s a MainPage.xaml file from a project named PivotDemonstration I created this project normally, that is, by selecting Windows Phone Application from the New Project dialog... report that the music library on the phone had no music… except if I first ran an XNA program I am told this is a bug in the initial release of Windows Phone 7, and I decided to work around this bug by making the program accessible from the Games hub on the phone To do this I set the following attribute in the App tag of the WMAppManifest.xml file: Genre="apps.games" 73 0 I also gave the program Background.png... FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" Width="240" Height="240"> . Header="TextBlock"> <ScrollViewer> <! from http://www.gutenberg.org/files /71 78 /71 78- 8.txt > <TextBlock TextWrapping="Wrap"> For a long time I used to go to. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns :phone= "clr-namespace:Microsoft .Phone. Controls;assembly=Microsoft .Phone& quot; xmlns:shell="clr-namespace:Microsoft .Phone. Shell;assembly=Microsoft .Phone& quot; xmlns:controls="clr- namespace:Microsoft .Phone. Controls;assembly=Microsoft .Phone. Controls". mc:Ignorable="d" d:DesignWidth=" 480 " d:DesignHeight=" ;76 8& quot; FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}"

Ngày đăng: 13/08/2014, 08:20