Silverlight MVVM ListBoxItem IsSelected - silverlight

I have a collection of ViewModels bound to a ListBox. I am trying to bind the IsSelected properties of each together. In WPF it works by setting the style:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
This does not work in Silverlight. How can I accomplish this?

In Silverlight, you are not able to create "global" styles, that is, styles that modify all controls of a certain type. Your style needs a key, and your control needs to reference it.
Also, TargetType simply needs the control type name. Silverlight does not support the x:Type extension.
ib.

Here's how i do it:
<ListBox.ItemTemplate>
<DataTemplate>
...
<CheckBox VerticalAlignment="Top" HorizontalAlignment="Left"
x:Name="CheckBox1" IsChecked="True" Grid.Row="0">
<inf:BindingHelper.Binding>
<inf:BindingProperties TargetProperty="Visibility" SourceProperty="IsSelected"
Converter="{StaticResource VisibilityConverter}"
RelativeSourceAncestorType="ListBoxItem" />
</inf:BindingHelper.Binding>
</CheckBox>
...
</DataTemplate>
</ListBox.ItemTemplate>
You need to do relative binding, which doesn't exist in Silverlight unfortunately...
BindingHelper is a helper class which overcomes this limitation (search for "relative binding in silverlight" to find it).

Related

Styles on DataTemplates

I have an issue with using Styles on DataTemplates in WPF ... It appears to suck. Suppose that you define a DataTemplate:
<DataTemplate DataType="{x:Type local:DataSource}">
<TextBox Style="{StaticResource TextBoxStyle}" Text="{Binding Path=myData}" />
</DataTemplate>
Is there now any way to dynamically style this element? (eg. change the background color in some parts of the application) My problems are:
if you set a style in some parent-control, it gets ignored since there is already a style in the datatemplate
if you set a property on a parent-control, it doesn't get inherited since styles have precedence over property inheritence
Does anyone see a way to do this?
you can try it using DynamicResource
<DataTemplate DataType="{x:Type local:DataSource}">
<TextBox Style="{DynamicResource TextBoxStyle}" Text="{Binding Path=myData}" />
</DataTemplate>
and then later if you need to change the style in any other control. you can just declare same resource with same key for that control. Suppose you are using it in ListBox.
<ListBox>
<ListBox.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<!--define changed style.-->
</Style>
</ListBox.Resources>
</ListBox>
new style will apply over there.

Binding ListBoxItem's IsEnabled property with ItemTemplate set

I have the following ListBox:
<ListBox ScrollViewer.VerticalScrollBarVisibility="Disabled"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
SelectionChanged="ListBoxContainerSelectionChanged"
ItemsSource="{Binding Movies}"
ItemContainerStyle="{StaticResource HeaderListBoxItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<Controls:MoviesItemControl Header="{Binding Title}"
Detail="{Binding FormattedDescription}"
Rating="{Binding Rating}"
Opacity="{Binding IsSuppressed, Converter={StaticResource DimIfTrueConverter}}"
IsEnabled="{Binding IsSuppressed, Converter={StaticResource InverseBooleanConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'm trying to set the Disabled state of ListBoxItems that are 'Suppressed' (Movies with no description found). I have a property which I am able to bind to my individual control, but I want them to not be selectable in the actual list. (And use the disabled state included in my ItemsContainerStyle)
I have seen a few implementations on SO using Trigger, but that does not seem to be available in WP7, and I would prefer to not have to create a different style for each control so that they bind properly.
Any ideas?
See the following question: WP7 - Databind ListboxItem's IsEnabled Property
Which in turn links to this: Better SetterValueBindingHelper makes Silverlight Setters better-er!
I tried out SetterValueBindingHelper by David Anson for this specific scenario and it worked great. All you have to do is to add SetterValueBindingHelper.cs to your project and then you can bind IsEnabled in the setter like this
<Style x:Key="HeaderListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<delay:SetterValueBindingHelper Property="IsEnabled"
Binding="{Binding IsSuppressed}"/>
</Setter.Value>
</Setter>
</Style>

WPF - Freezable in a style of a button not inheriting DataContext

I am modeling an attached command pattern after the AttachedCommandBehavior library here. My button looks like this:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="vms:Attached.Behaviors">
<Setter.Value>
<vms:Behaviors>
<vms:Behavior Event="Click"
Command="{Binding ClickCommand}" />
</vms:Behaviors>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Everything works great, but when the setter on the Behavior is executed, the Command is null.
Behavior is a Freezable, and Behaviors is a FreezableCollection<Behavior>. It just doesn't seem to be inheriting the DataContext from the Button.
On the other hand, this works correctly:
<Button>
<vms:Attached.Behaviors>
<vms:Behavior Event="Click" Command="{Binding ClickCommand}" />
</vms:Attached.Behaviors>
</Button>
Unfortunately I can't do it this way, because I need to target generated ListViewItems using ItemContainerStyle.
Is there some way to get the DataContext in the Style?
The Attached Command Behavior library is the germ of the idea that became Blend Behaviors. The Blend Behaviors are much more powerful and standardized and so I recommend you switch to using them. But whether you are using Attached Command Behavior or Blend Behaviors, the problem is essential the same: they don't work as expected when trying to set them using a style. I've solved this problem for Blend Behaviors with full support for binding in this StackOverflow answer:
How to add a Blend Behavior in a Style Setter
Without testing it, I guess you have to move the ACB behavior to a resource marked with x:Shared="False" in order to get the binding to work.
I had the same problem, and using RelativeSource did the trick. I'll show you my before and after code...
Before: (This DIDN'T work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding OpenMenuItem}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
After: (This DOES work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding Path=DataContext.OpenMenuItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
You'll obviously have to tweak the parameters of the Relative Source to your specific situation. It seems that, for whatever reason, attached properties don't inherit the data context, so you have to tell if how to.

WPF: Dynamically change ListBox's ItemTemplate based on ListBox Items Size

I need to change the DataTemplate of my ListBox, based on the ListBox items count. I have come up with the following XAML:
<Window.Resources>
<DataTemplate x:Key="DefaultTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Text}"/>
<TextBlock Text="default template" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="OtherTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Text}"/>
<TextBlock Text="other template" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListBox Name="listBox1" ItemsSource="{Binding Path=Items}">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count}" Value="1">
<Setter Property="ItemTemplate" Value="{StaticResource OtherTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
With the above XAML, once I added two or more items to the bound list, the data template changed as expected (from other to default). However, if I remove the first item in the list with more than two items, the entire listbox just becomes empty (I verified that the bound list is non-empty). Removing the second item in a two items list works fine though (i.e. template changed from default to other).
Any ideas why this is happening? Or perhaps I went about the wrong way to solve this problem?
you could use data triggers, or you could use a DataTemplateSelector Here is an article that shows the basics. and here is the MSDN on applying it to the items control (also, a listbox)
I can't speak for the exact problem or the cause, but it is because a DataTrigger is setting a template when the count is 1 and only 1.
You can do 1 of 3 things to solve this problem, but only 1 I would recommend.
a) Implement your own DataTrigger by deriving from System.Windows.TriggerBase
b) Use an implementation of System.Windows.Data.IValueConverter that will convert from ItemsControl.Items.Count into a DataTemplate. Retrieve the templates by placing an element in scope of your resources as Binding.ConverterParameter, casting them to FrameWorkElement and call FrameWorkElement.FindResource().
C) This is my recommendation, write your own DataTemplateSelector to do the grunt work. This mechanism is specifically targeted at the functionality you with you achieve. I recently wrote one that will pick a DataTemplate based on the type of the source object without requiring a DataTemplate with no x:Key set. Using Properties on the template selector, you can pass DataTemplates into the DataTemplateSelector using XAML, removing that FindResource code 'todo' list.

Silverlight ~ MVVM ~ Dynamic setting of Style property based on model value

I have a class called Question that represents a question and it's answer. I have an application that renders an ObservableCollection of Question objects. Each Question is rendered as a StackPanel that contains a TextBlock for the question verbiage, and a TextBox for the user to enter in an answer. The questions are rendered using an ItemsControl, and I have initially set the Style of the Questions's StackPanel using a StaticResource key called 'IncorrectQuestion' (defined in UserControl.Resources section of the page). In the UserControl.Resources section, I've also defined a key calld 'CorrectQuestion' which I need to somehow apply to the Question's StackPanel when the user correctly answers the question. My problem is I'm not sure how to dynamically change the Style of the StackPanel, specifically within the constraints of a ViewModel class (i.e. I don't want to put any style selection code in the View's code-behind). My Question class has an IsCorrect property which is accurately being set when the correction is answered. I'd like to somehow reflect the IsCorrect value in the form of a Style selection. How do I do that?
Using a value converter is a solution.
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<local:BoolToStyleConverter x:Key="Correctness">
<local:BoolToStyleConverter.FalseValue>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Salmon" />
</Style>
</local:BoolToStyleConverter.FalseValue>
<local:BoolToStyleConverter.TrueValue>
<Style TargetType="TextBox">
<Setter Property="Background" Value="AliceBlue" />
</Style>
</local:BoolToStyleConverter.TrueValue>
</local:BoolToStyleConverter>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Question}" />
<TextBox x:Name="Answer" Text="{Binding Answer, Mode=TwoWay}"
Style="{Binding IsCorrect, Converter={StaticResource Correctness}}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
You can find the basis for the BoolToStyleConverter is based on this blog post.
Created as:-
public class BoolToStyleConverter : BoolToValueConverter<Style> { }

Resources