WPF book has been released and Silverlight book will be available on 28th of August
Friday-Evening Fun with Silverlight’s Animation Easing Functions
Visual Studio has been released, Silverlight 4 has been released and the books are on their way…
Silverlight 4 Release Candidate is here
Visual Studio 2010 RC and Silverlight 4 Beta
The DataGrid and the “Input string is not in a correct format” message in Silverlight
public class Person : INotifyPropertyChanged
{
private int? _age;
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
Changed("FirstName");
}
}
public int? Age
{
get { return _age; }
set
{
_age = value;
Changed("Age");
}
}
private void Changed(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now let’s fill up a List with some Test-Persons and add that list to the DataContext of your page:
public MainPage()
{
InitializeComponent();
this.DataContext = new List {
new Person{ FirstName="Thomas",Age=29},
new Person{ FirstName="Julia",Age=27},
new Person{ FirstName="Ben",Age=1},
};
}
No in the XAML-File of your MainPage a DataGrid could be defined like below. Notice the ItemsSource-Property that is bound to the DataContext:
<my:DataGrid ItemsSource="{Binding}"
AutoGenerateColumns="False">
<my:DataGrid.Columns>
<my:DataGridTextColumn Header="FirstName"
Binding="{Binding FirstName}"/>
<my:DataGridTextColumn Header="Age"
Binding="{Binding Age}"/>
</my:DataGrid.Columns>
</my:DataGrid>
When the User now enters a string as Age, the FormatException is raised before the Property is updated and the Message of the FormatException is displayed in the DataGrid:
Now the question is, where to change this string. The DataGrid has a BindingValidationError-Event, but there you have only read access to the FormatException and the Errormessage. In the Property itself you can’t do anything, because the Exception is thrown before the Age-Property is set.
The solution is to define a Property special for Display in the Person-class. Normally you would create a ViewModel that encapsulates the Person-class and contains the additional property. In my case I implement the Property directly in the Person-class. I call it AgeDisplay-Property. Inside that property you can then throw your own FormatException with your special text. My Person-class now looks like this:
public class Person : INotifyPropertyChanged
{
private int? _age;
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
Changed("FirstName");
}
}
public int? Age
{
get { return _age; }
set
{
_age = value;
Changed("Age");
Changed("AgeDisplay");
}
}
public string AgeDisplay
{
get { return Age.ToString(); ; }
set
{
if (value == null)
{
Age = null;
return;
}
int result;
if (int.TryParse(value, out result))
{
Age = result;
}
else
{
throw new FormatException("Age must be a number. "
+ " Characters are not allowed.");
}
}
}
private void Changed(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now you simple bind the DataGrid’s Column to AgeDisplay instead of Age:
<my:DataGrid ItemsSource="{Binding}"
AutoGenerateColumns="False">
<my:DataGrid.Columns>
<my:DataGridTextColumn Header="FirstName"
Binding="{Binding FirstName}"/>
<my:DataGridTextColumn Header="Age"
Binding="{Binding AgeDisplay}"/>
</my:DataGrid.Columns>
</my:DataGrid>
When the user now enters a string into the Age-Column, the text of the FormatException thrown in the AgeDisplay-Property is displayed:
Great news for Silverlight on Linux
Silverlight 4 – How to focus a TextBox that is contained in your Custom Control on Startup
Focusing a TextBox that’s inside a Custom Control isn’t so easy at startup of your application. Let me explain the problem that is also discussed on http://forums.silverlight.net/forums/t/151235.aspx. Imagine you’ve created a custom control that has a TextBox as Part-element. The Style that sets the Template would look like this:
<Style TargetType="local:SimpleControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:SimpleControl">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_Text" Margin="10"></TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You get the TextBox from the Template in the OnApplyTemplate-Method
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_partTextBox = this.GetTemplateChild("PART_Text") as TextBox;
}
Now imaginge you want to focus your SimpleControl in the constructor of your MainPage like below:
public MainPage()
{
InitializeComponent();
HtmlPage.Plugin.Focus();
yourControl.Focus();
}
The Problem that occurs is that the OnApplyTemplate-Method is called after the Focus-Method. So you don’t have the TextBox defined in your Template. The Solution is very easy. Just hide the Focus-Method from the base-class and set a flag. But keep in mind that this won’t work if you store your SimpleControl in a variable of type Control, cause then no polymorphic call is made to the Focus-Method defined in SimpleControl. Here a sample implementation:
public class SimpleControl : Control
{
private bool _applyFocusToPartTextBox;
private TextBox _partTextBox;
public SimpleControl()
{
this.DefaultStyleKey = typeof(SimpleControl);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_partTextBox = this.GetTemplateChild("PART_Text") as TextBox;
if (_partTextBox != null)
{
if (_applyFocusToPartTextBox)
_partTextBox.Focus();
}
_applyFocusToPartTextBox = false;
}
public new bool Focus()
{
if (_partTextBox == null)
{
_applyFocusToPartTextBox = true;
return true;
}
return base.Focus();
}
}
How to print a List<string> in Silverlight 4 Beta over multiple pages?!
public partial class MainPage : UserControl
{
private List _list;
private const double ROWHEIGHT = 20;
private const double PAGEMARGIN = 30;
public MainPage()
{
InitializeComponent();
_list = new List();
for (int i = 1; i < 101; i++)
{
_list.Add(i + " thanks to Thomas for this printing sample");
_list.Add("Visit http://www.thomasclaudiushuber.com");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_currentIndex = 0;
var pd = new PrintDocument();
pd.DocumentName = "AListFromThomas";
pd.PrintPage += pd_PrintPage;
pd.Print();
}
private int _currentIndex;
private double _currentTop;
private double _availableSpace;
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
var pageRoot = new Canvas();
e.PageVisual = pageRoot;
_currentTop = PAGEMARGIN;
_availableSpace = e.PrintableArea.Height - PAGEMARGIN*2;
while (_currentIndex < _list.Count)
{
var txt = new TextBlock { Text = _list[_currentIndex] };
if (ROWHEIGHT > _availableSpace)
{
e.HasMorePages = true;
break;
}
txt.SetValue(Canvas.TopProperty, _currentTop);
txt.SetValue(Canvas.LeftProperty, PAGEMARGIN);
pageRoot.Children.Add(txt);
_currentTop += ROWHEIGHT;
_availableSpace -= ROWHEIGHT;
_currentIndex++;
}
}
}
When the Button_Click-Eventhandler is executed, the List gets printed over 5 pages. You can easily print it to PDFCreator or XPS Printer to test it. The output looks like this:
Download the source here and enjoy. Give me feedback by entering a comment to this blogentry or via email on www.thomasclaudiushuber.com/blog.