Saturday, April 23, 2011

Clicking away from FrameworkElement when mouse is still captured

This post is about a very specific problem about mouse capturing. There are certain situations in which we need to be notified when mouse is clicked outside the bounds of an element. As all of you know that this is classical mouse capturing situation used for various tasks including Drag & Drop. When we search around for a solution for this then we find out an attached event Mouse.PreviewMouseDownOutsideCapturedElement. This is documented in msdn as follows:

“Occurs when the primary mouse button is pressed outside the element that is capturing mouse events.”
<Window x:Class="WpfApplication3.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="483" Width="553"      
Mouse.PreviewMouseDownOutsideCapturedElement="Window_PreviewMouseDownOutsideCapturedElement" >
    <StackPanel>
        <StackPanel Height="33">
            <TextBox Name="textBox1" />
        </StackPanel>
        <StackPanel  >           
            <Button Content="Button" Height="32" HorizontalAlignment="Left"
                    Margin="126,209,0,0" Name="button1" VerticalAlignment="Top"
                    Width="297" Click="button1_Click" />
        </StackPanel>
    </StackPanel>
</Window>
This definitely does what it is supposed to do. In order to get this to work we must have a captured element. Let’s capture mouse on textBox1. So, clicking a mouse outside textBox1 (captured element) should result in causing this handler to get called.
public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();       
    }

    private void Window_PreviewMouseDownOutsideCapturedElement(object sender, MouseButtonEventArgs e)
    {

    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Mouse.Capture(this.textBox1);
    }
}
Let’s run this. We have the display as presented in XAML. It has a TextBox and a Button. When we click the button textBox1 captures the mouse using static Capture method on Mouse. We have subscribed PreviewMouseDownOutsideCapturedElement event in XAML. We could have easily done that in code behind as well. Obviously we would need AddHandler… mechanism for registering with an attached event. This is similar to XAML attached properties. When implemented in WPF, they work as Dependency properties. Similarly Attached events from XAML are implemented as routed events in WPF.

Now the issue is that this event gets fired even when we click inside textBox1. This seems awkwardly strange as this is purely not expected behavior when we read the msdn description. Now we know the behavior. How can we fix this? We just need to find out where the mouse was clicked and we should be good to go. You might be wondering we can get around that we can do that either through the sender parameter or Source / OriginalSource from MouseButtonEventArgs. But the strange thing is that sender is always the element on which the event is registered. So if were registering it on textBox1, it would be textBox1. Currently this would always be the Window object no matter where we click on the Window after capturing the mouse by clicking the button. On top of that, Source and OriginalSource are always the captured element. The other properties which could inform us if the mouse is directly over the textBox1 are always true.

The only way to fix it seems to be to do it ourselves. We can find out the position of mouse. If the position of mouse is within the bounds of captured element then we can just ignore this. Otherwise, we can execute the same logic as we were supposed to execute. We might need to register the event again.

private void Window_PreviewMouseDownOutsideCapturedElement(object sender, MouseButtonEventArgs e)
{
    bool isClickedWithin =  IsMouseClickWithin(this.textBox1, e.MouseDevice.GetPosition(this.textBox1));

    if(isClickedWithin)
    {
        //execute some logic
    }
}

private bool IsMouseClickWithin(FrameworkElement element, Point point)
{
    return (element.ActualWidth > point.X && element.ActualHeight > point.Y) || point.X < 0 || point.Y < 0;
}
In the above example, it is checking if the mouse position is within the actual bounds of textBox1. IsMouseClickWithin returns true for this. It returns false otherwise.

Wednesday, April 13, 2011

Hosting SQL CE based Entity Models in WCF Data Service

While developing an application in ASP.net for my friend I realized that we can not SQL Compact Edition with ASP.net. When you try to do that you would get this error page.

SQL Compact Edition error when used with ASP.net
The server encountered an error processing the request. The exception message is 'SQL Server Compact is not intended for ASP.NET development.'. See server logs for more details. The exception stack trace is: 

at System.Data.SqlServerCe.SqlCeRestriction.CheckExplicitWebHosting() at System.Data.SqlServerCe.SqlCeConnection..ctor() at System.Data.SqlServerCe.SqlCeProviderFactory.CreateConnection() at System.Data.EntityClient.EntityConnection.GetStoreConnection(DbProviderFactory factory) at System.Data.EntityClient.EntityConnection.ChangeConnectionString(String newConnectionString) at System.Data.EntityClient.EntityConnection..ctor(String connectionString) at System.Data.Objects.ObjectContext.CreateEntityConnection(String connectionString) at System.Data.Objects.ObjectContext..ctor
I got this when I was hosting WCF DataService in the ASP.net application exposing my Entity Model. The entities were based on a SQL CE database. To see this message, we need to set the behavior of service to allow the exception details to be shown to the user by this setting on the WCF Data Service.
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class MyDataService : DataService<App.ModelEntities>
{
    ...
}
Otherwise we see the following message when we run the application:

Although this is more secure message as we are not showing the stack trace to the user but during development this message seems very irritating. In order to avoid this and to get the detailed message set the above specified Service Behavior for the WCF Data Service.

This must be remembered that this limitation seems more like a suggestion by Microsoft so as to discourage the use of SQL Server Compact Edition for public facing systems because of its security limitations. But there might be valid reasons to still use SQL CE for ASP.net applications for demo applications or in-house developed applications. In order to get around that we can use AppDomain slots to set SQLServerCompactEditionUnderWebHosting to true. This results in allowing the use of SQL Server Compact edition by the runtime. We can set that during application startup in Global.asax.
public class Global : System.Web.HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        AppDomain.CurrentDomain.SetData("SQLServerCompactEditionUnderWebHosting", true);
    }
}
Now when we run the application we can see the service successfully giving XML data. We have discussed about AppDomain slots here:

http://shujaatsiddiqi.blogspot.com/2011/01/in-this-post-we-will-discuss-named-slot.html