WPF: DataGrid Search And Replace: Binding DataGridColumn to Attached Property won’t work. Use Dependency Property PropertyChangedCallback instead.

February 8, 2010 by baraholka1

Scenario

I am implementing Search and Replace functionality in my DataGrid (WPFToolkit June 2009 release). I started off by using the Search and Highlight technique by Shamman Tomer on his blog WPF Essentials which works a treat. I then wanted a CheckBoxColumn called ‘Replace’ in my DataGrid with the CheckBox to automatically become visible and checked when rows in the Highlighted Column became highlighted. The rest of this article will assume that you have implement Search and Highlight as per WPF Essentials

Anyway, Easy, I thought, I’ll just bind the CheckBoxColumn to the Attached Property that flags the Highlight Column to become highlighted.

Not so easy. Taxi Driver, start the meter on the Journey Of Pain if you will.

You see, what I didn’t know at this stage was that DataGridColumns are different. Jaime Rodruigez, a WPf Technical Evangelist at Microsoft explains

The Columns collection is just a property in the Datagrid; this collection is not in the logical (or visual) tree, therefore the DataContext is not being inherited, which leads to there being nothing to bind to.

Binding To Attached Property

The syntax for Binding to Attached Properties is straightforward. My Attached Property is ‘IsMatch’ in the FindAndReplace class in my Infrastructure project, so I just went:

wpftoolkit:DataGridCheckBoxColumn Header="Replace"
CellStyle="{DynamicResource IsReplaceCell}"
Binding="{Binding Path=(inf:FindAndReplace.IsMatch),
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}" /

IsReplaceCell is a copy of Shamam’s Style Resource that sets the IsMatch Property to perform highlighting. I figured that I could bind to this and get a happy little checkmark appearing in my column.

Nope. Didn’t work. As Jaime Rodruigez could’ve told me.

There followed six hours of intense Googlefying and code tinkering that finally alerted me to the existence of DependencyProperty PropertyChangedCallbacks. Yes, this is my first WPF project.

So I added a PropertyChangedCallback to the IsMatch AttachedProperty, which is a DependencyProperty, like so:

'Using a DependencyProperty as the backing store for IsMatch. This enables animation, styling, binding, etc...
Public Shared ReadOnly IsMatchProperty As DependencyProperty = _
DependencyProperty.RegisterAttached("IsMatch", _
GetType(Boolean), _
GetType(SearchOperations), _
New UIPropertyMetadata( _
False, _
New PropertyChangedCallback(AddressOf OnIsMatchChanged)))

A PropertyChangedCallback To Call My Own
The definition of the PropertyChangedCallback is:

'''
''' Callback for IsMatch PropertyChanged
'''
''' </remarks
Private Shared Sub OnIsMatchChanged(ByVal sender As DependencyObject, _
ByVal e As DependencyPropertyChangedEventArgs)
// Do something interesting
End Sub

Takes Two and Three

As you can see there’s heaps of information available to you in the Callback. You get the whole Dependency Property which contains the object causing the Callback, in my case a DataGridCell and also the DependencyPropertyChangedEventArgs which contain the old and new values of the DependencyProperty.

This was all new to me not having performed an autopsy on a DependencyProperty before, but eventually I found out that my DataGridCell object was in there and a couple of interesting Properties such as DataContext and Content.

Based on that I first tried to set the Checkbox to IsChecked by binding it to a Boolean Property in the DataGridCell’s DataContext and implementing IsPropertyChanged. Didn’t work. So, then inspecting the Content Property I found to my joy that it was a CheckBox control, so I was able to set it to the New value of my IsMatch Dependency Property in the PropertyChangedCallback like so:

Private Shared Sub OnIsMatchChanged(ByVal sender As DependencyObject, _
ByVal e As DependencyPropertyChangedEventArgs)
Dim cell As DataGridCell = CType(sender, DataGridCell)
cell.Content.IsChecked = e.NewValue
End Sub

To differentiate the CheckBox column it was necessary to distinguish it from the TextBox (Highlighting) column in the same Grid which also listened to IsMatch.

If TypeOf (cell.Column) Is DataGridCheckBoxColumn Then
cell.Content.IsChecked = e.NewValue
End If

To make sure that only the particular CheckBox columns in my Search and Replace Grids would be affected, I subclassed DataGridCheckBoxColumn to ReplaceDataGridCheckBoxColumn and did the following

If TypeOf (cell.Column) Is ReplaceDataGridCheckBoxColumn Then
cell.Content.IsChecked = e.NewValue
End If

This is not essential as only Checkbox columns that had the IsReplaceStyle would trigger the IsMatch DependencyProperty PropertyChangedCallback, but it made it that little bit more tidy.

This was a major win. I was feeling omnipotent.

Pass The Viagara

Did I say omnipotent? Perhaps I really meant impotent.

The above code works great if your Replace column is always visible. If, however, you want to toggle the Replace column from Visibility.Hidden to Visibility.Visible then the above code crashes during the PropertyChangedCallback.

This is because the Content Property of an invisible DataGridCell is null, so cell.Content.IsChecked throws a Null Reference Exception. After another few hours Googling, waving dead cats at the terminal and smashing the keyboard with a Nerf Claw Hammer I came up with a hack of majestic proportions.

It dawned on me that its only when the CheckBox column is invisible that the Content is Null, so if I run a Dummy search returning zero matches, this will have the effect of making the column Visible and thus Content Is Not Null, then I can run the real Search and make the now visible columsn checked if they match! BWA-HA-HA-HAAAAA-AAAAAAAAAAAAAAAAAAAA...aaaaaaaa......!! It was so horrendous I loved it on sight. Here it is, peformed by Code-Behind for the sheer pleasure of violating the MVVM Design Pattern.

' Copy current Find And Replace so we can put it back after Impossible Search executes
Private Sub tbFindText_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles tbFindText.LostFocus
Dim origFindAndReplace As FindAndReplace = New FindAndReplace
origFindAndReplace.FindText = String.Copy(Me._viewModel.FindAndReplaceViewModel.FindAndReplace.FindText)
origFindAndReplace.ReplaceText = String.Copy(Me._viewModel.FindAndReplaceViewModel.FindAndReplace.ReplaceText)

Me._viewModel.FindAndReplaceViewModel.FindAndReplace.FindText = String.Empty

' Perform Impossible Search using Highest Priority
Application.Current.Dispatcher.Invoke( _
System.Windows.Threading.DispatcherPriority.Send, _
New System.Threading.ThreadStart(AddressOf ExecuteFindTextHandler))

' Perform real search using lowest priority, hoping that Highest Priority Thread terminates first.
' Should really use a Thread callback
Me._viewModel.FindAndReplaceViewModel.FindAndReplace = origFindAndReplace
Me._viewModel.FindAndReplaceViewModel.Visibility = Windows.Visibility.Visible

Application.Current.Dispatcher.Invoke( _
System.Windows.Threading.DispatcherPriority.Background, _
New System.Threading.ThreadStart(AddressOf ExecuteFindTextHandler))
End Sub

CONFESSION: I'm not that cavalier. I did put it in ViewModel afterwards.

The Final DataGrid

Here's the markup for the final dataGrid. If you've copied Shamam Tomer's exampel from WPF essentials this should basically make sense to you. The only bit left to explain is why I am Binding DataGrid Visibility to an x:Static see below:

wpftoolkit:DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=ModuleContents}"
inf:SearchOperations.SearchTerm="{Binding Path=FindAndReplace.FindText,
UpdateSourceTrigger=PropertyChanged}"
wpftoolkit:DataGrid.Columns
wpftoolkit:DataGridTextColumn Header="Content"
CellStyle="{DynamicResource ContentsCell}"
Binding="{Binding Contents}"
inf:ReplaceDataGridCheckBoxColumn Header="Replace"
Visibility="{Binding Source={x:Static Member=inf:DataGridViewModel.ColumnVisibility}, Path=ReplaceVisibility}"
CellStyle="{DynamicResource IsReplaceCell}"
Binding="{Binding Path=IsReplace, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" /
/wpftoolkit:DataGrid.Columns
/wpftoolkit:DataGrid

Binding DataGrid Visibility To x:Static

Like Jaime Rodruigez says, because dataGridColumns are not in the Visual or Logical tree you can't bind them to Class Properties. If you use Jaime's trick of fowarding the DataGrid's DataContext to the DataGridCell you can then bind the DataGridCell to an Property declared on another Control, but that seemed rather indirect to me. The other options open to you are:

  1. Binding to a StaticResource
  2. Binding to a Static Class

I decided binding to a Static Class fitted the MVVM pattern better as Binding to a StaticResource means you can't update that Binding unless you expose the View's StaticResources to its ViewModel, which exposed more detail of the View to the ViewModel than seems advised under MVVM.

In Summary

To wire up a DataGrid for Search and Replace and include optional amazing toggling Replace Column, the following is required:
1. Replace column in DataGrid must be of type ReplaceDataGridCheckedColumn.
2. Static Class to hold Visibility of Replace Column for DataGrid
3. A DataGridCell Style with a Style Trigger bound to the Property containing the Property to be searched. This is often a ‘Contents’ column containing HTML or Text to be Searched
4. A DataGridCell Style for the Replace column which causes automatic checking with a Depeendency Property PropertyChangedCallback
5. Bind DataGrid to the Dependency Properties in the SearchOperations Attached class
6. IsReplace Property implemented in Business Class that the ItemsSource of the DataGrid Binds to.

Integrating WPF Ribbon With Prism and MVVM (with VB.NET example)

January 31, 2010 by baraholka1

The WPF Ribbon control is an exciting UI element that provides instant Wow factor to applications and is fun to use, but the current CTP version for Framework v3.5 which is a parallel release for the WPF June 2009 toolkit, does not natively integrate with Prism or MVVM.

Firstly, the RibbonCommand is based on WPF’s standard RoutedUICommand class which does not have the same capacities as Prism’s DelegateCommand and secondly the RibbonCommand requires its CanExecute and Execute Event Handlers in Code Behind which violates the MVVM pattern. In addition, RibbonCommands must be declared as Static Resources which is an unusual restriction on WPF development.

Fortunately there are some smart programmers out there who have grappled with these issues. It turns out you need to:

  1. Sub-class RibbonCommand in order to delegate (that’s dele-GATE as a verb) the RibbonCommand to a Prism DelegateCommand via ICommand;
  2. Provide a Bridging Class which allows Static Resources to connect to a ViewModel Property instead of requiring EventHandlers in Code Behind.

The above is the discovery of sergioesp on the Prism Discussion Forum. I translated his code into VB.NET and added support for InputGestures (Menu Short Cuts).

SergioEsp, you are a legend!

So here’s the beef…

In the XAML for the RibbonWindow you need the following markup:

NB, inf is the xmlns for my Infrastructure project in which I have defined the classes CommandReference and DelegatedRibbonCommand

Window.Resources
inf:CommandReference x:Key="SaveAllCommandReference"
Command="{Binding SaveAllCommand}"/

inf:DelegatedRibbonCommand
x:Key="SaveAllCommand"
LabelTitle="Save All"
LabelDescription="Save all edits (Ctrl+S)"
ToolTipDescription="Save All edits"
ToolTipTitle="Save All (Ctrl+S)"
SmallImageSource="/Images/Ribbon/disk.png"
LargeImageSource="/Images/Ribbon/disk.png"
InputGestureKey="Ctrl+S"
DelegatedCommand="{StaticResource SaveAllCommandReference}" /

/Window.Resources

CommandReference is the Bridging Class that allows StaticResources to connect to a ViewModel Property by exposing a Dependency Property. SergioEsp got this class by using the WPF Futures MVVM Visual Studio Template, version 0.1. I didn’t want to use the MVVM Template, so I just downloaded the code and stole the CommandReference class. Here it is, translated to VB.NET

Imports System
Imports System.Windows
Imports System.Windows.Input

'''
''' This class facilitates associating a key binding in XAML markup to a command
''' defined in a View Model by exposing a Command dependency property.
''' The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
'''
'''
''' Taken from MSDN WPF Futures MVVM Toolkit v0.1 source. Converted from C# to VB
'''
Public Class CommandReference
Inherits Freezable
Implements ICommand

#Region "Constructor..."

Public Sub CommandReference()
'// Blank
End Sub

#End Region

#Region "Properties..."

Public Shared ReadOnly CommandProperty As DependencyProperty = _
DependencyProperty.Register("Command", GetType(ICommand), GetType(CommandReference), _
New PropertyMetadata(New PropertyChangedCallback(AddressOf OnCommandChanged)))

Public Property Command() As ICommand
Get
Return CType(GetValue(CommandProperty), ICommand)
End Get
Set(ByVal value As ICommand)
SetValue(CommandProperty, value)
End Set
End Property

#End Region

#Region "ICommand Members"

Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute

If Not Command Is Nothing Then
Return Command.CanExecute(parameter)
End If

Return True
End Function

Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
Command.Execute(parameter)
End Sub

#End Region

Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged

Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

Dim commandReference As CommandReference = CType(d, CommandReference)
Dim oldCommand As ICommand = TryCast(e.OldValue, ICommand)
Dim newCommand As ICommand = TryCast(e.NewValue, ICommand)

'If oldCommand IsNot Nothing Then
' 'RemoveHandler oldCommand.CanExecuteChanged, commandReference.CanExecuteChanged
'End If
'If newCommand IsNot Nothing Then
' newCommand.CanExecuteChanged += commandReference.CanExecuteChanged.Canxecute

'End If

commandReference.Command = newCommand
CommandManager.InvalidateRequerySuggested()

End Sub

#Region "Freezable"

Protected Overrides Function CreateInstanceCore() As Freezable

Throw New NotImplementedException()
End Function

#End Region

End Class

The DelegatedRibbonCommandClass is a translatation of SergioEsp’s RibbonCommandEx class with added support for InputGestures. As you can see, the RibbonCommand is made to use the CanExecute and Execute handlers of the ICommand in the DelegatedCommand Property of DelegatedRibbonCommand.

Since we are using Prism, that DelegatedCommand Property is a Prism DelegateCommand. The ViewModel code is shown further below, but first, here’s the DelegatedRibbonCommandClass:

Imports System.Windows.Input
Imports Microsoft.Windows.Controls.Ribbon

'''
''' Class to delegate a RibbonCommand to an ICommand.
''' Allows RibbonCommands to be used within WPF Prism in a MVVM design pattern
'''
'''
''' Code taken from MSDN Prism Discussion Forum
''' http://wpf.codeplex.com/Thread/View.aspx?ThreadId=52089
'''
Public Class DelegatedRibbonCommand
Inherits RibbonCommand

'''
''' KeyGestureConverter for all DelegatedRibbonCommand instances
'''
'''
Private Shared _myKeyGestureConverter As KeyGestureConverter
Public Shared ReadOnly Property MyKeyGestureConverter() As KeyGestureConverter
Get
If _myKeyGestureConverter Is Nothing Then
_myKeyGestureConverter = New KeyGestureConverter
End If
Return _myKeyGestureConverter
End Get
End Property

'''
''' ICommand that this RibbonCommand will delegate to
'''
'''
Private _delegatedCommand As ICommand
Public Property DelegatedCommand() As ICommand
Get
Return _delegatedCommand
End Get
Set(ByVal value As ICommand)

Me.SetKeyGesture()

If Not _delegatedCommand Is value Then
_delegatedCommand = value
If Not _delegatedCommand Is Nothing Then
AddHandler Me.CanExecute, AddressOf DelegatedRibbonCommand_CanExecute
AddHandler Me.Executed, AddressOf DelegatedRibbonCommand_Executed
Else
RemoveHandler Me.CanExecute, AddressOf DelegatedRibbonCommand_CanExecute
RemoveHandler Me.Executed, AddressOf DelegatedRibbonCommand_Executed
End If
End If
End Set
End Property

'''
''' Property which contains String for InputGesture
'''
'''
Private _inputGestureKey As String
Public Property InputGestureKey() As String
Get
Return _inputGestureKey
End Get
Set(ByVal value As String)
_inputGestureKey = value
End Set
End Property

#Region "Event Handlers..."
'''
''' Event handler for Execute event.
''' Calls Execute method on DelegateCommand
'''
'''
'''
'''
Private Sub DelegatedRibbonCommand_Executed(ByVal sender As Object, _
ByVal e As ExecutedRoutedEventArgs)
DelegatedCommand.Execute(e.Parameter)
End Sub

'''
''' EventHandler for CanExecute event
''' Calls CanExecute function on DelegatedCommand
'''
'''
'''
'''
Private Sub DelegatedRibbonCommand_CanExecute(ByVal sender As Object, _
ByVal e As CanExecuteRoutedEventArgs)
e.CanExecute = DelegatedCommand.CanExecute(e.Parameter)
End Sub

#End Region

'''
''' Set the KeyGesture for this DelegatedRibbonCommand
'''
'''
Private Sub SetKeyGesture()
If Not String.IsNullOrEmpty(Me.InputGestureKey) Then
MyBase.InputGestures.Add( _
MyKeyGestureConverter.ConvertFromString(Me.InputGestureKey))
End If
End Sub

End Class

In the ViewModel, we define the Prism DelegateCommand that we want the Ribbon to use. This is standard Prism and MVVM code.

Private _saveAllCommand as ICommand
Public Property SaveAllCommand() As ICommand
Get
return _saveAllCommand
EndGet
Set(value As ICommand)
_saveAllCommand = value
EndSet

In ViewModel Constructor or some arbitrary InitialiseCommands method:
SaveAllCommand = new DelegateCommand(Of Object)(Address Of Me.SaveAll)

The StaticResource to the ViewModel Property by the markup further up.
The WPFRibbon then uses the Prism DelegateCommand in the ViewModel by referencing the StaticResource.
Note below that the SaveAllCommand is the x:Key for the DelegatedRibbonCommand in the Window Resource, NOT the ICommand ViewModel Property.

r:Ribbon.ApplicationMenu
r:RibbonApplicationMenu Command="{StaticResource MainMenuRootCommand}"
!-- Hack to make sure sub-menu items appear.
Add a RecentItems List

http://www.chakkaradeep.com/post/Proper-way-to-create-Menus-2b-Submenus-in-WPF-Ribbon.aspx--

r:RibbonApplicationMenu.RecentItemList
Rectangle Height="200"/Rectangle
/r:RibbonApplicationMenu.RecentItemList
r:RibbonApplicationMenuItem Command="{StaticResource SaveAllCommand}" /
r:RibbonApplicationMenuItem Command="{StaticResource CloseAllCommand}" /
/r:RibbonApplicationMenu
/r:Ribbon.ApplicationMenu

Happy to answer any questions if any of the above is not clear.

The word in the forums is that Microsoft are rewriting the WPF Ribbon to make it natively compatible with MVVM so hopefully before long we won’t need these aditional classes to make it possible.

Conditionally Execute HTML In Internet Explorer and/or Non-Internet Explorer Browsers (and How to Provide an Alt Image If Flash Is Disabled)

January 29, 2010 by baraholka1

Our Web Monkeys Were Collapsing With Exhaustion…
…so the developers were summoned from their ivory tower to lend a hand. My job, to write the temporary US Landing Page for our company while Marketing smashed their heads together to devise an irresistible glob of eye candy for the permanent site.

I had a requirement to show a Flash file on the Web Page. The Flash file had to be shown in IE6, IE8 and Firefox. I was given an example Web Page and was told that the lead tester’s machine commonly hung while viewing our pages in IE6.

Embedding Flash

The HTML for embedding a Flash object is quite different in Firefox than in IE.

The HTML for embedding a Flash object in IE starts out like this:
object classid=”clsid:D27CDB6E-AE6D-11cf-96B8-444553540000″

whereas in Firefox it looks like this:
object type=”application/x-shockwave-flash”

If IE6 encounters the object tag for the Firefox definition of a Flash file it hangs, whereas IE8 eats it and ignores it.

“Cool”, I thought, “now I know why our lead tester’s machine hangs all the time. I must be a freaking genius to figure out this problem in about 30 seconds flat.”

It seemed strange to me that our Web Monkeys hadn’t figured this out before.

The sample web page I was pointed to had this code in it:

cross browser html

That first block at the top, the one starting with with [If IE], rendered totally in green text inside Visual Studio means ‘Comment’ so the first thing I did was throw away the comments to get a clean file to work with.

Did you spot my Journey Of Pain beginning ?

Yes, that Green text was a comment all right, but my ignorant non-Web Monkey consciousness did not know that they were very special comments indeed, known as Conditional Comments, specifically designed for Cross-Browser implementations just like the one I had been assigned.

In other words, I had just thrown away the core functionality I needed to get the task completed. THE PAIN! THE PAIN! THE ROTTING PAIN!

So it took me about two days to finally figure out that I needed those ‘comments’ and that the template which had been given to me was totally correct. I just needed to update file names and links but instead spent three-fifths of an eternity trawling the Web for exotic and completely unneeded browser detection strategies.

The bitter irony is that if I had not been told that our lead tester’s machine uang on IE6 then I would not have thought that the code I had been supplied was buggy or redundant. AARGH! For those with Accessibility Options turned on I will type that again AAAAAARGGGGHHHH!!.

Yeah, so that code at the top is perfectly fine. It shows a Flash file in IE6, IE8 and Firefox. And the anchor tag underneath the two Flash object blocks shows an Alternate Image in case Flash is disabled in either IE or Firefox.

…and if you hadn’t guessed yet the reason our lead tester’s machine hangs in IE6 has absolutely nothing to do with cross-browser Flash issues. That was merely a monstrous bioluminescent red herring (also here)

Here’s How It Works

Conditional Comments ONLY work in IE for Windows and a few other particular browsers. As David Hammond explains in CSS Hacks – The Good, The Bad And The Ugly

Conditional Comments apply specifically to browsers using Internet Explorer’s Trident layout engine, meaning IE-based browsers like Maxthon and Avant handle them like Internet Explorer does while browsers using other layout engines see them simply as regular comments. Internet Explorer on the Mac uses a different layout engine and doesn’t support conditional comments.

So when IE lumbers along and reads that first Comment [If IE], it goes “Yee-har. That’s for me. Let me interperet that sucker.” and when it finds <!– [if !IE] it does the reverse and ignores it as serenely as lunchtime crowds do the homeless.

On the other hand, non-IE browsers see the first block as a standard comment and therefore ignore it; but later ignore ONLY the Conditional Comment tags [if !IE] that frame the Firefox Flash definition as they are, in fact, also standard everyday comments if you look at the structure. So non-IE browsers read and interperet only the HTML inside the [If !IE] Conditional Comments whereas IE ignores the lot.

Clever eh?

Wish I had read Wikipedia’s page on Conditional Comments before I started, but then I didn’t even know there was such a beast before I started. Cost me two days of precious heartbeats that. THE ROTTING PAIN!!

WPF: Validating ComboBox

January 4, 2010 by baraholka1

Happy New Decade!!

Through inefficient Googling this one cost me more time that it needed to, but the answer on how to validate a ComboBox in WPF turns out to be very simple. Just Bind to SelectedItem and have a WPF ValidationRule for the type of the class in the ComboBox list.

In XAML:

ComboBox.SelectedItem
Binding Path=”SelectedCustomer”
Mode=”TwoWay”
BindingGroupName=”FormBindingGroup”
Binding.ValidationRules
local:CustomerValidation /
ExceptionValidationRule /
/Binding.ValidationRules
/Binding
/ComboBox.SelectedItem
/ComboBox

In Code-Behind or ViewModel:

Private _SelectedCustomer As Customer
Public Property SelectedCustomer() As Customer _
Get
Return _SelectedCustomer
End Get
Set(ByVal value As Customer)
_Customer = value
End Set
End Property

CustomerValidation class:
”’
”’ Class to encapsulate Customer validation
”’
”’
Public Class CustomerValidation
Inherits ValidationRule

Public Overrides Function Validate( _
ByVal value As Object, ByVal cultureInfo As CultureInfo) As System.Windows.Controls.ValidationResult

If Not TypeOf (value) Is WebLibrary.Customer Then
Return New ValidationResult(False, “Customer not selected”)
ElseIf value Is Nothing Then
Return New ValidationResult(False, “Customer not selected”)
Else
Return ValidationResult.ValidResult
End If

End Function

And in a Resource File you have the Style Triggers which show the validation messages in a ToolTip as embraced by the Greater Google:

!– TextBoxes
Failing Validation get a ToolTip with the error message in it –
Style x:Key=”ValidatedControl” TargetType=”{x:Type Control}”
Style.Triggers
Trigger Property=”Validation.HasError” Value=”true”
Setter Property=”ToolTip”
Value=”{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}” /
/Trigger
/Style.Triggers
/Style

Style x:Key=”ValidatedComboBox” TargetType=”{x:Type ComboBox}” BasedOn=”{StaticResource ValidatedControl}” /
Style x:Key=”ValidatedTextBox” TargetType=”{x:Type TextBox}” BasedOn=”{StaticResource ValidatedControl}” /

Naturally somehere you will need to make a call to update the BindingSource in order to make the Validators fire
e.g.

‘Forces call to Validators. Just calling BindingGroup.UpdateSources doesn’t achieve that.
For Each bindingExpression As BindingExpressionBase In Me.MainForm.BindingGroup.BindingExpressions
bindingExpression.UpdateSource()
Next

Acknowledgements

Wei Zhou, moderator of the .NET Development Forum WPF group will definitely receive a glorious gift pack of premium jasmine-scented Tadpole Roe,
for solving my immediate problem in the very uncryptically named thread “Combo Box Validation” (how did I miss it for two hours); and

Beth Massi, of Visual Basic Developer Centre receives a complimentary quarter-tonne package of Tadpole-flavoured Cashew Nuts courtesy of our proud sponsors Reject Fish Products Inc., for her very nice Resource File layout for the Style Triggers in “Displaying Data Validation Messages In WPF”

LINQ SubmitChanges throws Index Out Of Range Error

December 23, 2009 by baraholka1

You’ll get this if you have Associations in your dbml file which do not reflect the reality of your database.

In my case I was given a dbml file (NOT MY BUG! NOT MY BUG!) that had spurious Associations in them. Like unwanted relatives these must be bought off sent to nunneries deleted.

My database tables are:

Product
Order
Customer
Student

I was doing an update on an Order record and got the Index Out Of Range error on SubmitChanges.

Inspecting the dbml, on the advice of this very helpful thread on Stack Overflow and also this one on MSDN LINQ Project General, there were LINQ Associations between Product->Order, Product->Customer and Product->Student.

The last two Associations were spurious. They shouldn’t have been there, specifically, there is no ProductID ForeignKey on Customer and no ProductID ForeignKey on Student either

Once I deleted the spurious, unwanted, grotesque, unneeded, and overall wrong Associations I was once again able to save my Order records during Update, so making the planet safe for Capitalism.

Server Did Not Recognise The Value Of HTTP Header SOAPAction (Or How Not To Add A New WebMethod to an existing WebService)

December 17, 2009 by baraholka1

This can come about when your Web Services References file (automatically generated by Visual Studio) is out of sync with your asmx file because you have manually hacked added some Web Methods. Make sure that all your WebMethods have corresponding entries in the Web Service Web Reference file (Reference.vb or Reference.cs).

WARNING: If you are trying to Add a new WebMethod to an existing Web Service do not do what I did below. It will only consign you to a Journey of Pain.

I am maintaining an app. that has a large WebService that operates on a group of DataSets. Each DataSet has three WebMethods associated with it. I created a new DataSet, cut and pasted the three standard Web Methods the app. needs to operate on the new DataSet and manually hacked the Reference.vb file to set up the calls to Invoke the Web Method and the Async versions of the Web Method i.e this stuff:

Public Function GetPreviewSEODataSetByDomain(ByVal domaincode As Integer) As System.Data.DataSet
Dim results() As Object = Me.Invoke(“GetPreviewSEODataSetByDomain”, New Object() {domaincode})
Return CType(results(0), System.Data.DataSet)
End Function

BUT I only manually hacked in the necessary Invokes for ONE of the WebMethods, not all three. For some reason this caused the SOAPAction error. When I removed the other two WebMethods from the asmx file the error went away.

If the above does not pertain to you, make sure you haven’t messed up the Web Service Namespace.

Now here’s what I should have done:

How To Add A New WebMethod to an existing WebService

1. Add the new Web Method to your asmx file. Decorate with WebMethod attribute of course.
2. Get Visual Studio to update the WebReference file, Reference.vb/Reeference.cs. This file does not belong to the Web Service per se. Rather it belongs to the Project which calls the WebService. Visual Studio generates it when you add the Web Reference.

To update it, locate the reference to your Web Service in your calling Project’s Web Reference folder, right-click and select Update Web Reference from the Context Menu. No more hacking, everything works, pain in cranium stops. To celebrate, I recommend making yourself a free instant coffee from the staff room. That’s what I did. (NB. This is unconnected to actually debugging your WebService)

Cannot Drag Additional Table From Server Explorer Into Existing DataSet (.xsd) Failed to add TableAdapter.

December 8, 2009 by baraholka1

Just a short one here,…

I have a very complicated DataSet (.xsd) file and our schema is being updated. I needed to drag a couple of new tables from the ServerExplorer into the DataSet Designer…but Visual Studio wouldn’t let me do it. I just got the ‘ghostbusters’ icon and the table would not drop.

Trying to work around the issue, I did right-click on Designer Add->TableAdapter and configured the Adapter but when clicking ‘Finish’ I got the error ‘Failed to add TableAdapter’ along with the SQL Named Pipes Provider error 40.

For half an hour I toyed with hand-coding the necessary table and all associated ADO.NET code directly into the DataSet designer file. Fortunately this madness subsided.

Then I created a new DataSet and copied/pasted the old DataSet from the Designer into the new one. This worked. No Named Pipes errors AND I was able to drag new tables into the Designer for the new DataSet. But this was all just a Journey Of Pain. I should be able to simply edit the existing DataSet. I threw a brick through the monitor and went home for a relaxing session of concreting the hamster (also here) to let my right-brain work on the problem unencumbered by reason.

Well today’s a new day and since it was my last straw, what I did was rebind the Project to Visual Source Safe. I had unbound the Project from Soure Control a little earlier for weird reasons I will keep to myself. Once I rebound the Project to Source Control via File -> Source Control -> Change Source Control I was able to add the new table to the Dataset. This was so stupidly illogical that it did not surprise me in the least.

Once day I will be able to explain why this makes any difference but today I can’t. But if you’re in a similar position why not try rebinding the Source Control? It worked for me and now I’m a multi-millionaire with a full head of hair and a tremendously loyal pet Aardvark. Buy one today.

WPF: Replicating MDI Application Behaviour

November 25, 2009 by baraholka1

I decided to use Prism, aka Microsoft Composite Application Guidance For WPF and Silverlight.

This is very much like using a supersonic jet fighter to pop down to the local shops but its effective, not very hard and does not require you to write reams of fragile custom code. You just need to spend a bit of time getting used to Prism. Now that’s not a trivial task, but you should have covered the fundamental concepts within a working week, assuming you have some prior knowledge of Dependency Injection.

The huge advantage to you is that Prism is written by people who are much better programmers than you or I are, so the code is robust, whereas writing a custom MDI Window Manager is out of the league of most Intermediate-level developers (most of us).

Very simply, Prism allows the development of Modular Applications in which the User Interface of an application is decomposed into loosely-coupled Modules, each of which occupies a specific area on the screen, known as a Region.

No window (known in Prism as a View) can move outside the Region in which it is contained. This replicates the MDI-like behaviour you’re looking for.

Prism is also a library of Best Practices – it’s produced by the Microsoft Patterns and Practices team – so you get heaps of built-ins for free. And there’s a helpful forum where others using Prism are happy to help us newbies.

You can also have multiple windows (views) open simultaneously if you want, but this will render as a tabbed interface which is not exactly the same as MDI.

I embarked upon this approach and have been successful in producing a MDI-like app. It wasn’t too hard and I learnt heaps along the way. Go for it.

Update 27-Jan-2010

Ahmed in comments asked me to post the code for my MDI sample.

All I did was use a WPF ContentControl for each Prism Region. A ContentControl can only display one View at any one time, so each time you activate a View the one currently in the Region becomes invisible and the new one you activated becomes visible.

This provides the illusion that there is only one window present at any one time. In fact, all the Views you activated are still there, just invisible until you explictly RegionManager.Remove the View from the Region. This is kind of good though, because the ‘invisible’ views are there in memory and so become visible again instantly you do RegionManager.Activate on them. Effectively this is caching the Views for performance.

So here’s my Shell Window markup:

Grid/
ContentControl Grid.Column="1" Grid.Row="1" cal:RegionManager.RegionName="MainRegion"
VerticalContentAlignment="Top"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"

ContentControl Grid.Column="1" Grid.Row="1" Name="AdminRegion"
cal:RegionManager.RegionName="AdminRegion"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.ColumnSpan="2"

ContentControl Grid.Column="2" Grid.Row="1" cal:RegionManager.RegionName="ToDoListRegion"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"

/Grid

So you notice I have three ContentControls, one for each of my regions. I also have a Menu with a gazillion MenuItems (not shown).

Each Menu Item is bound to a Prism DelegateCommand like so:

'''
''' Initialise Commands On The Module Menu
'''
Private Sub InitialiseContentCommands()
Me.ContentShowLocalContentCommand = _
New DelegateCommand(Of Object)(AddressOf Me.ShowLocalContent)
Me.ContentShowModuleContentCommand = _
New DelegateCommand(Of Object)(AddressOf Me.ShowModuleContent)
Me.ContentShowModuleDetailsCommand = _
New DelegateCommand(Of Object)(AddressOf Me.ShowModuleDetails)
End Sub

And here’s the ViewModel code related to those DelegateCommands to show the View:

#Region "Module View Injection..."

'''
''' Show Module Content View
''' Update MainRegion with Module Content View
'''
Public Sub ShowModuleContent()
Dim mainRegion As Region = mainView._regionManager.Regions(RegionName.MainRegion)

Dim existingView As Object = mainRegion.GetView(ViewName.ModuleContentView)

If (existingView Is Nothing) Then

Dim moduleContentViewModel As IModuleContentViewModel = _
mainView._container.Resolve(Of IModuleContentViewModel)()
mainRegion.Add(moduleContentViewModel.View, ViewName.ModuleContentView)
mainRegion.Activate(moduleContentViewModel.View)
Else
mainRegion.Activate(existingView)
End If

End Sub

#End Region

Also in the Shell ViewModel I handle a Prism CompositeEvent that is published every time a View is closed in the MainRegion (i.e. by the User clicking on the Close Button for a View. This removes (destroys) the View as opposed to just letting it get overwritten by the next View that gers activated.

#Region "Event Handlers..."

'''
''' Handler for Composite CloseViewEvent
'''
Public Sub CloseViewEventHandler(ByVal closeViewInfo As CloseViewInfo)
Dim regionManager As RegionManager = _
mainView._container.Resolve(Of IRegionManager)()

'Find the region
Dim regionQuery = _
From regn In regionManager.Regions _
Where regn.Name = closeViewInfo.RegionName _
Select regn

' Find the View in the region
Dim region As Region = regionQuery.Single()
Dim view As Object = region.GetView(closeViewInfo.ViewName)

' Remove the view
If view IsNot Nothing Then
region.Remove(view)
End If

' Since ContentControl can only show one view at a time, if any one View is closed
' then no views are visible
Me.AllViewsClosed()
End Sub

I also have a CloseAll MenuItem which closes all the cached (invisible) Views in the Main Region, to give the users and the application the opportunity to clean up all those Views. This is done by having every View Register its CloseButton Command with the a Prism CompositeCommand called CloseAllCommand and having every View provides a handler which Publishes a Prism Event Aggregation event I defined called CloseView.

The Shell ViewModel subscribes to the Event Aggregation CloseView event and removes the calling View from the MainRegion. Since all Views do this, all Views are Removed

In XAML:

MenuItem Header=”_ Close Background Windows” Name=”mniCloseAll”
Command=”{x:Static inf:GlobalCommand.CloseAllCommand}” FontSize=”9″ /

In the ViewModel:

”’
”’ Subscribe to Event Aggregator CloseViewEvent
”’
”’
Private Sub SubscribeCloseViewEvent()

Dim ea As EventAggregator = mainView._container.Resolve(Of IEventAggregator)()

‘ Subscribe to CloseViewEvent
Dim closeViewevent As CloseViewEvent = ea.GetEvent(Of CloseViewEvent)()

If _closeViewSubscriptionToken IsNot Nothing Then
closeViewevent.Unsubscribe(_closeViewSubscriptionToken)
End If

‘ Perform the event on the UIThread
‘ keepSubscriberReference Alive is false. This means the event maintains a
‘ weak reference to the subscriber instance, allowing the garbage collector to
‘ dispose the subscriber instance when there are no other references to it.
‘ When the subscriber instance gets collected, the event automatically unsubscribes.
_closeViewSubscriptionToken = _
closeViewevent.Subscribe(AddressOf CloseViewEventHandler, ThreadOption.UIThread, False)
End Sub

So hopefully you can see that each region only has one View visible at any one time (kind of like a MDI application that I am porting) and because the Views are situated in a Region, they can’t move out of there, which is MDI-like behaviour. No letting your users drag windows all over the screen and losing them.

Careless users.

Visual Studio 2008 Disappears From Screen When Loading Solution or Project

November 5, 2009 by baraholka1

I started up Visual Studio 2008 and clicked on my Solution file AmazingSolution on the Start Page. Visual Studio started dragging up the solution and was just loading up a XAML file when it disappeared. Just disappeared.

This was about as expected as a nasty case of Sponteneous Combustion. Alice In Wonderland is one of my favourite books but when Visual Studio starts behaving like the Chesire Cat it’s a little perturbing…more so since I had just hacked the Registry to overcome the issue of Intellisense not appearing in XAML files after installation of Windows SDK.

My terror was complete. I had destroyed Visual Studio.

The paramedics were called, the Emergency Sacrifical Ferret put on stand-by and I ran a repair on Visual Studio, as Microsoft Connect recommended.

Then I rebooted, restarted Visual Studio and loaded AmazingProject. Visual Studio disappeared again. I slaughtered the Ferret and ordered a crate of Vodka in celebration of my impending sacking, but then checked the Event Logs. Glory! It contained a very useful entry:

.NET Runtime version 2.0.50727.3603 – Fatal Execution Engine Error (7A036050) (80131506)

and Googling that showed that Microsoft Connect have patch files for that very issue…Well, it was too late for the Ferret, but I HADN”T DESTROYED VISUAL STUDIO AFTER ALL!

To Summarize

1. The horrifying sudden disappearance of Visual Studio 2008 can be caused by a bug relating to the loading of XAML files. Check your Event Logs and if you see .NET Runtime version 2.0.50727.3603 – Fatal Execution Engine Error get the patch from Microsoft Connect at the link above. No Ferrets need to be slaughtered.

2. The unrelated problem of Intellisense disappearing from XAML files is caused by installing a Windows SDK after (on top of) Visual Studio 2008. This overwrites the registry key VS uses to locate the XAML Intellisense file. To fix that, run a Repair on Visual Studio 2008 or, if you’re feeling brave, hack the registry.

So What Happened To You ?

Sick of having no Intellisense in XAML I Googled the registry hack. At the same time I did a ‘Close All But Me’ in Visual Studio on a XAML file then closed Visual Studio. I then restarted Visual Studio to allow the registry hack to take effect, whereupon it did the Chesire Cat on me. It was simply a malevolent coincidence…perhaps a warning never to do a registry hack when a more polite known fix exists :-) (In this case the Visual Studio Repair.

Hey Bruce, chuck us another Ferret will ya ?

WPF Setting Default Sort Order For Datagrid Without Using A Static Resource

November 4, 2009 by baraholka1

For this you need a CollectionViewSource and bind the DataGrid ItemsSource to the View property of the CollectionViewSource.

The Basic Wire-Up
Andrea Boschin on Silverlight PlayGround demonstrates the basic wiring for Binding the DataGrid ItemsSource to the CollectionViewSource. Andrea is using MVVM and hence the CollectionViewSource is a property in his ViewModel class. I’ve excerpted it here:

Public Class SomeViewModel

// The View of this cvs will get bound to a datagrid
Public CollectionViewSource People { get; set; }

// The actual data that the cvs will construct its view out of
protected ObservableCollection(Of People) PeopleInternal { get; set; }
}
} //end class

In XAML, bind the DataGrid to the View of the People CollectionViewSource:

wpftoolkit:DataGrid ItemsSource="{Binding People.View}"
wpftoolkit:DataGrid.Columns
wpftoolkit:DataGridTextColumn Header="Name"
MinWidth="120" Binding="{Binding Path=Name, Mode=TwoWay}" /
wpftoolkit:DataGridTextColumn Header="Age"
Width="auto" Binding="{Binding Path=Age, Mode=TwoWay}" /
/dg:DataGrid.Columns
/wpftoolkit:DataGrid

Taking over from where Andrea left off, when the ObservableCollection is set, we initialise the CollectionViewSource:


protected ObservableCollection(Of People) PeopleInternal
{ get {return _peopleInternal };
set;
{
_peopleInternal = value
People.Source = _peopleInternal
}
}

Setting Default Sort Order
In order to establish the default Sort Order for the Datagrid, add a SortDescription to the CollectionViewSource that the DataGrid is bound to. This creates the required view.

Let’s say we wanted our Datagrid to have a sort order of ‘Name’, assuming ‘Name’ is a field on the People class.


protected ObservableCollection(Of People) PeopleInternal
{ get {return _peopleInternal };
set;
{
_peopleInternal = value
People.Source = _peopleInternal
// Setting Sort Order for People
// Sort by Name field
People.SortDescriptions.Clear()
SortDescription sd = new SortDescription("Name", ListSortOrder.Ascending)
People.SortDescriptions.Add(sd)
}
}

So, every time PeopleInternal is updated, the CollectionViewSource creates a view on PeopleInternal which is sorted by Name. This is held in People.View. Because the DataGrid is bound to this View the data in it is always sorted by name when the ItemsSource (the ObservableCollection(Of People) is initialised

This works around the behaviour in the WPF Toolkit DataGrid where all SortDescriptions for the DataGrid are lost when the ItemsSource is initialised

Of course, users will still be able to click on Column Headers and sort at any time on any other column they want to.