Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Part 22: Animating the Reel Grid with a Storyboard

Download

Right click “Save as…”

Source Code: http://aka.ms/absbeginnerdevwp8
PDF Version: http://aka.ms/absbeginnerdevwp8pdf

The final step will be fun ... we'll animate the reel object on our record audio screen. We want it to rotate as we're recording ... that's a nice visualization of what the app is doing, a nice visual cue to the user that they are in the middle of an important operation with the app.

Our game plan in this lesson:

  1. We'll create a Storyboard that includes an animation to rotate the Ellipse and the other objects.
  2. We'll programmatically start and stop the animation

... but first we'll need to learn a little about creating animations on the Windows Phone.

 

1. Declaratively define the animation

Before we dive too deeply into the topic of animation, I want to discuss it at a high level. Once we're comfortable with how animation works in XAML UI frameworks, we'll have a bit more confidence to apply a specific animation to rotate our reel.

First, its important to realize that XAML controls like the Grid, Button, etc. are defined separately from animation definitions. You can create an animation that is applied to different controls as needed. An animation is associated with a control and then triggered by some event. In our case, we've already defined the Grid and the shapes that will comprise the reel. We'll soon define an animation and associate the two, then we'll write C# code to trigger the animation. Again, we'll do all of this before the end of this lesson.

Animations are made up of a Storyboard object and at least one Animation object.

Let's start with the Animation. First, there are several different types of animation data types. We're using a DoubleAnimation data type because we want to move a property (the Rotation property) from 0.0 to 360.0—the angle of the animation every 4 seconds. In other words, we want the Grid to rotate every 4 seconds. Besides the DoubleAnimation, there's also a ColorAnimation class for animating between two colors, and a PointAnimation for modifying an objects X Y coordinate or its size.

An Animation is basically a timeline combined with a result. The results are determined by the specific Animation class you pick like we just talked about. It's important to know that all Animation classes inherit from a Timeline class which confers properties related to timing of the animation ... the Begin time, allowing you to delay the start of animation, perhaps waiting for other animations on the storyboard to begin or complete, a Duration property that effects how long the animation should take before delivering the desired result—how long should it take for our fade-in effect, in this case. There are also AutoReverse and RepeatBehavior properties, which do what they suggest.

A Storyboard is a collection of one or more Animations. You group the Animations you want triggered by a specific event such as a button click, a loaded event, and so on. The Storyboard allows you to pair an Animation with a target object. The animation is just a definition of what property should be affected, when it should be affected and how long. We have to APPLY that Animation to a target object. In our case, that target object will be the Grid that contains our shapes representing the reel.

Our Storyboard will be simple since we just want one thing to happen—a rotation from 0 to 360 degrees every 4 seconds. Once we're ready to allow the Storyboard to play, we call its Begin() method. It will in turn kick off all it's child Animations and child Storyboards.

There are 4 possible transforms that you can animate as listed and diagrammed on this page:

http://msdn.microsoft.com/en-us/library/ms750596.aspx

(I realize that this article is specifically for WPF, but many of the concepts transfer to the Windows Phone API.)

  • RotateTransform
  • ScaleTransform
  • SkewTransform
  • TranslateTransform

... as well as other attributes you can change with the ColorAnimation and the like. There's a lot more to learn about animations, and for a more complete explanation of animations, I recommend you start here:

http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206955(v=vs.105).aspx

Now that you have the basics of StoryBoards and Animations, let's modify the Grid containing the shapes that comprise the reel graphic. You'll recall that the large ellipse and the smaller rectangles and ellipse are all contained in a Grid for layout purposes.

To get them to all move correctly at the same time, it is easier to just animate / rotate the grid (rather than each individual shape):

I'll add a name so I can access it programmatically, and I want to indicate that I want to entire grid and all its children to animate together, as a group, or rather as a composite:

  1. Add the programmatic name attribute.
  2. I set the RenderTransform property to CompositeTransform. Essentially, what I'm saying is that I will transform the contents of the Grid as one complete unit. I've not provided the details of HOW I'll transform the grid, just the fact that, for the purpose of performing transformations on the Grid, I want to treat the Grid and its children as a group. I'll define HOW I'll transform the grid in a Storyboard.

Near the top of the RecordAudio.xaml page (beneath the PhoneApplicationPage declaration), I will add a Resources section:

  1. We're creating a Page-level resource.
  2. We're creating a Storyboard ... because it's Page-level, we could re-use this in other spots in our page. We'll give the Storyboard a name, "RotateCircle" and once it starts, it's repeat behavior should be "Forever". We'll control start and stop programmatically. I'll access this Storyboard programmatically by it's name in a moment to start / stop it.
  3. We'll build a DoubleAnimation object. Why DoubleAnimation? As we discussed at the outset, we want to modify the Rotation property (see the Storyboard.Target property) from 0.0 to 360.0, which are numerical (and therefore will require we use the DoubleAnimation). We set the duration of the animation to 4 seconds and so, in 4 seconds, we want to increase the Rotation value starting at 0 and increasing to 360. We set the TargetName, which is the name we gave the Grid containing our reel. The TargetProperty is tricky ... we have to write it this way due to the fact that we're working with a CompositeTransform ... as I mentioned a moment ago, we want to animate the Grid AND ITS CHILDREN. Otherwise, we could simply set the TargetProperty="Rotation".

2. Programmatically start and stop the animation

What comes next is easy now that we've declaratively defined the animation. We'll start and stop the animation based on the state of the ToggleButton:

  1. While recording, start the animation.
  2. When finished recording, stop the animation.

If we were to run the application at this point, we would notice an odd behavior while recording:

 

 

The reel is not rotating in place, but rather, it is rotating around the page. This is because we didn't define the spot of origin for the transform to take place. Instead of at its default X Y position (0, 0 ... upper left-had corner), we want it to be in the middle (.5, .5), so we'll set the RenderTransformOrigin to that position:

 

 

Now when we re-run the app, the reel spins instead of traveling:

 

 

3. Deploy to physical Phone Device

Just for fun, I wanted to see this working on my Nokia Lumia 920, so I connected my device to my computer with a USB cable and used the drop-down next to the Run button on the toolbar to select Device:

I was able to run the app, record and save sounds, even hit break points just like I could using the Emulator in Visual Studio. Excellent.

 

Recap

To recap, in this lesson we learned about animation using Storyboard and the relation to Animation classes. We talked about the different types of animations and transforms you can add to your app. Then, we learned how to programmatically trigger the storyboard and how to stop it. We learned about things like how to modify the rotation animation's starting point by changing RenderTransformOrigin attribute. The big takeaway is that if you can imagine it, there's likely a way to accomplish it given the wealth of animation options in XAML and the Windows Phone API.

Now that we've finished our first version of this app, I want to challenge you to add some functionality on your own. I haven't yet added these features myself, and there's no denying that they would require a day or two to research, develop and debug, but here's what I would like you to try and add:

  1. Figure out how to add images to each item. You'll need to manipulate the data model to accommodate an associated image file, and you'll have to add image files to the project, add an Image control to the DataTemplate, and bind to it. Actually, if you watch the rest of this series you'll get some hints on how to accomplish this because we'll be binding images to tiles (among other things) as we create our second full app.
  2. Figure out how to delete items. Right now, if you add a custom sound, there's no way to get rid of it. That's an unfortunate limitation. Clint suggests you use a Context Menu from the Windows Phone Toolkit (which we'll learn about that when we work on the next app as well).
  3. Figure out how to pin sounds to the Start page. That's something I've never tried to implement, but I know there are tutorials out there that can lead you in the right direction.

Are there other features that would make this app cool? Try adding them or share them as challenges to your fellow students in the comments below the video.

Now we have to move on ... we'll begin our second app in the next lesson.

Tags:

Follow the Discussion

  • I love your videos!!!

  • @ZhouningMan: Smiley  I love your comments.

  • Could the Blend for VS an option for manipulating the animation? Anyway,  I like this tutorial very much.

  • This video series is wonderful. I was already thinking about putting images as the background tile for the sounds as my kid loves this app. It took my brain more time to figure it out than it took to actually code it. 

    The toolset in this app's series alone is enough to make a wide variety of other apps.

    Thanks!!!

  • @shenzhongwei: Yes, in fact I think that Clint started down that path originally when working with the animation bits.  There were some things left over from Blend that I took out and just coded by hand.  HOWEVER, I'm not a Blend guy and someone else might do a better job explaining how you could create an animation in Blend then apply it using C#.

    @mackenzie: Yes, Clint did a nice job blowing out the functionality to include a lot of concepts.  Thanks!

  • @BobTabor Great work!

  • PasteurPasteur

    I'm an iOS Developer trying to learn WP8, but being an "Apple guy" Microsoft technologies and ways of doing things are admittedly very foreign to me. So far, this series has been a goldmine in helping me bridge the knowledge gap. Bob, you are a fantastic instructor, and I hope you plan to do more of these videos in the future.

    1/3 of the "extra" tasks done (pin to start), and now to see if I can tackle the rest.

  • @Shinto: Thanks!

    @Pasteur: Awesome feedback.  Smiley  Glad this is helping ... I think you are exactly the kind of person Microsoft wants to recruit with this series.

  • ProgrProgr

    Hey, Bob! I don't speak english very well, but your lessons are great! Thank you wery much!
    And I have a question to you: I want to create an animation - page rotating when PhoneApplicationPage orientation changed. But i don't understand how can i do this with storyboard object. Can you help me, please?

  • Clint RutkasClint I'm a "developer"

    @Progr: http://stackoverflow.com/questions/17456894/any-straight-forward-way-to-animate-the-transition-from-portrait-to-landscape-in

  • ProgrProgr

    @Clint, Thank you very much!

  • Hey @Clint @BobTabor,

    I am trying to add an option to delete the customaudio file. The context menu seems good. And I even added a messagebox asking to confirm whether they really want to delete the file. 

    private void Delete_Click(object sender, RoutedEventArgs e)
            {
                //Check which object the event is passing here ?
    
                var selected = (sender as SoundData) as SoundData;
    
                //SoundData data = selected.SelectedItem as SoundData;
    
                MessageBoxResult result = MessageBox.Show("Do you want to delete this item ?", "Are you sure ?", MessageBoxButton.OKCancel);
                if (result == MessageBoxResult.OK)
                {
                    if (selected == null)
                    {
                        MessageBox.Show("The file doesn't exist");
                        return;
                    }
                    //Check if the file exists at location
                    if (File.Exists(selected.FilePath))
                    {
                        MessageBox.Show("Wav will be deleted");
                    }
                    else
                    {
                        using (var storageFolder = IsolatedStorageFile.GetUserStoreForApplication())
                        {
                            using (var stream = new IsolatedStorageFileStream(selected.FilePath, FileMode.Open, storageFolder))
                            {
                                //storageFolder.DeleteFile();
                            }
                        }
                    }
                }
                else
                {
                    return;
                }
                //delete it
                //update the pivot page
            }

    This is the code that I am using. There are multiple problems here.

    1. I don't exactly understand how to pass the clicked wav file SoundData data to the Delete_Click event. If I can successfully pass it, then I can check if it is valid and then I can go on and delete it. Kindly put me in the right direction on how to pass the SoundData of the longclicked wav file. I don't think I can use the SelectionChangedEvent here because it is a contextmenu and it doesnt have the right attribute. Correct me if I am wrong.

    2. After I get the data, I can just refer to the item.FilePath property to know the location and delete it ? Or do I have to use the IDisposable approach to manage the resources properly and then use IsolatedStorageFile to look into the directory and then delete the file ? Please just give me hints so that I can make up with a solution myself.

    And I remember Bob saying that we'll receive more hints about contextmenu and etc in the later videos, but sincerely I don't want to wait. I want to add an option to delete customaudio files in my soundboard app and then only move on with the tutorial.

    EDIT: Right now when I test the app to delete the wav, it says "The file doesn't exist".

  • Clint RutkasClint I'm a "developer"

    @mvpspl619: first, we're really trying to keep questions only to the topic of the video.  We love the fact you're building an application but have to keep the comments to the video.

    Rewatch / read http://channel9.msdn.com/Series/Windows-Phone-8-Development-for-Absolute-Beginners/Part-15-Playing-a-Sound-when-a-ListItem-is-Selected  We leverage the SelectionChanged event.  We see what the SelectedItem was on LongListSelector then reset it back to null.  (See #3).  I'd also actually rethink how you're doing deleting as well and make it work like Windows Phone with a context menu or a multi-select long list selector.  See / try out the Photo in WP8.  Both of these controls are in the WPToolkit (http://phone.codeplex.com).  We also talk about this toolkit a bit later on in the series.

  • Thank you, I will follow what you said. Only reason I asked my query here is because Bob suggested to add the images to tiles and option to delete using context menu in this video as it is the end of developing SoundBoard app. 

    Thanks anyway.  Smiley

  • Here an idea - make the reel rotate _backwards_ when the user presses the 'play' button and stop rotating when the sound stops without making it a toggle button (no second button press!). 

    Hint: you'll need: www.silverlightshow.net/items/How-to-invoke-method-in-the-UI-thread.aspx

    Email me at WP8DfAB-ch22 (at) Freytag.org if needed. 

  • Clint RutkasClint I'm a "developer"

    @rfreytag: neat idea, I thought about the UI and how best to make it for a beginner to develop.  bringing in the Dispatcher and UI vs background threads starts getting confusing for someone new to the platform.

  • @Clint - I see your point.  While what I implemented above works fine my next stunt did not.  I tried implementing a 'click and hold' event using a MouseLeftButtonDown + MouseLeftButtonUp + Timer and got hung up on another cross-thread exception.  Clearly I have more to learn. 

  • Clint RutkasClint I'm a "developer"
    @rfreytag: use the tap or hold event. Don't reinvent user expectation behaviors as well Smiley
  • AnhDuongAnhDuong

    I can pin the sounds to the start screen but the new tile only displays the Title of the sound and it only navigates users to the MainPage.xaml when users tap on it.
    I guess by "pinning the sound to start screen", Bob meant that we can play the sound directly on the start screen by tapping on it.
    I've done some research about secondary tiles but nothing like "playing audio using secondary tile" came up. Any suggestion?

    Thank you for reading this. Any help is appreciated.

  • Clint RutkasClint I'm a "developer"
    @AnhDuong: we didn't cover pinning in this series. While it is awesome you are extending the app, We are trying to keep the comments to the topic covered in the video. that said, I suggest looking at stackoverflow.com. They without a doubt talk about this along with dev.windowsphone.com. Big this is note your query string on the pinned tile
  • Hi,

    I am getting the following error when click the record button (around 12:50 in video timing).

    System.InvalidOperationException was unhandled by user code
    HResult=-2146233079
    Message=Cannot resolve TargetProperty (UIElement.RenderTransfom).(CompositeTransform.Rotation) on specified object.
    Source=System.Windows
    StackTrace:
    at MS.Internal.XcpImports.CheckHResult(UInt32 hr)
    at MS.Internal.XcpImports.Storyboard_Begin(Storyboard storyboard)
    at System.Windows.Media.Animation.Storyboard.Begin()
    at SoundBoard.RecordAudio.RecordAudioChecked(Object sender, RoutedEventArgs e)
    at System.Windows.Controls.Primitives.ToggleButton.OnChecked(RoutedEventArgs e)
    at System.Windows.Controls.Primitives.ToggleButton.OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    at System.Windows.DependencyObject.RaisePropertyChangeNotifications(DependencyProperty dp, Object oldValue, Object newValue)
    at System.Windows.DependencyObject.UpdateEffectiveValue(DependencyProperty property, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, ValueOperation operation)
    at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value, Boolean allowReadOnlySet)
    at System.Windows.Controls.Primitives.ToggleButton.OnToggle()
    at System.Windows.Controls.Primitives.ToggleButton.OnClick()
    at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
    at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
    at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
    InnerException:

    I researched on internet as well but no help on that. This is present in the file RecordAudio.xaml

    <phone:PhoneApplicationPage.Resources>
    <Storyboard x:Name="RotateCircle" RepeatBehavior="Forever">
    <DoubleAnimation
    Duration="0:0:4"
    To="360"
    Storyboard.TargetProperty="(UIElement.RenderTransfom).(CompositeTransform.Rotation)"
    Storyboard.TargetName="ReelGrid"
    d:IsOptimized="True" />
    </Storyboard>
    </phone:PhoneApplicationPage.Resources>

  • Clint RutkasClint I'm a "developer"

    @habula: well, lets work on reading what that exception says.  Basically it says the storyboard is invalid because it can't figure out where the CompositeTransform.Rotation is on "ReelGrid".

    This makes me wonder did you add in RenderTransform into the XAML (more or less the first step from the text)?

    <Grid 
        Name="ReelGrid"
        Height="200" 
        Width="200"
        RenderTransformOrigin=".5, .5" >
        <Grid.RenderTransform >
            <CompositeTransform/>
        </Grid.RenderTransform>

    Also, when you hit stuff like this, compare your code to our code.  Each lesson has a dedicated snapshot of the source code Smiley

  • Hi Clint,

    Thanks for the response. Actually there was a spelling mistake. We looked again at the error message carefully (Message=Cannot resolve TargetProperty (UIElement.RenderTransfom).(CompositeTransform.Rotation) on specified object.) and realized that it should be Transform. Once again thank you for your time Smiley .

  • Clint RutkasClint I'm a "developer"

    @habula: no problem.  glad it is resolved now!

  • Hi Clint,

    I'm trying to implement the delete functionality with a ContextMenu and I had some questions around it. You've mentioned in the comments above that you'd like to limit the comments here to the material covered in the tutorials, so I was wondering if there's a different place here that we can ask questions related to extending the functionality of the app, any issues encountered and design related questions? Great series, BTW. I've been meaning to get into Windows Phone app development for a while and this was the perfect place to start.

    Raza.

  • Clint RutkasClint I'm a "developer"

    @RazaNaqvi: Hey Raza, we really don't have a place dedicated to this.  one place would be the channel 9 coffee house forums.  If you are running into problems with APIs, I suggest http://dev.windowsphone.com and www.stackoverflow.com

  • I'm trying to add the features suggested at the end of the video but I'm having some problems.

    I've thought to use a ContextMenu to make the "Pin to start" and the "delete sound" stuff, so this ContextMenu has two MenuItem and each one should call a different method. Of cours I've put the ContextMenu inside the SoundTileDataTemplate, so that each tile has this context menu.

    Now, here's the problem: how can I pass the SoundData to the method to correctly delete it?

    If I use the Tap event, there's no way to pass parameters using XAML (and I can't even navigate to the LongListSelector's selected item because the highest parent of the MenuItem is the Popup which hasn't any parent).

    I've found something online using Command and CommandParameter but I've got no idea on how to make it work.

     

    As a side question, is there any way to enable the second MenuItem only if we're working with the custom sounds?
    Something like a conditional rendering for that item, based on the lists' item type?

     

    Thank you Smiley

  • Clint RutkasClint I'm a "developer"

    @StepTNT: while I think it is amazing you're extending the application, I have to keep the responses to the topic covered in the video.  I suggest asking the questions over at http://dev.windowsphone.com or www.stackoverflow.com

  • @Clint: I know, but StackOverflow is not WP friendly Sad I'll head to dev.windowsphone.com, have a nice day Smiley

  • Clint RutkasClint I'm a "developer"

    @StepTNT: really?  I've never had an issue with stackoverflow.  Could you email me an example where you've had a bad experience asking a question?  clint.rutkas@microsoft.com

  • I love this series (and did love the C#-series as well) because of the thorough structure and clear instructions! Smiley

    The SoundBoard-app works just fine on the emulator for me, but now I've run into a problem, when running this app on a Windows 8 Phone: When I try to save a custom sound, I get an error on line 78 of the RecordAudio.xml.cs (isoStore.MoveFile-statement), even when using your version of the app:

    isoStore.MoveFile(_tempFileName, soundData.FilePath);

    The error message:

    System.IO.IsolatedStorage.IsolatedStorageException was unhandled by user code
      HResult=-2146233264
      Message=Operation not permitted.
      Source=mscorlib
      StackTrace:
           at System.IO.IsolatedStorage.IsolatedStorageFile.MoveFile(String sourceFileName, String destinationFileName)
           at SoundBoard.RecordAudio.FileNameCompleted(Object sender, PopUpEventArgs`2 e)
           at Coding4Fun.Toolkit.Controls.PopUp`2.OnCompleted(PopUpEventArgs`2 result)
           at Coding4Fun.Toolkit.Controls.UserPrompt.OkClick(Object sender, RoutedEventArgs e)
           at System.Windows.Controls.Primitives.ButtonBase.OnClick()
           at System.Windows.Controls.Button.OnClick()
           at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
           at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
           at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
      InnerException:

    Since the error message says "Operation not permitted", I guess the app doesn't have permission to write new files? But how do I grant that permission?
    (It's a Nokia id101 Lumia developer device).

    Thanks, Simon

  • Clint RutkasClint I'm a "developer"

    @manyasone: does our version of the application work for you?

  • with your "soundboard_complete_from_videos" i've got the same problem: it works with the emulator, but not with the phone.

    (edit: everything but saving a custom sound works with the phone; when i tip the disc icon, i get the error message)

  • Clint RutkasClint I'm a "developer"

    @manyasone: is this app you made or the sample source code we provide on codeplex?

  • Clint RutkasClint I'm a "developer"

    @manyasone: I think it could be a double click on the OK button.  I could only get this to happen once and on my very first try.

  • @BobTabor :Really Really great video for the tutorial Smiley
    i am the information System Engineering Degree student, taking Final Year Project is about ~ Online Audio distribution. (it's challenge to build an WP8 apps)
    Like people can record their own audio and share to the world.

    Your tutorial help me a lot, i'm totally new in to build the Mobile apps.
    Since in the tutorial is Local Apps, have any suggestion video to build apps is online base?
    such as using WCF to sent and retrieve data from online database?

  • Clint RutkasClint I'm a "developer"

    @AnthonyLaw:  There are a ton of online resources for building those style applications.  Right now for absolute beginners, our current list is: http://channel9.msdn.com/posts/Beginner

  • Hi, first thanks for you guys contributing this series.

    Somehow, I have a problem when I implement the feature of deleting one "mine" item, and no matter what i do it cannot refresh the custom page. I have re-loaded the data from IsolatedStorageSettings which has correct, and Navigate the Uri, but this doesn't work, the deleted item always lie here, when i click it, you know, the application crashes down.What issue do I miss?

    Thanks again.

  • Clint RutkasClint I'm a "developer"

    @YueDaWei: I'm not sure what you are doing and that is a feature that is outside the scope of the application we built.  I'd leverage the debugger and start stepping through your code and validating what you are doing.  Mimic what we did to add items to the settings, just flip it from adding to removing.

  • Andres Rojasarojaspa MVP SharePoint

    @mvpspl619:Hello.. I tried this code and it works for me:

    1. Create a new page with a pivot page template

    <!--LayoutRoot is the root grid where all page content is placed-->

    <Grid x:Name="LayoutRoot" Background="Transparent">

    <phone:Pivot Title="{Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}">

    <phone:PivotItem Header="delete files">

    <phone:LongListSelector x:Name="LLCustomFiles"

    LayoutMode="List"

    SelectionChanged="DeleteCustomFilesListSelector_SelectionChanged"

    ItemTemplate="{StaticResource DeleteCustomFilesTemplate}"

    >

    </phone:LongListSelector>

    </phone:PivotItem>

    </phone:Pivot>

    </Grid>

    2. Create a new local ítem template like the mainpage.xaml to binding information.

    <phone:PhoneApplicationPage.Resources>

    <DataTemplate x:Name="DeleteCustomFilesTemplate">

    <StackPanel Margin="0,0,0,17">

    <TextBlock Text="{Binding Title}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"></TextBlock>

    <TextBlock Text="{Binding FilePath}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"></TextBlock>

    </StackPanel>

    </DataTemplate>

    </phone:PhoneApplicationPage.Resources>

    3. Create a event for LoongListSelector SelectionChanged. Here I'm going to send you the entire code begin for page created in point 1.

     

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Net;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Navigation;

    using Microsoft.Phone.Controls;

    using Microsoft.Phone.Shell;

    using SoundBoard.ViewModels;

    using System.IO.IsolatedStorage;

    using Newtonsoft.Json;

    using SoundBoard.Resources;

    namespace SoundBoard

    {

    publicpartialclassDeleteCustomFiles : PhoneApplicationPage

    {

    public DeleteCustomFiles()

    {

    InitializeComponent();

    LoadCustomFiles();

    BuildLocalizeApplicationBar();

    }

    privatevoid BuildLocalizeApplicationBar()

    {

    ApplicationBar = newApplicationBar();

    ApplicationBarIconButton homeAppBar =

    newApplicationBarIconButton();

    homeAppBar.IconUri = newUri("/Assets/AppBar/check.png", UriKind.Relative);

    homeAppBar.Text = AppResources.AppBarGoHome;

    homeAppBar.Click += homeAppBar_Click;

    ApplicationBar.Buttons.Add(homeAppBar);

    }

    void homeAppBar_Click(object sender, EventArgs e)

    {

    NavigationService.Navigate(newUri("/MainPage.xaml", UriKind.RelativeOrAbsolute));

    }

    privatevoid LoadCustomFiles()

    {

    SoundGroup data;

    string dataFromAppSettings;

    if (IsolatedStorageSettings.ApplicationSettings.TryGetValue(SoundModel.CustomSoundKey, out dataFromAppSettings))

    {

    data = JsonConvert.DeserializeObject<SoundGroup>(dataFromAppSettings);

    }

    else

    {

    data = newSoundGroup();

    data.Title = "delete files";

    }

    LLCustomFiles.ItemsSource = data.Items;

    }

    privatevoid DeleteCustomFilesListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)

    {

    LongListSelector selector = sender asLongListSelector;

    // verificando que nuestro sender sea in LongListSelector

    if (selector == null)

    return;

    SoundData data = selector.SelectedItem asSoundData;

    //verificamos que nuestro sender es un SoundData

    if (data == null)

    return;

    try

    {

    using (IsolatedStorageFile storageFile = IsolatedStorageFile.GetUserStoreForApplication())

    {

    storageFile.DeleteFile(data.FilePath);

    }

    // Remove the SoundData from App.ViewModel.CustomSounds

    int i = GetSoundDataIndex(data);

    App.ViewModel.CustomSounds.Items.RemoveAt(i);

    // Save the list of CustomSounds to IsolatedStorage.ApplicationSettings

    var dataSerialize = JsonConvert.SerializeObject(App.ViewModel.CustomSounds);

    IsolatedStorageSettings.ApplicationSettings[SoundModel.CustomSoundKey] = dataSerialize;

    IsolatedStorageSettings.ApplicationSettings.Save();

    // We'll need to modify our SoundModel to retrieve CustomSounds

    // from IsolatedStorage.ApplicationSettings

    NavigationService.Navigate(newUri("/MainPage.xaml", UriKind.RelativeOrAbsolute));

    }

    catch (IsolatedStorageException ex)

    {

    MessageBox.Show(ex.Message);

    }

     

    }

    privateint GetSoundDataIndex(SoundData data)

    {

    List<SoundData> listSound = App.ViewModel.CustomSounds.Items;

    int iReturn = 0;

    for (int i = 0; i < listSound.Count; i++)

    {

    if (listSound[i].FilePath.Equals(data.FilePath))

    {

    iReturn = i;

    break;

    }

    }

    return iReturn;

    }

    }

    }

    I hope this piece of code works for you.

     

    Regards,

     

    Andres Rojas

    MVP SharePoint

  • feraaskferaask

    Hey Bob, love the series so far! Really helpful in learning windows phone development :)

    I've been trying out the recording app and I think I've found a bug that isn't talked about in the video. For some reason whenever I try to play a piece of custom audio that I recorded for a second time I am getting the following exception:
    "An exception of type 'System.IO.IsolatedStorage.IsolatedStorageException' occurred in mscorlib.ni.dll but was not handled in user code"

    on the line:
    using (var stream = new IsolatedStorageFileStream(data.FilePath, FileMode.Open, storageFolder))

    inside the LongListSelector_SelectionChanged method in MainPage.xaml.cs

    I've tried it on the provided complete source code that I downloaded as well and it has the same bug although I have no clue why it is happening. Any ideas?

  • feraaskferaask

    Well I was able to fix the issue by creating a private instance field in the MainPage.xaml.cs which holds the filePath of the last played audio file which I then use to compare with the new filePath every time the user clicks on a tile to play the audio and if the file's are the same I just call the AudioPlayer.Play() method for the media element instead of reopening the filestream or setting up the URI again. Basically:

    if (data.FilePath.Equals(lastFilePath))
    AudioPlayer.Play();
    else
    {
    lastFilePath = data.FilePath;
    if (File.Exists(data.FilePath))
    {
    AudioPlayer.Source = new Uri(data.FilePath, UriKind.RelativeOrAbsolute);
    }
    else
    {
    using (var storageFolder = IsolatedStorageFile.GetUserStoreForApplication())
    {
    using (var stream = new IsolatedStorageFileStream(data.FilePath, FileMode.Open, storageFolder))
    {
    AudioPlayer.SetSource(stream);
    }
    }
    }
    }

    Although this is somewhat of a "hacky" fix I cannot for the life of me figure out what is causing it to crash the second time you play a custom audio file. (I think it has to do with the file stream remaining Undisposed and then trying to open it again i.e. opening the same file stream twice or something but I'm not sure why...) From the method stub of the IsolatedStorageFileStream description the only ways for it to throw an exception are:

    isf has been disposed. -or- Path is badly formed. -or- Path is null. -or- Isf is null. -or- The directory in path does not exist.-or- No file was found and the mode is set to System.IO.FileMode.Open.

    but stepping through the debugger I can see all the values passed into the method are the same from the first time the click event is called and the second time where it throws the exception. In any case I'm glad to have at least found a fix for anyone else who has the issue. Hope this helps!

  • Did anyone manage to add images to the "Tiles".. I'm new to the WP8 development, can anyone provide me the way forward to do this???

  • AmanAman

    Hi Bob,

    Just wanted to let you know I took on that challenge, and coded by myself deleting the custom items tile as well as the actual file, AND implemented a "Favorites" pivot item that you could add sounds to from any of the pages. That last part was really fun :)

    THANKS SO MUCH FOR THIS GUIDE!

  • @Aman: Awesome!  Good for you!  I need to incorporate more of that sort of thing in the future.

  • NafisNafis

    Thanks for these great videos, Bob. But I am stuck in something. I have animated the reelgrid. Now I want to rotate the circle dynamically. You have given the "To" property of the storyboard "360". I want it will change after every click. For example, at first click 360, 2nd click 150, 3rd click 270 etc..... how could I get this??

  • private void Delete_Click(object sender, RoutedEventArgs e)
    {
    MessageBoxResult message = MessageBox.Show("This Sound will be deleted permanent.", "Delete?", MessageBoxButton.OKCancel);
    if (message == MessageBoxResult.OK)
    {
    using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
    {
    var selectedSound = (sender as MenuItem).DataContext as SoundData;
    SpeakerLongListSelector.ItemsSource.Remove(selectedSound);

    // Delete selectedSound.FilePath
    if (isoStore.FileExists(selectedSound.FilePath))
    {
    isoStore.DeleteFile(selectedSound.FilePath);

    }

    var data = JsonConvert.SerializeObject(App.ViewModel.CustomSounds);

    IsolatedStorageSettings.ApplicationSettings[SoundModel.CustomSoundKey] = data;
    IsolatedStorageSettings.ApplicationSettings.Save();

    }
    }

    Dear Bob!

    I just try to write down the Delete_Click code to delete item in the LongListSelector. The problem I got is the sound *.wav can not be deleted when click "delete". It just disappear when i quit and reopen the app. So mayb something need to be refresh after deleting item?

     

Remove this comment

Remove this thread

close

Comment on the Post

Already have a Channel 9 account? Please sign in