Faux Video Scrubber

Category: windows metro apps csharpvb

Question

steve_aras on Thu, 18 Oct 2012 16:57:04


What XAML hierarchy would you use to create a Faux Video Scrubber seen below?  I am not editting video.  This drawing is the only way I could think of to convey the concept.  The concept is closer to a photo gallery slide show.  We did A, then we did B, then we did C.  I also have other data that is associated with the A,B,C...images. (Start Time, End Time, Name, ect..)

A complex video scrubber would allow you to scroll both the list of images AND the scrubber bar. I don't need it to be that complex.  I want the "green" scrubber bar to be in a fixed location at the center of the screen, and as one slides the filmstrip(a.k.a. list of images) they will pass by the scrubber. 

I need all the images to be able to pass by the scrubber.  I've tried many itterations of this concept using varied controls.  Currently I'm looking at a ListView that I populate with "empty" frames at the beginnig and the end of the "film strip".  That way I can swipe the strip such that A and F can pass by the scrubber.

I've also considered dropping the ListView onto a rectangle...then try using uiElement Manipulation on the parent rectangle.  What I need is.... for the list of images to scroll/slide by that fixed point.... and be able to fire an event or somehow constantly understand the position of the list item in relation to the fixed target.

So at any time I can say:  The fixed target is 3/4s of the way across the "C" image.  The "C" Image has a Start Time of 8:00AM and an End Time of 9:00AM..... so the scrubber bar must be at 8:45AM. 

Of course, after the user finishes scrolling, the App at it's discretion, can move the Film Strip back to current time.    Try not to think overly complex....like taking the photos resolution into consideration.  The properties of the photo do not come into play.

If you could just give me an idea of which controls to use and their hierarchy....I'll try to strugggle to put the whole thing together.


SA


Replies

Matt Small on Fri, 19 Oct 2012 20:54:18


Here's a working solution:

http://sdrv.ms/T5Jw6z

For easy reference:

I created a scrollviewer that contains a ListView with a horizontal StackPanel as its itemspanel.  As the ListView creates each image from its template and ItemsSource, I get a reference to that item in its loaded event handler and add that to a list.  When the scrollviewer's view changes, I loop through all of the images in my images list and compare their physical location in relation to the line.  If the image is under the line, then I get that image's properties.  I determine how far past the line is the image is on the left side of the line and divide that by the total width of the line  to get a percentage.

XAML:

    <Grid Background="Black" x:Name="MainGrid">
        <StackPanel VerticalAlignment="top" HorizontalAlignment="left" Margin="10">
            <TextBlock x:Name="PictureName" Text="PictureName" FontSize="20"/>
            <TextBlock x:Name="PictureHW" Text="Height X Width" FontSize="20"/>
            <TextBlock x:Name="PicturePercentage" Text="Percentage" FontSize="20"/>
        </StackPanel>    
            <ScrollViewer Height="300" Background="White" VerticalScrollMode="Disabled" VerticalScrollBarVisibility="Disabled"  HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Visible" ViewChanged="ScrollViewer_ViewChanged_1">
                <ListView x:Name="FilmStrip">
                    <ListView.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" Height="300"/>
                        </ItemsPanelTemplate>
                </ListView.ItemsPanel>
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <Image Source="{Binding}" Height="300" Loaded="Image_Loaded_1" />
                        </DataTemplate>
                    </ListView.ItemTemplate>                    
                </ListView>
            </ScrollViewer>
        <Line StrokeThickness="5" Stroke="Purple" Fill="Purple" X1="960" X2="960" Y1="0" Y2="2000" x:Name="MyLine"></Line>
    </Grid>
C#:
  protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            List<string> MyImages = new List<string>();
            MyImages.Add(<AN_IMAGE_STRING>);
            MyImages.Add(<AN_IMAGE_STRING>);
...
            MyImages.Add(<AN_IMAGE_STRING>);
            FilmStrip.ItemsSource = MyImages;
        }

        private void ScrollViewer_ViewChanged_1(object sender, ScrollViewerViewChangedEventArgs e)
        {
            GeneralTransform GT;
            foreach (Image I in ImageList)
            {
                    GT = I.TransformToVisual(MainGrid);
                    Point ThisPoint = GT.TransformPoint(new Point(0,0));
                   
                    if (ThisPoint.X  <= MyLine.X1 && ThisPoint.X + I.ActualWidth >= MyLine.X1)
                    {
                        PictureName.Text = (I.GetValue(Image.SourceProperty) as BitmapImage).UriSource.ToString();
                        PictureHW.Text = Math.Ceiling(I.ActualWidth).ToString() + " x " + Math.Ceiling(I.ActualHeight).ToString();
                        PicturePercentage.Text = Math.Truncate(((MyLine.X1 - ThisPoint.X) / I.ActualWidth) * 100.0).ToString() + "%";
                        return;
                    }
                }
            PictureName.Text = string.Empty;
            PictureHW.Text = string.Empty;
            PicturePercentage.Text = string.Empty;
            }
            
       
        List<Image> ImageList = new List<Image>();

        private void Image_Loaded_1(object sender, RoutedEventArgs e)
        {
            ImageList.Add(sender as Image);
        }



Matt Small - Microsoft Escalation Engineer - Forum Moderator
If my reply answers your question, please mark this post as answered.

NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.


steve_aras on Sun, 21 Oct 2012 21:11:11


Wow...Matt...Thanks.  I'm still a relative newbie (OK a serious newbie)...I couldn't have figured that one out.  Because of my newbieishness, I'm still trying to understand the core basics of Binding.  In your resolution, I wouldn't of thought of creating the "second" ImageList called from OnLoad of the uiElement. My "Image" is actually a Template bound to a custom class object named "Activity....then and bound using ItemSource on the ListView.  "Activity" is an object comprosing several properties (ImageURI, StartTime, EndTime).    I was trying to pass a reference back to the Binding object in order to modify other properties of that object.

Seems weird to create the binding collection....only to create a mirror of it.  Anyway...Thanks again...BIG TIME.  You really saved me and shown some light down in the Binding abyss.

Here is some added code from the app that will probably demonstrate my confusion.  If you can see/understand where I'm getting confused and can suggest some reference/further reading....please reply.

 

List

<Image> ImageList = newList<Image>();

       

List<DateTime> StartTimeList = newList<DateTime>();

       

List<DateTime> EndTimeList = newList<DateTime>();

privatevoidImage_Loaded_2(objectsender, RoutedEventArgse)

        {

            MyImageList.Add(sender

asImage);

           

Activityitem = (Activity)(sender asImage).DataContext;

           

            StartTimeList.Add(item.StartTime);

            EndTimeList.Add(item.EndTime);

 


SA



Matt Small on Mon, 22 Oct 2012 15:00:18


Well, the source of the listview in my app itself is not a series of images but of strings, so it doesn't do very well to work on strings. The reason I did it that way is because I found it easier to have an Image in the ItemTemplate with its source bound to a string than insert an Image object wholly into the ItemTemplate at runtime.

In your case I would create a custom class (I'll call it "Frame") that encapsulates the Image, StartTime, and EndTime as properties.  Create a single List<Frame> and bind your ListView to that List of frames.  As each image passes under the line, get a reference to that image, find its Frame object in your list of Frames, and then get the starttime and endtime.  You can use the percentage formula I already am using in my app and apply that to the total number of minutes that your frame represents.  For example if your Frame starts at 8:30 and ends at 9:10, then that's 40 minutes.  At 10% of the way through, that's 4 minutes + 8:30 = 8:34.

steve_aras on Mon, 22 Oct 2012 16:26:35


Matt...Thanks Again.  I'll need to figure out what you mean by "As each image passes under the line, get a reference to that image, find its Frame object in your list of Frames..."  I do have that "List<Frame>"...I call it:  Activity.  I'm wondering if you mean for me to store the StartTime (EndTime) on the page (similar to hidden fields in HTML) OR as you wrote.....Get the actual reference back to the item in List<Activity> ...then use Activity.StartTime.

I'll work on "Get the actual reference" today.  You're a great help.

Matt Small on Mon, 22 Oct 2012 17:39:42


Try this:  bind the listview to your list of Activity objects. As I did in my code, pass in only a string to the Image, not an actual image object, and when each image is loaded, create a list of references to the images.

When the scrollviewer moves, you can check to see which image is under the line.  When you've figured out which image it is, get the path to the image and compare it to each Activity's image string in your list.  When you have a match, you will then have the appropriate starttime and endtime.  Then do the calculations on the time using the percentage and you'll be set.