Showing posts with label silverlight. Show all posts
Showing posts with label silverlight. Show all posts

30 November 2011

Binding, and Templates, and ContentControls, oh my!

I'm working in Silverlight 4, and have been trying to get a particular binding scenario to work right. It seems to be the case that try to satisfy any one or two requirements is easy enough, but when things start piling up, Silverlight can get a bit tricky.

My requirements:

  1. I'm creating a UserControl
  2. The UserControl itself needs to work inside a template.
  3. I want to define properties on the UserControl.
  4. I want a ContentControl in the UserControl
  5. I want to define a ContentTemplate on the content control
  6. And lastly, I want this inner template to bind to properties of the UserControl

The problems:

  • I can't name the UserControl and use ElementName inside the template, because the UserControl itself is also going to be used in a template, and ElementName doesn't always behave well in that situation.
  • I can't programatically set the DataContext or Bindings within the template, because Silverlight doesn't give programatic access to a template.
  • Setting the DataContext of the UserControl or the ContentControl to the UserControl itself doesn't help, because the DataContext of a template is actually the Content property of the ContentControl.
  • I can't set the Content of the ContentControl to the UserControl itself, because this makes Silverlight hang (presumably due to an infinite recursive loop).

So here's what I've got working. It's disgusting, but seems to work.

<UserControl>
...
<ContentControl x:Name="content"/>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Self.SomeValue}" />
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>
...
</UserControl>

And the code...

public partial class MyControl : UserControl
{
public class Indirection
{
public ValueEditor Self { get; set; }
}

public MyControl()
{
InitializeComponent();
this.content.Content = new Indirection() { Self = this };
}

public static readonly DependencyProperty SomeValueProperty = ...
public string SomeValue { ... }

}

So the idea is to set the ContentControl.Content to a proxy object that is not the control itself, but with a reference to the control, which can then be accessed in the path of the binding.

If you know a nicer way to do this, please leave a comment!

24 November 2011

INotifyPropertyChanged code snippet

I've never really worried about getting into Visual Studio code snippets. But I've been using INotifyPropertyChanged a lot recently, so it got me looking at them again.

Found this cool snippet by Brian Schroer for filling out properties. Here's my slightly customized version of it to suit personal taste. Thanks Brian!

Steps to intall:

  1. Save the following to NotifyProperty.snippet on your desktop1
  2. In Visual Studio go to Tools / Code Snippets Manager
  3. Press Import and locate the file
  4. Import the file into My Code Snippets

Steps to use:

  1. In the code editor, type propn then press TAB twice.
  2. Immediately type the name of the property. Press tab.
  3. Then type the .Net Type name of the property.

The code:



<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>propn</Title>
<Shortcut>propn</Shortcut>
<Description>Code snippet for property and backing field in class implementing INotifyPropertyChanged</Description>
<Author>Brian Schroer</Author>
<!-- http://geekswithblogs.net/brians/archive/2010/07/27/inotifypropertychanged-with-less-typing-using-a-code-snippet.aspx -->
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>string</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[/// <summary>
/// Gets or sets the $property$
/// </summary>
public $type$ $property$
{
get { return _$property$;}
set
{
if (value != _$property$)
{
_$property$ = value;
OnPropertyChanged("$property$");
}
}
}
private $type$ _$property$;
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

15 November 2011

Binding UserControl properties in Silverlight

It seems to me that the logical thing to do in Silverlight when building some sort of composite control is to create a UserControl, expose some properties, add some controls to the user control, and bind them to those properties.

For example, a custom CoffeeMug control might want to expose MugColor and PercentFull properties. However, it's taken me quite a while to figure out how to get this to work properly.

The problem is in selecting the binding source. WCF offers the FindAncestors binding helper, which can be used to locate the user control relative to the control being bound, but Silverlight offers no counterpart.

So initially I had been using ElementName:

<UserControl x:Name="thisControl" ... >
...
<Rectangle x:Name="mugShape"
Background="{Binding MugColor, ElementName=thisControl}" />
...
</UserControl>

This works pretty well in simple circumstances, but it wasn't working for me in DataTemplate scenarios, such as in the DataGrid. I was having similar problems with using custom controls in custom ComboBox and ListBox data templates.

Well, apparently its a known shortcoming of Silverlight. When the data template is asked to make a concrete instance, it no longer carries the necessary context information to resolve element names directly. It can however be overcome by manually specifying the binding in code.


public CoffeeMug()
(
this.Loaded += new new RoutedEventHandler(Control_Loaded);
}
void Control_Loaded(object sender, RoutedEventArgs e)
{
Binding binding = new Binding("MugColor");
binding.Source = this;
// remember to set binding.Mode = TwoWay if applicable
this.mugShape.SetBinding(Shape.BackgroundProperty, binding);
}

This guy has also hacked together a behavior to make everything much nicer in an MVVM environment as well, but I don't think I'd go to that much effort personally.

26 October 2011

Enum.GetValues in Silverlight

It seems Enum.GetValues is not defined in the Silverlight client profile.
The owl says it best.
Oh well, a hacking we shall go.