Unhandling previously handled events in Silverlight

What?

You may well find yourself wanting to attach to an event which has already been handled inside a control with no way to override and prevent the event from being handled. As seen in my prior post to this, you can make use of the AddHandler method. I am going to use the Backspace key down in a TextBox as an example, the event is only raised and not handled when the TextBox is empty. (This can be seen in the sample application in the output coming from the behavior)

How?

I have created a behavior which “unhandles” a key down and executes a command when the event is raised (Note: there is no need to attach to the event on the textbox as well). To prove this, in the examples output you can see the key down and key up events being raised as well as the behavior being executed on backspace.

Behavior:

public class UnhandleKeyDownBehavior : Behavior
{
    private KeyEventHandler _handler;

    protected override void OnAttached()
    {
        base.OnAttached();
        _handler = new KeyEventHandler(AssociatedObjectKeyUp);
        AssociatedObject.AddHandler(UIElement.KeyDownEvent, _handler, true);
    }

    private void AssociatedObjectKeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key)
        {
            e.Handled = false;
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.RemoveHandler(UIElement.KeyDownEvent, _handler);
    }

    public Key Key { get; set; }
}
}

Xaml:

xaml

Sample App

Install Microsoft Silverlight

Detect whether the mouse is down in Silverlight

After recently searching for a way to do this and finding nothing I thought I would share a solution. It involves using the ever useful AddHandler method on the UIElement you wish to detect the mouse down on and simply setting a flag. Job done.

It is worth noting that popups and child windows may appear on top of the UIElement which the handler is attached to.

Code Snippet:

this.AddHandler(UIElement.MouseLeftButtonDownEvent,
    new MouseButtonEventHandler(MainPageMouseLeftButtonDown), true);
this.AddHandler(UIElement.MouseLeftButtonUpEvent,
    new MouseButtonEventHandler(MainPageMouseLeftButtonUp), true);
void MainPageMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    MouseDown = false;
}
void MainPageMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    MouseDown = true;
}

Sample App

Install Microsoft Silverlight

Using Silverlight and JavaScript to prevent leaving a webpage

Recently I have been thinking about applications with data input and the problems of being in a browser. One of the perils of being in a browser is that a user may have some unsaved data input tasks. The data can be easily lost by accidently navigating to a different webpage, closing the browser or closing the tab.  

Preventing this is relatively easy making use of Silverlight and JavaScript communication in conjunction with the window.onbeforeunload function. See below for example and code snippets. :D

Sample Application

The javascript:

 
<script language="javascript" type="text/javascript">
window.onbeforeunload = askConfirm;
function askConfirm() {
    var control = document.getElementById("silverlightControl");
    var preventLeave = control.Content.Page.PreventLeave();
    if (!preventLeave) {
        return;
    }
    var message = control.Content.Page.LeaveRequested();
    return message;
}
</script>  

The silverlight code:

 
public MainPage()
{
    InitializeComponent();
    HtmlPage.RegisterScriptableObject("Page", this);
}

[ScriptableMember]
public string LeaveRequested()
{
    return "You have unsaved changes to your current document?";
}

[ScriptableMember]
public bool PreventLeave()
{
    return (bool)PreventLeaveCheckBox.IsChecked;
}

Normal mapping in Silverlight

 This is a port of a shader I wrote a long time ago at University to Silverlight. It was originally written it Direct X and C++. I am not going to go into too much detail about the implementation but will mention key points I came across.

Application here

Using the Application:

  • The light is a directional light facing the surface which can be moved from left to right in the x-axis. It is at a set distance from the surface.
  • The light also gets rotated with the surface via the plane rotation.
  • The surface paramaters are used by the shader to determine the ratio’s of the diffuse, ambient and specular(the shinyness) lighting of the surface.
  • It is possible to drop your own texture and normal by dropping them on the two panels (png or jpg).
  • Have fun playing around as you can create some quite random effects. (Update: I have just added the ability to use the webcam as the source, you can make a random ‘ice’ effect by using the second sample texture)

Background reading to understand the concepts:

  • Texture mapping: here
  • Diffuse reflection: here
  • Normal mapping: here
  • Specular reflection: here

Issues / annoyances while porting:

  • In silverlight there is no way to set a float3 in the pixel shader, so i registered a Point for the X/Y and also a float for the Z axis.

References:

  • Vector3 class from here. As I couldnt be bothered to write my own, also has some very good descriptions of the class and each method.
  • I cant for the life of me remember where I found the first 3 textures and normals. The others came from here.
  • I took advantage of using my image loader here.
  • Original shader:

Generic processing of a Drag event from the file system

This is an extension method to help a user in processing a DragEventArgs to a object type of their choice.

How

  • Take a function to create the required Type from a FileStream.
  • Take an Action to handle an exception.
  • Check that the drop array contains a FileDrop.
  • Select the First FileInfo and open a FileStream.
  • Execute createAction with this FileStream. If this fails the exception will be handled by the onFailed callback.

The Code:

Double Click behavior, mvvm compliant

Often when creating an application you will want a secondary mechanism for executing a command, the primary mechanism will often be a button click (e.g. Opening a record). In MVVM the backing ViewModel will expose an ICommand which is bound to the Button’s Command. This behavior executes the same command when a double click occurs.

Sample Application:

Install Microsoft Silverlight

How:

  • Register to the Mouse Up event on the AssociatedObject, we use the Mouse Up event so that we can attach the behavior to DataGrid / ListBox templates as the Mouse Down events are swallowed by these.
  • Create a dependancy property for the ICommand and the ClickInterval (this will commonly be hard coded by exists for demonstration purposes)
  • Set up a DispatcherTimer with the desired interval, also attach the Tick event and stop the timer.
  • When Mouse Up occurs event occurs the first time we start the DispatcherTimer.
  • For a double click to occur the Mouse Up event must be raised while the timer is active, if it is then we execute the Command and stop the timer.

Code:

public class DoubleClickBehavior : Behavior<UIElement>
{
    private readonly DispatcherTimer _timer = new DispatcherTimer();

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.MouseLeftButtonUp += AssociatedObjectMouseLeftButtonUp;
        _timer.Tick += new EventHandler(_timerTick);
        _timer.Interval = TimeSpan.FromSeconds(ClickInterval);
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.MouseLeftButtonUp -= AssociatedObjectMouseLeftButtonUp;
    }

    private void _timerTick(object sender, EventArgs e)
    {
        _timer.Stop();
    }

    private void AssociatedObjectMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (!_timer.IsEnabled)
        {
            // If the timer is not already running then start it.
            _timer.Start();
        }
        else
        {
            _timer.Stop();
            if(CommandToExecute != null)
            {
                // If the timer is active there has been a second event so execute the command.
                CommandToExecute.Execute(this);

            }
        }
    }

    public static readonly DependencyProperty IntervalProperty =
        DependencyProperty.Register("ClickInterval",
        typeof(double),
        typeof(DoubleClickBehavior),
        new PropertyMetadata(0.25,
            new PropertyChangedCallback(OnClickIntervalChanged)));

    private static void OnClickIntervalChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var doubleClick = (DoubleClickBehavior)sender;
        doubleClick._timer.Interval = TimeSpan.FromSeconds(doubleClick.ClickInterval);
    }

    public double ClickInterval
    {
        get { return (double)GetValue(IntervalProperty); }
        set { SetValue(IntervalProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("CommandToExecute",
        typeof(ICommand),
        typeof(DoubleClickBehavior),
        new PropertyMetadata(null));

    public ICommand CommandToExecute
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }
}
public class DoubleClickBehavior : Behavior<UIElement>
{
private readonly DispatcherTimer _timer = new DispatcherTimer();

protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseLeftButtonUp += AssociatedObjectMouseLeftButtonUp;
_timer.Tick += new EventHandler(_timerTick);
_timer.Interval = TimeSpan.FromSeconds(ClickInterval);
}

protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseLeftButtonUp -= AssociatedObjectMouseLeftButtonUp;
}

private void _timerTick(object sender, EventArgs e)
{
_timer.Stop();
}

private void AssociatedObjectMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (!_timer.IsEnabled)
{
// If the timer is not already running then start it.
_timer.Start();
}
else
{
if(CommandToExecute != null)
{
// If the timer is active there has been a second event so execute the command.
CommandToExecute.Execute(this);
}
}
}

public static readonly DependencyProperty IntervalProperty =
DependencyProperty.Register(“ClickInterval”,
typeof(double),
typeof(DoubleClickBehavior),
new PropertyMetadata(0.25,
new PropertyChangedCallback(OnClickIntervalChanged)));

private static void OnClickIntervalChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var doubleClick = (DoubleClickBehavior)sender;
doubleClick._timer.Interval = TimeSpan.FromSeconds(doubleClick.ClickInterval);
}

public double ClickInterval
{
get { return (double)GetValue(IntervalProperty); }
set { SetValue(IntervalProperty, value); }
}

public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register(“CommandToExecute”,
typeof(ICommand),
typeof(DoubleClickBehavior),
new PropertyMetadata(null));

public ICommand CommandToExecute
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}

Behavior to process an Image Drop from file system to Image Source

This behavior is used to set the source of a silverlight image control when an image is dropped from the file system onto a UIElement in the Silverlight application.
Sample Application:

Install Microsoft Silverlight

How:

  • Register to the Drop event on the AssociatedObject.
  • The dropped file is processed to a bitmap image from a filestream. I took advantage of using the extension method for processing a file drop which can be seen here.
  • Set the Source of the TargetImage to be the dropped image.

Code:

public class ImageDropBehavior : Behavior<UIElement>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Drop +=
                new DragEventHandler(AssociatedObject_Drop);
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.Drop -=
                new DragEventHandler(AssociatedObject_Drop);
        }

        private void AssociatedObject_Drop(object sender, DragEventArgs e)
        {
            Func<FileStream, BitmapImage> func = (stream) =>
            {
                BitmapImage bitmapImage = new BitmapImage();
                bitmapImage.SetSource(stream);

                return bitmapImage;
            };

            var droppedImage =
                e.ProcessDragEventArgsTo<BitmapImage>(func, _ => { });
            if (droppedImage != null)
            {
                TargetImage.Source = droppedImage;
            }
        }

        public static readonly DependencyProperty TargetImageProperty =
            DependencyProperty.Register("TargetImage",
            typeof(Image),
            typeof(ImageDropBehavior),
            new PropertyMetadata(null));

        public Image TargetImage
        {
            get { return (Image)GetValue(TargetImageProperty); }
            set { SetValue(TargetImageProperty, value); }
        }
    }
}

Using the behavior:

  • Attach the behavior to a UIElement.
  • The AllowDrop property needs to be set to true on the UIElement the behavior is attached to else the Drop event will never be raised.
  • Element bind the TargetImage property on the behavior to an Image.
  • See the behavior being used in blend:

Notes:

  • If the target image has data binding, this behavior will remove the binding when it sets the image source.
  • Will only work for Silverlight supported images. At the point of writing these are jpg and png.
public class ImageDropBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Drop += new DragEventHandler(AssociatedObject_Drop);
}

protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Drop -= new DragEventHandler(AssociatedObject_Drop);
}

private void AssociatedObject_Drop(object sender, DragEventArgs e)
{
Func<FileStream, BitmapImage> func = (stream) =>
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);

return bitmapImage;
};

var droppedImage = e.ProcessDragEventArgsTo<BitmapImage>(func, _ => { });
if (droppedImage != null)
{
TargetImage.Source = droppedImage;
}
}

public static readonly DependencyProperty TargetImageProperty =
DependencyProperty.Register(“TargetImage”,
typeof(Image),
typeof(ImageDropBehavior),
new PropertyMetadata(null));

public Image TargetImage
{
get { return (Image)GetValue(TargetImageProperty); }
set { SetValue(TargetImageProperty, value); }
}
}

A generic pool of objects

Right as this is my first post let’s keep it nice and simple.

A pool works by managing a collection of objects kept in memory so they are always available. A pool can be particularly useful for many situations such as managing large classes which take time to initialise and can help contain classes with memory leaks. There is no correct way to make a pool and it should be optimised to the requirements of the situation.

This pool is designed to recycle a collection of objects:

  • It creates a new object of type T when it is empty.
  • It is important that we only store ONE reference to each instance of an object in the collection, hence the validation in the ReturnToPool() method.
  • The lock() method is used to make this class threadsafe.
  • In most instances such as managing a memory leak, it should be used as a singleton.
  • When an object is ready to be returned it is not the responsibility of this class to do so. (I will make a post in the future about correct ways to do this)
The code:
public class Pool<T>
{
    private readonly Stack<T> _pool = new Stack<T>();
    private readonly Func<T> _createNewFunction;

    public Pool(Func<T> createNewFunction)
    {
        _createNewFunction = createNewFunction;
    }

    public T GetFromPool()
    {
        lock (_pool)
        {
            if (_pool.Count <= 0)
            {
                // Create a new T
                return _createNewFunction();
            }

            return _pool.Pop();
        }
    }

    public void ReturnToPool(T item)
    {
        lock (_pool)
        {
            if (_pool.Contains(item))
            {
                // If it is already in the pool don't re-add it.
                throw new InvalidOperationException("This item is already in the pool");
            }

            _pool.Push(item);
        }
    }
}
Return top