Monday, October 6, 2008

From Silverlight 2 Beta 2 To RC0

On September 25th Microsoft released Silverlight 2 RC0 (for developers only). At the end of September I migrated my SL 2 Beta 2 application to new version. First my project didn't compile, later I had some run-time errors ... Finally I have working SL2 RC0 application. 

Some changes I have noticied when I did migration:

  1. generic.xaml was moved underneath the themes folder under control library project

  2. IsEnabled property was added to Control class

  3. RoutedEventArgs.Source  was changed to OriginalSource

  4. MouseEventArgs.Handled was moved  to MouseButtonEventArgs (SL team: In Beta 2 timeframe, we heard a lot of feedback that a lot of events are marked as Handled internally. This had caused a lot of troubles. Generally speaking, you should only mark an event as handled when other event handlers expects you to do so. But it's unlikely that is a requirement, especially for events using MouseEventArgs, such as MouseEnter/Leave/Move. Leave this property will cause more confusions than the problems it solves. So we decide to remove it from some events, while keep it on the other, when necessary. More information: http://silverlight.net/forums/t/30901.aspx)

  5. CancellingEdit and CommittingEdit events of DataGrid control were removed (SL team: The event was there in Beta 2, but it didn't work correctly so it was removed for RTM.  We are hoping to bring it back in SL 3. More information: http://silverlight.net/forums/t/30533.aspx)

  6. Some changes in styles and templates (When I compiled my project, I got warnings and errors:  " The property '_UnknownContent' does not exist on the type 'ResourceDictionary' in the XML namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation' " and " The type 'List`1' is inside a ResourceDictionary and does not have a key". I had errors, because I had used skins like here.  I changed some namespaces and styles to new format, but I still had build errors.  Finally, I  created new project, copied files from old project and I got warnings  - not errors!  Amazing, but it helped me!  I have ignored warnings and my project has worked correctly! )

  7. DataGridCellsPresenter, DataGridColumnHeader, DataGridColumnHeadersPresenter, DataGridDetailsPresenter, DataGridRowHeader, DataGridRowsPresenter classes were moved to namespace System.Windows.Controls.Primitives

  8. ContentPresenter derives from FrameworkElement instead of Control

  9. Some changes in animations:  You get exception " Operation is not valid on an active Animation or Storyboard. Root Storyboard must be stopped first."  when you change  Storyboard.TargetNameProperty  on an active Storyboard. You should stop Storyboard before changing the property.

  10. Some changes in ContentControl: Now content doesn't stretch automatically to borders of the control. I set VerticalContentAlignment and HorizontalContentAlignment properties to Stretch value.

  11. IsTabStop default value was changed to true.

  12. Some changes in databinding:  With Beta2 you can make two way binding without specyfing Path. Now you get exception "Two way binding requires Path". 

  13. Some changes in wrapping text for TextBlock:  Parts of the text with more than one word are hidden. With Beta2 it works correctly, it seems to be a bug.

For more information about changes  you see  Breaking Changes Since Beta 2 document, RC0 documentation or Silverlight forums.

Thursday, September 18, 2008

Silverlight Tip 8#: Measurement and layout pass in Silverlight run asynchronously!

If you try to use ActualWidth and ActualHeight properties, they sometimes are equal zero! Why? How fix it? You should read very good Tamir Khason's post about it.

Wednesday, September 17, 2008

Silverlight Tip 7#: Handling ListBox MouseLeftButtonDown event with VisualTreeHelper

In Silverlight 2 beta 2 ListBoxItem (and ListBox) doesn’t trigger MouseLeftButtonDown event anymore! You can add handler for this event in XAML of ItemContainerStyle. I wrote post about it 9 days ago.

If you wouldn't specify the event handler in XAML, you have a problem: you can't modify code of ListBoxItem class. But you can use visual tree after loading ListBox.

You can examine the visual tree structure with VisualTreeHelper class. I have written recursive method to get all visual elements which parents are objects of a specific type:

private static List<UIElement> children = new List<UIElement>();

...

private static void GetChildrenWithParentRec(UIElement parent, Type targetType)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
if (count > 0)
{
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (VisualTreeHelper.GetParent(child).GetType() == targetType)
{
children.Add(child);
}
GetChildrenWithParentRec(child, targetType);
}
}
}

I have used this method to obtain visuals which are children of ListBoxItem elements of ListBox control. Before you show ListBox, you can use code:

//GetChildrenWithParent uses GetChildrenWithParentRec
List<UIElement> children = ChildrenHelper.GetChildrenWithParent(listBox, typeof(ListBoxItem));

if (children.Count > 0){
foreach (UIElement el in children)
el.MouseLeftButtonDown += new MouseButtonEventHandler(ListBoxItem_MouseLeftButtonDown);
}

It works for me.

Friday, September 12, 2008

Silverlight Tip 6#: How to start editing cell of DataGrid by pressing text key

When you press text key (f.e 'a') on selected cell of MS Excel sheet, you automatically start write to the cell. Why don't implement this behavior with Silverlight DataGrid control? I have written some code to implement it:

void dataGrid_KeyDown(object sender, KeyEventArgs e)
{
//editing: bool flag which you set in BeginEdit, CancelEdit, CommitEdit event handlers
if (!editing)
{
//IsTextKey: custom function to check if pressed key is a text key (f.e letter, digit)
if (IsTextKey(e))
{
dataGrid.BeginEdit(e);
}
}
}


void dataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
if (e.Column.DisplayIndex == 0)
{
TextBox t = e.EditingElement as TextBox;

t.Focus();

KeyEventArgs args = e.EditingEventArgs as KeyEventArgs;

if (args != null)
{
if (IsTextKey(args))
{
//GetStrFromKey: custom function to get char from pressed key
//There isn't KeyPress event and KeyChar property of KeyPressEventArgs
t.Text = GetStrFromKey(args);
t.Select(t.Text.Length, t.Text.Length);
}
}
}
}

Thursday, September 11, 2008

Silverlight Tip 5#: DataGrid PreparingCellForEdit event

Recently I've used DataGridTemplateColumn in DataGrid control and I've implemented selecting text in cell before editing. This is standard behavior of predefined DataGridTextColumn, but using DataGridTemplateColumn you must implement it yourself (sample code below).

void dataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
if (e.Column.DisplayIndex == 0)
{
TextBox t = e.EditingElement as TextBox;

int length = t.Text.Length;

t.Focus();

KeyEventArgs args = e.EditingEventArgs as KeyEventArgs;

if (args != null)
{
if (args.Key == Key.F2)
t.Select(length, length);
}
else
{
t.Select(0, length);
}
}
}

Wednesday, September 10, 2008

Silverlight Tip 4#: How to detect user clicks outside of your control

In order to do this you may find application root and add mouse click event handler to it:


private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
...

//root
FrameworkElement root = Application.Current.RootVisual as FrameworkElement;
if (root != null)
{
root.MouseLeftButtonDown += new MouseButtonEventHandler(root_MouseLeftButtonDown);
}
}

and check when user clicks:


void root_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
double x = e.GetPosition(this).X;
double y = e.GetPosition(this).Y;

if (x < 0 || x > this.ActualWidth || y < 0 || y > this.ActualHeight)
{
//to do
}
}

But this is not ideal solution, because it doesn't work when you click outside your control and on the control which handles MouseLeftButtonDown event (for example Button). For more information see: http://silverlight.net/forums/p/23071/82288.aspx

Tuesday, September 9, 2008

Silverlight Tip 3#: ScrollIntoView

When you programmatically set selected item in a ListBox control and the item is unvisible, the Listbox doesn't automatically scroll to make it visible. But you can use ScrollIntoView method.


this.DropDownListBox.SelectedIndex = index;
this.DropDownListBox.UpdateLayout();
this.DropDownListBox.ScrollIntoView(this.DropDownListBox.SelectedItem);