Archive
Gotcha: WPF UserControl SizeChanged event not firing at resizing
Useful to know:
If you set the Width and Height on the UserControl though, you have set a fixed size and thus even if its parent tool window changes size, the UserControl never will. You should not set Width and Height on the UserControl if you want that event to be raised as the parent tool window changes size.
from:
http://www.actiprosoftware.com/community/thread/20365/size-change-event-for-the-usercontrol
Suggestion: property and event setting block attached to C# type instance
Instead of having to write in C#
speechRecognizer = CreateSpeechRecognizer();
if (speechRecognizer != null)
{
speechRecognizer.SomeProperty = someValue;
speechRecognizer.SomeOtherProperty = someOtherValue;
speechRecognizer.SpeechRecognized += SpeechRecognized;
speechRecognizer.SpeechHypothesized += SpeechHypothesized;
speechRecognizer.SpeechRecognitionRejected += SpeechRecognitionRejected;
}I’d prefer to write:
speechRecognizer = CreateSpeechRecognizer() {
SomeProperty = someValue,
SomeOtherProperty = someOtherValue,
SpeechRecognized += SpeechRecognized,
SpeechHypothesized += SpeechHypothesized,
SpeechRecognitionRejected += SpeechRecognitionRejected
}that is after any Type I’d like to be able to add {…} block with assignments to its properties, like I can do when I create a new type.
Do note that I also would like this syntax and the existing new Type(…) {…} syntax (which is a subset of this one) to support assignment and removal of event handlers, not just setting of properties, as shown in the example above
If you like this suggestion, vote it up at:
Gotcha: MarkerReached event of MediaElement returns new Markers
I just checked in the implementation code for a new feature for ClipFlair Studio’s Captions/Revoicing component:
When playing back recorded (or loaded from a WAV or MP3 file) audio for a caption/revoicing entry, the playback is now limited to the duration of the respective caption, (End-Start) time that is (btw that component has a duration column too that is hidden by default and can be shown by flipping it with the gear button on its titlebar and selecting the respective option to show the column).
The original audio is not affected and is stored in whole inside the saved state of the component/activity, so that you can adjust the caption entries timerange at any time to fit all or part of that recorded audio entry if you wish.
Will see into adding an “Limit playback” option to the backpanel of that component (it will default to true/checked) for any ClipFlair activities that don’t use the Start/End/Duration columns (e.g. if some activity just wants a grid of Captions and Audio entries for practicing and maybe for comparing to audio samples provided by the activity author or teacher at the optional “Comments (Audio)” column).
While implementing this feature, there were some “gotchas” that caused me some headache to spot:
1) When MediaOpened event is called by the MediaElement control, the Markers collection has just been reset and you need at that point to add your TimelineMediaMarker that will notify you when the playback limit point has been reached to stop the playback. In the case above this event is called after recording some audio or loading some WAV or MP3 audio file at an AudioRecorderControl (one is used at each row of the captions grid)
2) One shouldn’t remove an added marker at MediaEnded or MediaFailed events. This is since those will fire at each revoicing entry playback, whereas the MediaOpened while only occur once when the Audio property is populated at the AudioRecorderControl. As I mention above, MediaElement clears the Markers collection every time new content is loaded to it, so we need not worry about removing the marker we had added before.
3) Maybe the least obvious issue and the one that caused me most of headache to spot was that the MediaElement’s MarkerReached event gets back a MediaMarker that isn’t the same Marker instance as the one you had added to the Markers collection. So you have to use the Text property of the marker when you create it and then compare with the text from the one you got in the event to see if they are equal strings (btw, when C# compares strings it does it by content even if you use == instead of Equals method, unlike Java, where you shouldn’t use == to compare strings)
Gotcha: MediaElement must be in visual tree for MediaOpened, MediaEnded to be fired
At ClipFlair’s AudioRecorderControl (used in Captions/Revoicing component of ClipFlair Studio), I use the following code to initialize a MediaElement to use for playback.
After a long time a found out that if the MediaElement is not in the visual tree (for example defined in XAML, or defined in code and then added to the visual tree), then it will not always fire MediaOpened and MediaEnded events, which are crucial if you want to have a two-state Play/Stop button (a ToggleButton).
public MediaElement Player { get { return player; }
set {
if (player != null) {
player.MediaOpened -= MediaElement_MediaOpened;
player.MediaEnded -= MediaElement_MediaEnded;
}
player = value;
if (player != null) {
player.MediaOpened += MediaElement_MediaOpened;
player.MediaEnded += MediaElement_MediaEnded;
player.AutoPlay = false;
player.PlaybackRate = 1.0;
player.Balance = 0;
Volume = DEFAULT_VOLUME;
}
} }
protected void MediaElement_MediaOpened(object sender, RoutedEventArgs e) {
try {
//player.Position = TimeSpan.Zero;
player.Stop(); //this stops current playback (if any) and rewinds
player.Play();
} catch (Exception ex) {
PlayCommandUncheck(); //depress playback toggle button
//don't talk to ToggleButton directly
Status = MSG_PLAY_FAILED + ex.Message;
} }
protected void MediaElement_MediaEnded(object sender, RoutedEventArgs e)
{
PlayCommandUncheck(); //depress play button to stop playback
//don't talk to ToggleButton directly }
Gotcha: MediaElement AutoPlay faster than doing Play at MediaOpened
Just added the following comment to: https://github.com/loarabia/ManagedMediaHelpers/issues/15
Managed Media Helpers contains the very useful Mp3MediaSource class for .NET / Silverlight / Windows Phone.
Added compile-time SWITCHES and respective code to Silverlight demo code for PRELOAD (into memory stream) and AUTOPLAY (this was a bit tricky, need to call Play at MediaOpened event, not right after setting the source to the MediaElement).
Also seems to be 2-3 sec slower than AutoPlay (!) irrespective of using PRELOAD. Maybe it is faster to set the source again every time you want to play and just keep AutoPlay=true
Update: had a bug at the following code, obviously you first must set the MediaOpened event handler, then set the Source to the MediaPlayer. Also, one could set that handler once at the InitializeComponent method.
//———————————————————————–
// <copyright file="Page.xaml.cs" company="Larry Olson">
// (c) Copyright Larry Olson.
// This source is subject to the Microsoft Public License (Ms-PL)
// See http://code.msdn.microsoft.com/ManagedMediaHelpers/Project/License.aspx
// All other rights reserved.
// </copyright>
//
// Edited by George Birbilis (http://zoomicon.com)
//———————————————————————–#define AUTOPLAY
//#define PRELOADnamespace Mp3MediaStreamSourceDemo
{
using Media;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;/// <summary>
/// A Page of a Silvelight Application.
/// </summary>
public partial class Page : UserControl
{
/// <summary>
/// Initializes a new instance of the Page class.
/// </summary>
public Page()
{
InitializeComponent();me.Volume = 1.0; //set max volume
#if !AUTOPLAY
me.AutoPlay = false;me.MediaOpened += (s, e) =>
{
//me.Position = TimeSpan.Zero;
me.Stop(); //this stops current playback (if any) and rewinds back to start
//me.PlaybackRate = 1.0;
try
{
me.Play();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
};
#endif
}/// <summary>
/// Event handler for the Button on the Page.
/// </summary>
/// <param name="sender">
/// The button which was clicked.
/// </param>
/// <param name="e">
/// The state when this event was generated.
/// </param>
private void OpenMedia(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.ShowDialog();Stream data = ofd.File.OpenRead();
#if PRELOAD
Stream m = new MemoryStream((int)ofd.File.Length);
data.CopyTo(m);
m.Position = 0;
data = m;
#endif
Mp3MediaStreamSource mediaSource = new Mp3MediaStreamSource(data);
me.SetSource(mediaSource);
}
}
}
HowTo: Perform ASP.net action after page child controls are databound
While creating metadata entry/update pages for ClipFlair’s Activity and Clip Galleries I had the problem of figuring out how to do some initialization (from XML data, loaded from a filename based on the 1st item of a DataBound control), after all child controls of the ASP.net Page have been databound (from XmlDataSource).
Seems others have asked about that too, so I contributed my solution:
Based on ASP.net Page Life Cycle Events article I used:
protected void Page_PreRenderComplete(object sender, EventArgs e)
{
if (!IsPostBack) //only at 1st load
UpdateSelection();
}
protected void UpdateSelection()
{
UpdateSelection(listItems.SelectedValue);
}
protected void listItems_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateSelection();
}
where UpdateSelection was loading XML data from a file selected at a dropdown list (which at start is pointing to index 0) and needed some CheckBoxLists on the page to have first gotten their items from other XML files so that they would allow the code to check items on them based on the XML data (from then on UpdateSelection is just getting called at the dropdownlist SelectedIndexChanged event – those do PostBacks so at PreRenderComplete we ignore them to avoid doing UpdateSelection twice)
Gotcha: var x = x() in Javascript gives “Object Expected” error
At ClipFlair Studio (a Silverlight app), I had some time ago implemented a confirmation warning upon user trying to close the webpage (when it was running inside the web browser), which then had stopped functioning. It seems at some refactoring I had added code like the following:
var activityView = activityView();
and it was failing with error “Object Expected” (and even worse this was done silently – had to use browser’s debugging tools to catch it – because the respective code was running at onbeforeunload browser window event).
Such code would be fine in C#, but in Javascript it seems that a local variable with name x hides a function named x (with any number of parameters in its definition). This is obviously because functions are first-class objects in Javascript and can be treated like variables themselves too.
All started working again fine after changing that code to:
var a = activityView();
Below is the respective block of Javascript code included in a script tag at the webpage that hosts the Silverlight control. Note the “control.content.activityWindow”, where “activityWindow” is accessed via the Silverlight HTMLBridge (that object – that has to be marked with ScriptableType class attribute – is registered using that key via HtmlPage.RegisterScriptableObject at the Silverlight app side).
function silverlightControl() { return document.getElementById("silverlightControl"); } function onSilverlightLoad(sender, args) { var control = silverlightControl(); if (control != null) control.focus(); } function activityWindow() { var control = silverlightControl(); if ( (control != null) && (control.content != null) ) return control.content.activityWindow; else return null; //need this so that it doesn't return undefined } function activityView() { var a = activityWindow(); if (a != null) return a.GetView(); else return null; //need this so that it doesn't return undefined } function onClosing() { var a = activityView(); if ( (a != null) && (a.WarnOnClosing) ) return "Do you want to exit ClipFlair Studio?";
//else return undefined is implied (no onClosing message that is) } function onClosed() { var a = activityView(); if (a != null) a.WarnOnClosing = false; } function installEventHandlers() { window.onbeforeunload = onClosing; window.onunload = onClosed; } installEventHandlers();