Thursday, October 30, 2008

RoutedEvents to Commands

Microsoft started down a great road by introducing Commanding with WPF. However, in my opinion they didn't go far enough. While it's great that some UI elements like Buttons, Menus, etc. support sending a Command when activated, there are many other places in UI design that an action taken implies a command. The most common example I can think of is when the SelectedItem value changes in a Listbox or Combobox. Perhaps you want to either display details about a selected listbox item. Maybe you want to take action if the user double-clicks the list item. Maybe you want to immediately do some action, like a user chooses a value from a ComboBox and your ready to move forward. (I won't go into whether this is really good UI design).

Regardless, my point is, there are lots of things exposed via RoutedEvents that have no command interface, but it sure would be nice to have a command instead of a routed command.

One great thing about WPF is the extensibility. Using the Attached Behavior pattern (by John Gossman) we can create a class that will allow us to add command patterns onto UIElement for any routed command. The code is at the end of this post.

Some interesting things I learned while making this class and then trying to use it. One, XAML is good, but it's got a long way to go to catch up to the more mature .NET languages, like C#. A difficulty in XAML is the lack of generic support, so you need to do little things like my creation of the RoutedEventCommandBindingCollection class, even though it is an empty class, it creates a concrete class of the generic ObservableCollection<>.

The fact that attached property instances are not separately initialized also was a problem. For example, it would be great to initialize each attached property to be an empty list. However, we can't do this. So instead, we need to do the initialization in the XAML, as seen in the code example below. If you exclude the RoutedEventCommandBindingCollection wrapping, you get a NULL exception because the attached property EventCommandBinding (which is a list) hasn't been initialized to an empty list.


Using the class in XAML:



<UWPath:RoutedEventCommandProxy.EventCommandBinding>
<UWPath:RoutedEventCommandBindingCollection>
<UWPath:RoutedEventCommandBinding Event="UIElement.MouseUp" Command="{StaticResource MouseWasClicked}"/>
</UWPath:RoutedEventCommandBindingCollection>
</UWPath:RoutedEventCommandProxy.EventCommandBinding>



The source code in C#:



public class RoutedEventCommandProxy
{
// Fun, this will keep track of all the bindings!
private static Dictionary<UIElement, RoutedEventCommandBindingCollection> handlerTable =
new Dictionary<UIElement, RoutedEventCommandBindingCollection>();

public static readonly DependencyProperty EventCommandBindingProperty =
DependencyProperty.RegisterAttached("EventCommandBinding",
typeof(RoutedEventCommandBindingCollection),
typeof(RoutedEventCommandProxy),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(PropertyChanged)));

public static void SetEventCommandBinding(UIElement element, RoutedEventCommandBindingCollection value)
{
element.SetValue(EventCommandBindingProperty, value);
}
public static RoutedEventCommandBindingCollection GetEventCommandBinding(UIElement element)
{
return (RoutedEventCommandBindingCollection)element.GetValue(EventCommandBindingProperty);
}

private static void PropertyChanged (DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
UIElement element = sender as UIElement;

// Remove any old stuff..
if (handlerTable.ContainsKey(element))
handlerTable.Remove(element);

RoutedEventCommandBindingCollection OldMappings = (RoutedEventCommandBindingCollection)args.OldValue;
if (OldMappings != null)
{
foreach (RoutedEventCommandBinding mapping in OldMappings)
{
if (mapping.Event != null)
element.RemoveHandler(mapping.Event,new RoutedEventHandler(Handler));
}
}

// Add the new stuff
RoutedEventCommandBindingCollection NewMappings = (RoutedEventCommandBindingCollection)args.NewValue;
if (NewMappings != null)
{
handlerTable.Add(element, NewMappings);
foreach (RoutedEventCommandBinding mapping in NewMappings)
{
if (mapping.Event != null && mapping.Command != null)
element.AddHandler(mapping.Event, new RoutedEventHandler(Handler));
}
}

}

private static void Handler (object sender, RoutedEventArgs e)
{
UIElement element = sender as UIElement;
if (handlerTable.ContainsKey(element))
{
RoutedEventCommandBindingCollection mappings = handlerTable[element];
foreach (RoutedEventCommandBinding mapping in mappings)
{
if (e.RoutedEvent == mapping.Event)
{
mapping.Command.Execute(e);
}
}
}
}
}

public class RoutedEventCommandBindingCollection : ObservableCollection<RoutedEventCommandBinding>
{
}

public class RoutedEventCommandBinding
{
public RoutedEvent Event { get; set; }
public ICommand Command { get; set; }
}

WPF and Focus

So, Control focus in WPF just frustrates me sometimes. It should be simple. Control.Focus(). But things are never as they seem. Take this code for example. It simply hides a panel asking for an ID and the presents a panel asking for a Pin. With the myPinPad variable is an instance of a custom control (inherited from UserControl) with nice buttons for a pin pad and a text field to type optionally type in a pin.



private void ShowPinPad ()
{
IDBadgePanel.Visibility = Visibility.Collapsed;
PinPanel.Visibility = Visibility.Visible;
myPinPad.Focus();
}


The PinPad custom control contains an override of the OnGotFocus method as shown below. The PWD variable is a PasswordBox control that will display the pin. Now, when the PinPad control gets focus, we want the PasswordBox to get focus so a person can just start typing.



protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
PWD.Focus();
}


Anyway, this doesn't work the first time around! Why not? Well, after some investigation it is revealed that while PWD.IsInitialized is true, PWD.IsVisible is false. Of course, call ShowPinPad again later on and it works. This time IsVisible is set to true.

So, what to do? Well, I think the reason it isn't visible is that despite the status of being Initialized, it really isn't yet. Since we just made the panel containing the control visible, perhaps the render engine hasn't had time to make the children (and hence the PWD control) visible yet.

One trick is to wait a bit before sending the focus. And by wait, I mean let all the other initialization stuff finish first on the thread. This could be done with a DispatchTimer, but an even easier trick is the following:



protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
this.Dispatcher.BeginInvoke((Action)delegate { PWD.Focus(); },
System.Windows.Threading.DispatcherPriority.Background);
}


With this trick, we create an anonymous delegate and schedule it for execution at the lowest possible priority. So, hopefully everything else will get initialized first. I've tried this with my app and it works.

Thursday, October 23, 2008

Fun with SQL transactions, ADO.NETand how ExecuteScalar can bite you.

Today I wanted to create a section of code that grouped a bunch of stored procedure (SP) calls into one transaction. If any of these stored procedures failed, I want to rollback the entire transaction. It basically went something like this:

Call SP:CreateCase
Call SP:LinkIdentifierToCase
Call SP:CreatePatient
Call SP:LinkPatientToCase

Ok, first thing I tried was the .NET 2.0 TransactionScope. I won't go into details here, there are plenty of sites online how to use it. Anyway, after some digging I found that TransactionScope only works with MSDTC service enabled on the SQL server when you are running SQL 2000! Ahhh.. That's a bit heavy since it's not distributed by any means.

So, I went back to look at the ASP.NET 1.x stuff, which is the SqlConnection.BeginTransaction method. OK, looks good on the surface.. but what's it really doing? Well, I took a look at SQL profiler to find out.. And the answer is... drum roll please... Simple sending T-SQL commands BEGIN TRANSACTION, COMMIT, and ROLLBACK.

Well, that's not too bad, at least I understand it. However, bigger issues came up when your SPs have transactions in them. See, while SQL Server claims to support nested transactions, it really doesn't. For example, what would you expect this to do:



begin transaction trans1
select @@trancount
begin transaction trans2
select @@trancount
rollback transaction trans2
select @@trancount
commit transaction trans1


I would expect it to roll back the entire transaction because trans2 failed. However, commit transaction trans1 shouldn't throw an error. Guess what, as soon as rollback is executed, all nested layers are rolled back. Instead, it should nicely fall out. OK, trans2 failed, one nested layer to go. When we see another commit or rollback, we know to rollback the entire thing. Anyway, this makes things "interesting"...

What ends up happening just isn't what you expect. So, you just need to be prepared for stuff. Luckily, it seems generally ADO.NET can handle this for you. I made a simple SP that either happily completes with a COMMIT or ends with a ROLLBACK. It is:



CREATE Procedure TestProc (@DoError int)
AS
BEGIN TRAN
insert into TestTable (Name) VALUES ('Test')
if (@DoError=0)
COMMIT
else
ROLLBACK
GO


Then I created my test code:



SqlConnection conn = new SqlConnection(ConnectionString);
conn.Open();
SqlTransaction tran = conn.BeginTransaction();
try
{
SqlCommand cmd = new SqlCommand("exec TestProc 0", conn);
cmd.Transaction = tran;
cmd.ExecuteNonQuery();

cmd = new SqlCommand("exec TestProc 1", conn);
cmd.Transaction = tran;
cmd.ExecuteNonQuery();

tran.Commit();
}
catch (Exception)
{
tran.Rollback();
}
conn.Close();


Here's the output of SQL profiler:



As I would expect, while the first call the SP succeeded, the second one failed, and all the "test" table should be empty. In fact, it was. Luckily for us, ADO.NET executes a IF @@TRANCOUNT > 0 after executing each statement. This allows it to know when to throw an exception if a ROLLBACK occurs in the T-SQL, which I'm catching in the try/catch block. However, I still call tran.RollBack just to ensure the exception wasn't caused by something else, even maybe an error message (but not rollback!) from the SP itself. And look, ADO.NET is smart enough to check if the @@TRANCOUNT is great than zero before calling ROLLBACK. I can't say it's that smart when you call trans.Commit, but you should never be calling Commit if you get an exception.

So, I thought my work was done, until I found another curious issue. It seems that ExecuteScalar does not throw an exception even if a rollback occurred within the SP or an error was generated by the SP. This obviously causes a problem in my book! There is a nice forum discussion about this on MSDN.

So, conclusion. Don't blindly use BeginTransaction, especially if you have stored procedures with transactions within them. Secondly, if you want to capture any sort of error from the SQL server, don't use ExecuteScalar.

Wednesday, October 22, 2008

Storing Information Per Thread

I recently ran across a situation where I wanted to store information per Thread. After doing some research I ran across the ThreadStaticAttribute class. You apply this attribute to a static field and the value for the variable is unique for each thread. Also note that a default value is useless since it will only be initialized once and the following threads will just have a null value. So, it is best to apply this attribute to a private field and have a public property that can initialize if needed.



[ThreadStatic]
private static string myThreadValue;

public static string MyThreadValue
{
get
{
if (myThreadValue == null)
myThreadValue = "New value";
return myThreadValue;
}
}


There are other methods to store data per thread. One is SetData / GetData methods on the class System.Runtime.Remoting.Messaging.CallContext. This method allows you to specify a named variable and a value. The reason I don't like this approach is that it doesn't allow you to strongly type the data since the SetData / GetDat use a Object parameter.

Finally, the Thread.AllocateDataSlot method allows you to create a "storage slot" and use the Thread.SetData and Thread.GetData to you to store information. The reason I don't like this approach is the same as mentioned above about the lack of strong typing. Secondly, this method is is slower than the ThreadStaticAttribute.