March 2008 - Posts

What's Trace Got To Do With Culture?

I really don't know the answer to that question, but we've hit the strangest issue. The following code runs well when <trace enabled="true"> is set on web.config, but throws FormatException when it's set to false.

DateTime.Parse(DateTime.Now.ToString());

We're thinking that somehow trace affects the system's culture, causing the code work. But it still doesn't make any sense to us. Looking in debug, it seems that DateTime.Now.ToString returns "DD" instead of the day, altought the current culture is "en-US". Can you make anything of it? Oh, and I should mention, this happens only on Windows 2000 Professional. When we run the software on Windows 2003 the problem does not reoccur.

Posted by dorony | with no comments

WPF Video Flipping Utility

I've recently bought a digital camera, which can also make videos. Thing is, if you turn it 90 degrees when you film the video, the video is flipped 90 degrees when you play it. No surprise there, I guess, but still very annoying.

Strangely, Windows Media Player doesn't seem to have any flipping capabilities, nor does any other (free) media player that I could find. You can actually permanently transform the video with Windows Movie Maker, but for some reason my videos turned out with crappy sound.

And so I figured, 'Hey, let's write something in WPF. That shouldn't be so hard'. And indeed, flipping a video in WPF is as easy as giving a MediaElement control a RotateTransform. Here is the result of the work. The bar on the right side allows you to flip the video in several directions.

videoFlip2

As you can tell, an upside down parrot. You can download the binaries and code right here. Still, it took me a few good hours to write this tool, and not 10 minutes as I anticipated, as I had to battle several issues.

The first issue was getting the video to play automatically after you open a file. I am using a MediaElement which has a MediaTimeline assigned to it. The problem is, that it seems the Loaded event of the MediaElement (when it's working with a MediaTimeline and not in independent mode) fires instantly when the window is created, and not when the video is actually loaded. So I had to create my own RoutedEvent for this. The XAML code looks something like this.

<Window.Triggers> <EventTrigger RoutedEvent="local:Main.MediaLoaded" SourceName="MainWindow"> <EventTrigger.Actions> <BeginStoryboard Name="beginStoryboard"> <Storyboard> <MediaTimeline x:Name="videoTimeline" Storyboard.TargetName="mediaElement" CurrentTimeInvalidated="videoTimeline_CurrentTimeInvalidated" /> </Storyboard> </BeginStoryboard> </EventTrigger.Actions> </EventTrigger> </Window.Trigger> ... ... <MediaElement x:Name="mediaElement" MediaOpened="mediaElement_MediaOpened" />

And here is the declaration of the event.

 

        public static readonly RoutedEvent MediaLoadedEvent;

 

 

        static Main()

        {

            MediaLoadedEvent = EventManager.RegisterRoutedEvent("MediaLoaded", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Main));

        }

 

        protected virtual void OnMediaLoaded()

        {

            RaiseEvent(new RoutedEventArgs(MediaLoadedEvent, this));

        }

 

        private void Open_Click(object sender, RoutedEventArgs e)

        {

            OpenFileDialog dialog = new OpenFileDialog();

            dialog.Filter = VideoFilesFilter;

            bool? result = dialog.ShowDialog();

            if (result == true)

            {

                videoTimeline.Source = new Uri(dialog.FileName);

                mediaControlsPanel.Visibility = System.Windows.Visibility.Visible;

            }

            OnMediaLoaded();

        }

As you can see the event is fired right after the user selects a video file in the OpenFileDialog that is displayed to him. This triggers the EventTrigger that fires the StoryBoard, which in turn plays the video.

The second issue was getting the video to display in it's natural size, but allowing the user to expand it by resizing the video. Playing with the window's SizeToContent property, and the MediaElement's Stretch property did not give me the satisfactory results. Either the video appeared full screen (when the MediaElement.Stretch is set to 'Uniform'), or it appeared correctly, but would not change its size when window is maximized (when MediaElement.Stretch is set to 'None'). I ended up setting leaving Stretch at 'Uniform' (the default) but giving the window a height and width (and not use SizeToContent). When a video is loaded, I manually change the window's size.

private void mediaElement_MediaOpened(object sender, RoutedEventArgs e)

{

   Height = mediaElement.NaturalVideoHeight + openButton.ActualHeight;

   Width = mediaElement.NaturalVideoWidth;

}

The final issue was with the Slider, i.e. the track-bar that allows you to drag the video to a certain location. The example here showed me how to let the Slider track the video, but I couldn't figure out how to catch the event of the user dragging the Slider. And indeed, the Slider does not expose these events, you have to get the Slider's Thumb control and register to the event's on that thingy. Bah.

  private bool _enableSliderAutoUpdate = true;

 

        private void Main_Loaded(object sender, RoutedEventArgs e)

        {

            Track sliderTrack = timelineSlider.Template.FindName("PART_Track", timelineSlider) as Track;

            sliderThumb = sliderTrack.Thumb;

 

            sliderThumb.DragCompleted += sliderThumb_DragCompleted;

            sliderThumb.PreviewMouseLeftButtonDown += sliderThumb_PreviewMouseLeftButtonDown;

        }

 

        private void videoTimeline_CurrentTimeInvalidated(object sender, EventArgs e)

        {

            if (_enableSliderAutoUpdate)

                timelineSlider.Value = mediaElement.Position.Ticks;

 

        }

        private void sliderThumb_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            _enableSliderAutoUpdate = false;

        }

 

        private void sliderThumb_DragCompleted(object sender, DragCompletedEventArgs e)

        {

            beginStoryboard.Storyboard.Seek(this, new TimeSpan((long)timelineSlider.Value), TimeSeekOrigin.BeginTime);

            _enableSliderAutoUpdate = true;

        }

 

        private void mediaElement_MediaOpened(object sender, RoutedEventArgs e)

        {...

            timelineSlider.Maximum = mediaElement.NaturalDuration.TimeSpan.Ticks;

 

        }

Notice that we register to the events on the Loaded event in the Window, and not in the constructor. If you do it in the constructor expect a NullReferenceException as the Slider's Thumb is not yet there. Also note the usage of a boolean - _enableSliderAutoUpdate - which makes sure that while the user is dragging the Slider's thumb its position is not updated, even though the video keeps playing.

And that's about it. Other than that the code is rather straight-forward and again, can be downloaded here.

Posted by dorony | 5 comment(s)
תגים:

Selling ReSharper

At my workplace we've bought ReSharper licenses for everyone. Still, some of the developers refuse to use it. They say it is too slow for them to use. It's true this software has serious performance issues. If I try to open three instances of visual studio with ReSharper on, my dual-core computer with 2GB of RAM will choke to death. I guess there's a reason it lets you display the amount of managed memory it's using in Visual Studio's status bar (that's rather like admiting in defeat, "we know our software is a memory hog, we've given up on that, but we'll at least let you know how screwed you are at any given moment").

And yet, I claim that it's totally worth it. So I try to work with less solutions open, in order to gain the incredible benefits ReSharper gives me. I honestly can't work without it anymore. It has changed the way I code, and I will never attempt a serious refactoring without it. But how should I sell it to the other developers? Convince them to give it another go and put the performance issues aside? Well, I created a presentation to demonstrate this software's various benefits (my dear ctrl+n, I would marry you right now if I could), but then I found this video, which says it all: the ReSharper Jedi at work.

This video shows how ReSharper allows you to 'code in reverse'. First write the usage of your classes and methods, and let ReSharper create them for you. Sounds like it could be great for TDD, isn't it? Well it is, only for now ReSharper doesn't know to create the classes for you in a different project (usually your tests won't be in the same project as the code). This plugin here might do the trick, though it is still a very early version, which makes some problematic assumptions (but the code is there, so you are welcome to help).

Anyway, guys, if you're reading this - get over it already. ReSharper will own you eventually, you'll see.

Posted by dorony | 2 comment(s)
תגים: