Changing DataTemplate with Property Binding in Silverlight - silverlight-4.0

I'm trying to implement this using Eugene Akinshin's code from here: http://forums.silverlight.net/t/237947.aspx/1
It seems like a really nice way to bind to already-existing properties and means the configuration can all be defined in XAML.
However, I can't get it to work.
I'm defining the templates to use like this:
<Converters:TemplateSelectorConverter x:Key="templateConverter">
<Converters:TemplateSelectorCase TemplateReference="Minimised" Template="{StaticResource SmallOrders}"/>
<Converters:TemplateSelectorCase TemplateReference="Restored" Template="{StaticResource MediumOrders}"/>
<Converters:TemplateSelectorCase TemplateReference="Maximised" Template="{StaticResource LargeOrders}"/>
</Converters:TemplateSelectorConverter>
and then setting the item template of my ListBox like this:
ItemTemplate="{Binding CurrentState, Converter={StaticResource templateConverter}}"
CurrentState is a string of either 'Minimised', 'Maximised' or 'Restored' (I've edited the linked example to have a string as the key rather than an int) and is set to 'Minimised' by default, but all I get is a list of Cannot create [my object type] in my ListBox.
The templates definitely work as I can expose the View in the ViewModel and set the DataTemplate in code, and there are visual states that rely on the same CurrentState property which work, so I know the View can access the property correctly. Unfortunately, if I breakpoint the Convert() method in the converter, it never gets hit.
All suggestions greatly appreciated!

Not sure if you have figured this out or not but placement of the Converter definition in the Resource in relationship to the DataTemplate will be the difference between it working and not working.
The Converter needs to be placed prior to the Data Template.

Related

How to bind the x:Uid of a TextBlock in XAML?

I am making a menu that can display many languages so I want to set the Uid of these TextBlock on each item, how can I set the Uid of a TextBlock in XAML through data binding. Because when I set x:Uid="{Binding Label}", the IDE shows that is an error. Please help me, thank you!
The binding is used Text="{Binding Label}", when you want value to be fetched from viewmodel. Drawback you will have to handle according to language the conversion of text.
x:Uid is supposed to be specified inside Strings->en_US folder. You create a resource file by default Resources.resw file inside it you add Name lets say Sample.Text and its Value as Your Text.
Inside your XAMl you are supposed to use x:Uid="Sample".
Now as per your requirement when you add more language using multilingual toolkit it would handle conversion for required language and on uploading to store it would select the particular language from app package and hence user views the app in the particular language they have selected on their device.
If you want to use the translation from strings folders you can instead of binding to the x:Uid value, bind the Text variable to a text key and use a value converter and find the translated text in code behind.
So the Xaml will look something like:
<TextBlock Text="{Binding Label, Converter={StaticResource TranslatorConverter}}"/>
Then you need the value converter:
public class TranslatorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
return resourceLoader.GetString(((String)value)); ;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The "Label" in your Resources.resw file shall then simply be without the ".text" part that you use when using the x:Uid binding.
However if the multilingual toolkit as suggested is easier or not I donĀ“t know. I have not used the multilingual toolikt.

XAML bind to Collection[index].ObjectProperty

I have an observable collection of objects in my VM. I want to bind to a property of a specific item in the list in a text block, something like this:
Binding="{MyVMCollection[0].Description}"
But this syntax does not work. Is it possible to do what I am after and if so, how?
Thanks!
You're missing the Binding keyword and I think you also need to use Path.
Binding="{Binding Path=MyVMCollection[0].Description}"
The type of the object needs to be a type where an array index would normally work for this to work. I'm not sure the exact constraints but use Type[] if in doubt.
eg. If it is some weird enumerable type like IOrderedEnumerable<T> (or some wierd LINQy type) then something like {Binding List[0]} won't work.

Populate IEnumerable through XAML neatly?

I am trying to populate a dependency property from XAML. The dependency property is an IEnumerable<KeyAction> where KeyAction is a complex type.
<loc:MyType.KeyActions>
<loc:KeyAction Action="Show" Key="Space" Modifiers="LeftCtrl" />
<loc:KeyAction Action="Hide" Key="Escape" />
</loc:MyType.KeyActions>
Now, this causes the KeyAction property to be 'declared twice' because XAML interprets each item as a candidate for the property, instead of items in a list.
For this to work, it would need to look something like:
<loc:CompletionPopupView.KeyActions>
<sys:List`KeyAction>
<loc:KeyAction Action="Show" Key="Space" Modifiers="LeftCtrl" />
<loc:KeyAction Action="Hide" Key="Escape" />
</sys:List`KeyAction>
</loc:CompletionPopupView.KeyActions>
And I'd need to add namespaces, and the generic syntax is probably even more terrible, if even doable. Is there any way to make the first example work?
There are two different ways you can make this work. One looks exactly like your first example, but requires changes to your class and doesn't operate quite the way you're asking for (which might or might not be a problem to you); the other acts exactly like you're asking but is a little more verbose. You can decide which one is better for you.
Option 1: Adding to a collection
XAML has magic shorthand syntax for initializing collections, using the exact syntax you show in your first example. However, it only works if the property type implements IList. (Yes, that's really the non-generic IList -- not normally a big deal though, all the generic collections that ship with .NET implement both IList<T> and IList.)
So you could do your first example, but only if your KeyActions property was declared as a type that implements IList. For example, you could change your property to:
public ObservableCollection<KeyAction> KeyActions { get {...} }
And then just put multiple child elements inside your property, and it would add them to the collection:
<loc:MyType.KeyActions>
<loc:KeyAction Action="Show" Key="Space" Modifiers="LeftCtrl" />
<loc:KeyAction Action="Hide" Key="Escape" />
</loc:MyType.KeyActions>
This isn't quite like what you asked for though, because the XAML doesn't create a new collection -- it adds to the existing collection. So if you choose this option, your class needs to instantiate the collection in its constructor (KeyActions = new ObservableCollection<KeyAction>();), so that you don't get a null reference exception when you start trying to Add elements to it.
Option 2: Creating a new collection
If you do need to create a new collection and assign it to your property, that's doable too. Unfortunately, XAML2006 (the flavor still used by WPF) only supports generics on the root element of the entire document -- so you can't instantiate a List<T> and assign it to a property.
But that's okay; you can use the same workaround that WPF does. Just create your own non-generic class that descends from a generic list.
public class KeyActionCollection : ObservableCollection<KeyAction> {}
Then you can instantiate it in XAML:
<loc:CompletionPopupView.KeyActions>
<loc:KeyActionCollection>
<loc:KeyAction Action="Show" Key="Space" Modifiers="LeftCtrl" />
<loc:KeyAction Action="Hide" Key="Escape" />
</loc:KeyActionCollection>
</loc:CompletionPopupView.KeyActions>
If you choose this option, you can keep your property declared as IEnumerable<KeyAction> if you wish. The custom list is only necessary to populate the list from XAML.
You could even combine both approaches: make the property read/write and of type KeyActionCollection, instantiate it in your constructor, and then XAML can choose whether to add to the existing collection using the shorthand syntax, or create a new collection; and runtime code could make the same choice.

Reusing property editors for Blend 4

I have a custom silverlight control, which exposes a property with DataGridLength type. Now I want that property to have the same editor as a common DataGridColumn's Width property, with the combobox and everything, like this:
instead, I only get a simple TextBox, with "Auto" written in, with no way to set to SizeToCells and so on.
I assume I need a DesignTime attribute, but none of the ones I found in ComponentModel namespace even came close...
I guess you just have to create an Enum with the all the values autorized (Pixels, SizeToCells, etc ...), you that Enum as the type of your property DataGridLength and then, in the code of your control, take the corresponding action regarding the value sent.

Silverlight 4 Binding based on what is returned from a converter

I think the easiest way to explain this is by example.
I have a Datagrid with a data context of a list of People Objects:
People{
string Name;
int AstroSignCode;
}
I am using a code to store the astro sign because the values will be persisted in a database. I cant just use the astrology Object.
Then I have a Text Column which binds to this object and uses a converter which returns an AstrologySign Object from a static list of signs based on a cross reference between AstrologySign.ID and People.AstroSignCode:
AstrologySign{
string Name;
DateTime StartDate;
DateTime EndDate;
int ID;
}
So my converter is returning an Object instead of something displayable.
How do I bind the Column to the Member of the object returned from the converter?
my Xaml so far for the column is this:
I think that I might need to use a DataGridTemplateColumn but I'm not sure anymore.
I answered my own question. it took some time to figure this out because to me, it was not obvious.
I did infact need to use a DataGridTemplateColumn instead of a DataGridTextColumn.
<sdk:DataGridTemplateColumn Header="Astro Sign">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding AstroSign, Converter={StaticResource AstrologySignConverter}}" Text="{Binding Name}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
What is happening here is the data context of the DataGrid is a list of People. Each Person has an astrosigncode which is an integer. This gets mapped by the AstrologySignConverter to an AstrologySign object which Contains the name. so I make a datatemplate and use the TextBlock control and i set it's context to a binding using the converter then i bind to the element of the object that the converter returns.
It's simple once you see that you can do it. Thanks anybody who tried to solve this!