ListBox of expanders not acting like radiobuttons - wpf

I have created a listbox of expanders like this question: Expander Auto Open/Close
The solution works with content in the expanders when the listbox is the only item on the window.
But when I try to use the same code with my custom control, I'm getting this error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.ListBoxItem',
AncestorLevel='1''. BindingExpression:Path=IsSelected; DataItem=null;
target element is 'Expander' (Name=''); target property is
'IsExpanded' (type 'Boolean')
I've tried adding the IsSelected Property in the ListBox.ItemContainerStyle as one poster suggested in another thread but that failed.
<ListBox Margin="5"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type Expander}">
<Setter Property="IsExpanded"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
</Style>
<Style TargetType="{x:Type controls:SelectionCriteriaControl}">
<Setter Property="MaxHeight"
Value="200" />
</Style>
</ListBox.Resources>
<Expander Header="Fund Family" Margin="2">
<StackPanel>
<controls:SelectionCriteriaControl DataContext="{Binding FundFamilySelectionCriteria, Mode=TwoWay}" />
</StackPanel>
</Expander>
<Expander Header="Asset Class" Margin="2">
<StackPanel>
<controls:SelectionCriteriaControl DataContext="{Binding AssetClassSelectionCriteria, Mode=TwoWay}" />
</StackPanel>
</Expander>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter />
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Fails miserably!!!!!
Any help appreciated :)

It's a bit of a large scenario for me to set up at the moment, but something that comes to mind to try:
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}" />
I don't think where templates are used that you can define relative sources by type.
Edit: This code worked fine: Based on your original, the TemplatedParent wasn't necessary.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="testList" Margin="5"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type Expander}">
<Setter Property="IsExpanded"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
</Style>
</ListBox.Resources>
<Expander Header="Fund Family" Margin="2">
<StackPanel>
<TextBlock Text="First"/>
<TextBlock Text="Second"/>
</StackPanel>
</Expander>
<Expander Header="Asset Class" Margin="2">
<StackPanel>
<TextBlock Text="First"/>
<TextBlock Text="Second"/>
</StackPanel>
</Expander>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter />
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button HorizontalAlignment="Left" Content="Test" Grid.Row="1" Width="60" Click="Button_Click"/>
</Grid>
And a quick code-behind to trigger a programmatic SelectedIndex set...
private void Button_Click(object sender, RoutedEventArgs e)
{
testList.SelectedIndex = 1;
}
Seems to work fine for me. Clicking on a list item expands, and even using the button to set it specifically by setting the selected index it expands. Something very fishy is affecting your specific scenario... :]

Related

WPF validation - displaying errors for elements in StackPanel

I'm having problems with how validation errors are displayed for TextBox elements in vertical StackPanel. I'm trying to display error messages below TextBox.
I have this error template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
If I have enough white space below the TextBox, error is displayed fine, but in StackPanel (for example), it does not add extra margin or padding for error messages when there are some, because of adorner layer.
It is expected to be so, according to this source:
Note that the Validation.ErrorTemplate will be displayed on the adorner layer. Elements in the adorner layer are rendered on top of the rest of the visual elements and they will not be considered when the layout system is measuring and arranging the controls on the adorned element layer.
How can I display validation error messages, so that they won't show over other elements in StackPanel?
You can also consider to include your error template in the TextBox's template.
Something like that (of course it can be improved):
<Style x:Key="eTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<StackPanel>
<Border BorderBrush="Gray" BorderThickness="1" CornerRadius="1" Padding="2">
<ScrollViewer Name="PART_ContentHost" Focusable="False"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
Background="#00FFFFFF" />
</Border>
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(Validation.Errors)}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In this way the ItemsControl is considered for the layout computation.
Ok, I found the solution with converter. I ended up with style similar to this:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="10" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding (ValidationError.ErrorContent)}" Foreground="Red" Margin="0,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Trigger.Setters>
<Setter Property="Margin" Value="{Binding (Validation.Errors).Count, RelativeSource={RelativeSource Self}, Converter={StaticResource ErrorsToMarginConverter}}"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
and converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int)
{
var errors = (int)value;
return new Thickness(10, 10, 10, (errors * 20));
}
return value;
}

ListView Style Trigger affecting one control and not another

I have a ListView control with a list of system messages in it, and a corresponding "action" button that will help my user resolve the message:
If the message is an error message, I want the Foreground of the text and the action button (the [more...] part) to turn red. As you can see, it's not doing that.
I'm using a Style.Trigger bound to the message data's Severity to set the Foreground. This works fine for the TextBlock in the DataTemplate, but fails to affect the Button in that Template. My XAML looks like this:
<ListView x:Name="ImageCenterStatus" Background="{DynamicResource SC.ControlBrush}" Margin="10,22,10,0" FontSize="12" Grid.Column="1" ClipToBounds="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock TextWrapping="WrapWithOverflow" cal:Message.Attach="[Event MouseUp] = [Action DoStatusAction($dataContext)]" Text="{Binding Path=DisplayedMessage}"/>
<Button x:Name="ActionButton" Content="{Binding Path=DisplayedActionLabel}" FontSize="12" cal:Message.Attach="DoStatusAction($dataContext)" Style="{DynamicResource SC.ActionButtonHack}"></Button>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Severity}" Value="Warning">
<Setter Property="Foreground" Value="#FFCE7A16" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Severity}" Value="Error">
<Setter Property="Foreground" Value="#FFD13636" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The Button has its own Style, SC.ActionButtonHack, but I am very careful not to override the Foreground anywhere that style:
<Style x:Key="SC.ActionButtonHack" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why does the TextBlock in the DataTemplate respond to the Foreground change in the Trigger, but not the Button?
What do I need to do to get the button to respect the Foreground change?
Bind TextElement.Foreground of ContentPresenter to ListViewItem's foreground to get it work:
<Style x:Key="SC.ActionButtonHack" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter x:Name="contentPresenter"
TextElement.Foreground="{Binding Foreground,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ListViewItem}}"
HorizontalAlignment="Center"
VerticalAlignment="Center" Margin="0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
OR
Either bind it on Button itself:
<Button x:Name="ActionButton"
Foreground="{Binding Foreground, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ListViewItem}}"
Content="{Binding Path=Name}" FontSize="12"
Style="{DynamicResource SC.ActionButtonHack}"/>

ItemContainerStyle reuse silverlight 5

I have control template with a checkbox as this:
<Style x:Key="GeneralChkbxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement">
<CheckBox ClickMode="Press" Content="{Binding Path=Name}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter
</Style>
I use this in a combobox and a listbox mentioning like this ItemContainerStyle="{StaticResource GeneralChkbxItemStyle .It works perfect when the datasource has a property named Name .
But I am wondering how can I use this template with other data source having class which does not have a name property . It might have a property name2, or name 3 etc depending on the datasource.
Any suggestions? Thanks.
In your template:
<CheckBox ClickMode="Press"
Content="{TemplateBinding Content}"
ContentTemplate="{templateBinding ContentTemplate}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
Then in your view:
<ListBox DisplayMemberPath="Name"..../>
<!--OR-->
<ListBox DisplayMemberPath="Name2"..../>
<!--OR-->
......
You may use implicit data templates.
<DataTemplate DataType="MyApp:Person">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate DataType="MyApp:Booking">
<TextBlock Text="{Binding Id}" />
</DataTemplate>
<Style x:Key="GeneralChkbxItemStyle"
TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement">
<CheckBox ClickMode="Press"
Content="{Binding .}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Bind image in templated ListBox to ViewModel property

TL;DR - I had a binding error. Tired eyes miss things.
I have implemented a multi-select CheckBox list using a ListBox as the container. Now, beside each checkbox in the list I want to display an image whose visbility is bound to a ViewModel property, but I'm having difficulty doing this.
My styles are:
<Grid.Resources>
<Style x:Key="ListBoxCheckStyle" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ItemsPresenter HorizontalAlignment="Left" VerticalAlignment="Top"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ItemsPanelTemplate x:Key="ListBoxCheckStyleItemsPanelTemplate">
<StackPanel />
</ItemsPanelTemplate>
<Style x:Key="ListBoxItemCheckStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<StackPanel Orientation="Horizontal">
<ChimeControls:CheckBox
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="0,0,10,0"
IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
<Image
Width="16"
Height="16"
VerticalAlignment="Center"
Source="{StaticResource OccurredStatusTypeImageSource}"
Visibility="{Binding HasConsentCondition, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Collapsed}"
HorizontalAlignment="Right"
Margin="10,0,10,0" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
My Listbox is defined as:
<ListBox
x:Name="objectivesListBox"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource ListBoxCheckStyle}"
ItemsPanel="{StaticResource ListBoxCheckStyleItemsPanelTemplate}"
ItemContainerStyle="{StaticResource ListBoxItemCheckStyle}"
ItemsSource="{Binding ObjectivesList}"
DisplayMemberPath="mgt_plan_obj_name"
AttachedProperties:ListBoxSelectedItems.Items="{Binding SelectedObjectives, Mode=TwoWay}"
SelectionMode="Multiple"/>
My image never displays though, and the getter of the property the Visibility is bound to is never called. What am I missing?
As suggested by nemesv, I checked my output window again and there was my binding error.

WPF - ListBox ignores Style When ItemsSource is bound

I have created styled a ListBox in WPF so that it is rendered as a checkbox list.
When I populate the ListBox's items manually, the styling works perfectly. However, when I instead bind the ItemsSource of the ListBox to a static resource (an ItemsControl containing the required items), the styling is completely dropped.
Here's the style:
<Style x:Key="CheckBoxListStyle" TargetType="ListBox">
<Style.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
<ContentPresenter
Grid.Column="1"
Margin="2,0,0,0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
</Style>
Here's the code for the ListBox that shows the style correctly:
<ListBox x:Name="ColumnsList"
Grid.Column="0"
Grid.Row="0"
Style="{StaticResource CheckBoxListStyle}"
BorderThickness="1">
<ListBox.Items>
<ListBoxItem>Test</ListBoxItem>
<ListBoxItem>Test2</ListBoxItem>
<ListBoxItem>Test3</ListBoxItem>
</ListBox.Items>
</ListBox>
Here's the code for the ListBox that ignores the style:
<ListBox x:Name="ColumnsList2"
Grid.Column="0"
Grid.Row="0"
Style="{StaticResource CheckBoxListStyle}"
BorderThickness="1"
ItemsSource="{Binding Source={StaticResource Test1}, Path=Items}">
</ListBox>
Hoping someone can help - I'm pretty new to all this and have tried everything I can think of, but everything I've read leads me to believe that setting ItemsSource should have the same outcome as setting the items manually, so I can't see any reason why this would not work.
Thanks,
AT
Change the Style.Resources to setting the ItemContainerStyle property and it should work like a charm.
<Style x:Key="CheckBoxListStyle" TargetType="ListBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
<ContentPresenter
Grid.Column="1"
Margin="2,0,0,0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
</Style>
In older versions (before SP1), when you define Styles in Style, one of those style will be ignored. Alternatively, you can set the Resources of Style in the parent resources..
Hope this helps!
This is because your TargetType in the CheckListBoxStyle is targetting a ListBoxItem, but when you set the ItemSource property of the ListBox you are binding to a list of other elements (ints for example). This means your target type should be int instead of ListBoxItem.
Alternatively do not specify a target type.

Resources