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.
Tuesday, April 26, 2016
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...
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...
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
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)
Tuesday, March 15, 2016
Configuring Reporting Services to be run from a middle tier when not in a domain
Earlier I wrote a blog entry on how I configured Reporting Services to be run from a middle tier using NETWORK_SERVICE. Today I had to figure out how to do the same thing when the middle tier and the reporting server are not in a domain - they are in two separate work groups.
The obvious thing to do is to create an identical user locally on each server, same user - same password. Let's assume we created a non-administrator user called ReportServiceUser. We made the user non-administrator because we care about security.
Next, you logon to the middle tier, find the application pool that hosts your WCFService, and use ReportServiceUser as the identity.
Then you logon to the Report Server and give ReportServiceUser access to the site and the reports. This is all very similar to the procedure explained in the earlier blog entry.
Try to run the report through Report Manager from the middle tier and you may or may not succeed. But if you use your application to run the report from the WCFService you will fail. You will also see that the application pool has stopped. Open up event viewer on the middle tier and look at the System events...
First we got three warnings, then we got an error. The error tells us the services was stopped but it's the first warning that has the important error code.
Simply searching Google on the error code will normally tell you the next step to take. In this case we find that ReportServiceUser does not have the required roles to act as an application pool identity. You need to give the user the ability to logon as a batch job and the ability to logon as a service.
Browse to Administrative Tools -> Local Security Policy. Then choose User Rights Assignment.
You need to add ReportServiceUser to both the highlighted rights. To do this, right-click one of them and select "Properties". In the popup click on the [Add User or Group] button and add ReportServiceUser. Then repeat for the other.
Now recycle the application pool to use these new capabilities.
The obvious thing to do is to create an identical user locally on each server, same user - same password. Let's assume we created a non-administrator user called ReportServiceUser. We made the user non-administrator because we care about security.
Next, you logon to the middle tier, find the application pool that hosts your WCFService, and use ReportServiceUser as the identity.
Then you logon to the Report Server and give ReportServiceUser access to the site and the reports. This is all very similar to the procedure explained in the earlier blog entry.
Try to run the report through Report Manager from the middle tier and you may or may not succeed. But if you use your application to run the report from the WCFService you will fail. You will also see that the application pool has stopped. Open up event viewer on the middle tier and look at the System events...
First we got three warnings, then we got an error. The error tells us the services was stopped but it's the first warning that has the important error code.
Simply searching Google on the error code will normally tell you the next step to take. In this case we find that ReportServiceUser does not have the required roles to act as an application pool identity. You need to give the user the ability to logon as a batch job and the ability to logon as a service.
Browse to Administrative Tools -> Local Security Policy. Then choose User Rights Assignment.
You need to add ReportServiceUser to both the highlighted rights. To do this, right-click one of them and select "Properties". In the popup click on the [Add User or Group] button and add ReportServiceUser. Then repeat for the other.
Now recycle the application pool to use these new capabilities.
Thursday, February 25, 2016
Right-align textblock in datagrid
Let's suppose you have a datagrid with a column that you want right-aligned. Sounds easy right? Your code behind might look something like this...
Imports System.Collections.ObjectModel
Class MainWindow
Public Property Amounts As New ObservableCollection(Of Decimal) From {0}
End Class
and your XAML might look like this...
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
The results look like this, which is what we expected.
Now let's put a background on the text block by adding another setter to the element style.
<Setter Property="Background" Value="Yellow"></Setter>
Well that isn't right :-(
The problem is that the text block is only as wide as it needs to be. What if we explicitly set the width to 50px by adding yet another setter?
<Setter Property="Width" Value="50"></Setter>
Wow - so now our cell is yellow but the right-align is broken. If you look carefully you can see the text block is right aligned within the cell but the contents of the text block are left aligned. The HorizontalAlignment property controls how the text block is aligned, not how it's contents are aligned. This is consistent, but a little non-intuitive. But there isn't a HorizontalContentAlignment property on a text block :-(
It turns out there are two simple ways to fix this.
Option 1 is to set the background of the cell. If no background is set for the textblock it is transparent and the cell background bleeds through.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Yellow"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Option 2 is to set the horizontal alignment to Stretch and set the TextAlignment property to Right. So for text blocks the TextAlignment property replaces the HorizontalContentAlignment property.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="TextAlignment" Value="Right"></Setter>
<Setter Property="Background" Value="Yellow"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Imports System.Collections.ObjectModel
Class MainWindow
Public Property Amounts As New ObservableCollection(Of Decimal) From {0}
End Class
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Now let's put a background on the text block by adding another setter to the element style.
<Setter Property="Background" Value="Yellow"></Setter>
Well that isn't right :-(
The problem is that the text block is only as wide as it needs to be. What if we explicitly set the width to 50px by adding yet another setter?
<Setter Property="Width" Value="50"></Setter>
Wow - so now our cell is yellow but the right-align is broken. If you look carefully you can see the text block is right aligned within the cell but the contents of the text block are left aligned. The HorizontalAlignment property controls how the text block is aligned, not how it's contents are aligned. This is consistent, but a little non-intuitive. But there isn't a HorizontalContentAlignment property on a text block :-(
It turns out there are two simple ways to fix this.
Option 1 is to set the background of the cell. If no background is set for the textblock it is transparent and the cell background bleeds through.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Yellow"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Option 2 is to set the horizontal alignment to Stretch and set the TextAlignment property to Right. So for text blocks the TextAlignment property replaces the HorizontalContentAlignment property.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<DataGrid ItemsSource="{Binding Amounts}">
<DataGrid.Columns>
<DataGridTextColumn Header="Amounts" Binding="{Binding}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="TextAlignment" Value="Right"></Setter>
<Setter Property="Background" Value="Yellow"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
Wednesday, February 3, 2016
Adding x:Name to a user control causes a compilation error
I was developing a new user control and realized I wanted to bind to a dependency property. The XAML looks like this.
<UserControl x:Class="FileInputBox.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<Button x:Name="theButton" DockPanel.Dock="Right" Click="theButton_Click">Browse...</Button>
<TextBox x:Name="theTextBox" MinWidth="{Binding ActualWidth, ElementName=theButton}" Margin="0,0,2,0"/>
</DockPanel>
</UserControl>
<UserControl x:Class="FileInputBox.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<Button x:Name="theButton" DockPanel.Dock="Right" Click="theButton_Click">Browse...</Button>
<TextBox x:Name="theTextBox" MinWidth="{Binding ActualWidth, ElementName=theButton}" Margin="0,0,2,0"/>
</DockPanel>
</UserControl>
I want to bind the text property of the TextBox to a dependency property of the UserControl called FileName. So I gave the UserControl a name and bound to it so the XAML looks like this.
<UserControl x:Class="FileInputBox.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<DockPanel>
<Button x:Name="theButton" DockPanel.Dock="Right" Click="theButton_Click">Browse...</Button>
<TextBox x:Name="theTextBox" MinWidth="{Binding ActualWidth, ElementName=theButton}" Margin="0,0,2,0" Text="{Binding Path=FileName, ElementName=root}"/>
</DockPanel>
</UserControl>
Now I get a compilation error...
The type name 'FileInputBox' does not exist in the type 'FileInputBox.FileInputBox'
It turns out the problem is that the Namespace and the Class names are the same. All I had to do is rename the Class. The problem occurs as the compiler tries to figure out exactly what is being called "root". Seems odd to me.
I found the solution and explanation here http://stackoverflow.com/questions/3351860/why-would-adding-an-xname-attribute-to-a-user-control-cause-a-compilation-error
Wednesday, January 27, 2016
Displaying a message box while a splash screen is visible
If you use a splash screen while your WPF application is initializing you may have noticed a problem when you try to show a messagebox while the splash screen is visible. Perhaps the initialization process failed in Application_Startup.
The problem is that the splash screen is the active window and it is designed to disappear as soon as the first visual element is displayed. By default, message boxes are owned by the active window. You can see what happens. The message box is owned by the splash screen, the splash screen disappears as soon as the message box is displayed, so the message box disappears too.
For me, the problem is made worse because I have an unhandled exception handler defined for the application so any unhandled exception is handled by the same code whether it was raised while the splash screen was visible or later.
Private Sub Application_DispatcherUnhandledException(sender As Object, e As System.Windows.Threading.DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
ShowFatal(e.Exception.Message)
End Sub
The problem is that the splash screen is the active window and it is designed to disappear as soon as the first visual element is displayed. By default, message boxes are owned by the active window. You can see what happens. The message box is owned by the splash screen, the splash screen disappears as soon as the message box is displayed, so the message box disappears too.
For me, the problem is made worse because I have an unhandled exception handler defined for the application so any unhandled exception is handled by the same code whether it was raised while the splash screen was visible or later.
Private Sub Application_DispatcherUnhandledException(sender As Object, e As System.Windows.Threading.DispatcherUnhandledExceptionEventArgs) Handles Me.DispatcherUnhandledException
ShowFatal(e.Exception.Message)
End Sub
The ShowFatal method simply displays a MessageBox. When the exception originates while the splash screen is displayed the message box disappears before the user can read it. They launch the application, something flashes, and the application exits. Not cool.
The trick is to know that the MainWindow does not yet exist during initialization. In fact, it is the creation of the main window that makes the splash screen go away. So if the main window is not yet created we display a dummy message box that attaches to the splash screen and causes the splash screen to go away. Then we display the proper message box.
Public Shared Sub ShowFatal(sMsg As String)
' If the main window is not yet displayed then show a dummy messagebox
If Application.Current.MainWindow Is Nothing Then System.Windows.MessageBox.Show("")
System.Windows.MessageBox.Show(sMsg, "Fatal Error", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Stop)
Application.Current.Shutdown(1)
End Sub
Subscribe to:
Posts (Atom)









