Tuesday, April 26, 2016

Control's IsEnabled is inherited from the parent control

Two posts in two days. Must be having a bad week.

I spent four hours trying to figure this one out. The problem was that the user is unable to select a row in a datagrid and the datagrid looked disabled. The datagrid is inside a user control.

I put a breakpoint in a routed command's can_execute method (very handy for debugging) and quickly determined that the datagrid's IsEnabled property is false. Then it gets weird - I set it to true in the watch window and it immediately gets set false again even though it is a read/write property.

So I added an IsEnabledChanged event handler in the initialization code. When the event handler was called to set the IsEnabled property false I looked at the call stack, but it just says "External Code". So my code isn't the problem.

The user control is inside an expander which is initially collapsed. I notice the datagrid is only being disabled as the expander expands, ie when the user control is initially rendered. I have no Loaded event handler.

I took all the styles off the datagrid in case I had some kind of trigger on a style but that didn't fix the problem. I don't have a default style for datagrids.

Then I went home because it was late and I was making no progress. Plus it was snowing at my house and I had to get home in my Prius.

The next morning I noticed the user control's IsEnabled property is false and I can't change it to true in the watch window. Same behaviour as the datagrid. I wondered if the datagrid's IsEnabled property is inherited from the user control.

Then I started to look for the code that was disabling the user control. Found it! When I modified the code to not disable the user control everything worked correctly.

It's amazing how sleeping on an intransigent problem can help you solve it. It's tempting to keep working on a problem but sometimes walking away from it for a while can be the right thing to do.

Monday, April 25, 2016

Suppressing dropdown on a combobox

Framework Version 4.0

I have a combo box that displays a list of values the user can chose from or they can enter their own reason. The combo box drop down is populated from a table that the users can maintain.

<ComboBox IsReadOnly="False" IsEditable="true" SelectedValue="{Binding Description}" DisplayMemberPath="Description" SelectedValuePath="Description" ItemsSource="{Binding Descriptions}"/>


Nothing too exciting here.

But sometimes the table is empty. Then we want the users to be able to enter text but they have nothing to chose from. When they drop the dropdown it looks nasty. Like this...


Combo box with a zero row itemssource

If I disable the combo box to prevent them dropping it then they can't enter a value either. The solution is to set MaxDropDownHeight = 0 either in code or using a converter. Then when the user tries to drop the dropdown nothing happens. Like this...

Same combo box with MaxDropdownHeight = 0

Tuesday, April 12, 2016

Extracting the real reason for a 500 error from Reporting Services

One of the things that makes our lives more difficult is when Microsoft returns generic error messages that simply mean "Something went wrong". In this case we find that Reporting Services returns error 500 when the report is missing, invalid, missing a data source, is called with bad parameters, or any of a hundred other possible problems.

Up until now I have simply pasted the URL into a web browser to see the real problem I really wanted something better.

The code is fairly simple. I create a web request and grab the response.

Dim URL As String = "http://MyReportServer/ReportServer?/REQUISITION&rs:Format=PDF&rs:Command=Render"
Dim Request As System.Net.HttpWebRequest = Nothing
Dim Response As System.Net.HttpWebResponse = Nothing
Dim Stream As System.IO.Stream = Nothing

Request = System.Net.WebRequest.Create(URL)
Request.UseDefaultCredentials = True
Request.PreAuthenticate = True
Request.Timeout = 300000
Response = Request.GetResponse()
Stream = Response.GetResponseStream()

If a problem occurs the call to GetResponse will throw an exception indicating the server returned a 500 server error. But the response stream actually contains the real error message embedded in an HTML page. So in the catch block try this code...

Dim ErrorMsg As String = ex.Message
If TypeOf ex Is System.Net.WebException Then
   Dim WE As System.Net.WebException = DirectCast(ex, System.Net.WebException)
   Dim Buffer(WE.Response.GetResponseStream().Length) As Byte
   Dim HTML As String
   Dim pLI As Integer, pA As Integer
   WE.Response.GetResponseStream().Read(Buffer, 0, WE.Response.GetResponseStream().Length)
   HTML = System.Text.Encoding.Default.GetString(Buffer)
   pLI = HTML.IndexOf("<li>") + 4
   pA = HTML.IndexOf("<a ", pLI)
   If pLI > -1 AndAlso pA > -1 Then
      ErrorMsg = HTML.Substring(pLI, pA - pLI)
   End If
End If
Throw new Exception(ErrorMsg)