ListView ItemContainerStyle Template - wpf

I created style to be used as my ListView's item template that contains a CheckBox and a TextBlock:
<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListView}">
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="Template">
...
The checkbox in the template is bound to the IsSelected property of the list view item:
<CheckBox x:Name="itemCheckBox" Width="13" Height="13" Focusable="False" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
And the textblock's text is bound to the Value property of the source of the list view item:
<TextBlock x:Name="textBlock" Padding="5,0,0,0" HorizontalAlignment="Stretch">
<ContentPresenter Content="{Binding Path=Value}"/>
</TextBlock>
Each of the items in my list is an object that contains two properties, Value and IsChecked. What I am trying to do is bind the IsChecked property of my object to the IsSelected property of my list view item. However, I do not know how to access the IsChecked property of my object from within the ListViewItem template.
I didn't have any trouble binding the content of the text block to the Value property of my object but where would I even put the binding definition if I want the IsChecked property of my object to be bound to the IsSelected property of the list view item?

The DataContext for each ListViewItem should be set to the item data when it is created by the parent ListView so you should be able to use:
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="IsSelected" Value="{Binding IsChecked}">
...
</Style>

Related

Style of a TextBox: Why is 'AcceptsReturn=true' not applied?

I have a custom control in WPF, which consists of a toggle button, a TextBlock and a TextBox. What I basically want to do is to show the TextBox when the toggle button is checked and the TextBlock otherwise. Furthermore I want allow defining to style properties on the control via dependency properties, which are applied to the TextBlock and the TextBox at runtime. The default template looks like this:
<Style TargetType="{x:Type views:EditableLabel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:EditableLabel}">
<Border Background="{TemplateBinding Background}">
<DockPanel Margin="0">
<telerik:RadToggleButton x:Name="PART_Toggle"
DockPanel.Dock="Right"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsInEditMode, Mode=TwoWay}">
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ToggleImage}" Height="14" />
</telerik:RadToggleButton>
<TextBlock x:Name="PART_TextBlock"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}" >
</TextBlock>
<TextBox x:Name="PART_TextBox"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
</TextBox>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The custom control has two dependency properties for styles, one for the PART_TextBlock and one for PART_TextBox. The styles are assigned in the OnApplyTemplate method of the custom control and in the property change callbacks of the two dependency properties:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBlock = (TextBlock) GetTemplateChild("PART_TextBlock");
_textBox = (TextBox) GetTemplateChild("PART_TextBox");
_toggleButton = (RadToggleButton) GetTemplateChild("PART_Toggle");
ApplyStyles();
UpdateVisibilities();
}
private void ApplyStyles()
{
if (_textBlock != null) _textBlock.Style = TextBlockStyle;
if (_textBox != null) _textBox.Style = TextBoxStyle;
}
(The callbacks are not shown here, as they are trivial, just calling ApplyStyles().
I use the custom control like this:
<views:EditableLabel Text="{Binding SelectedToolbox.Description, Mode=TwoWay}"
CanEdit="{Binding SelectedToolbox.CanEdit}"
ToggleImage="../Resources/Images/edit-26.png">
<views:EditableLabel.TextBlockStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</views:EditableLabel.TextBlockStyle>
<views:EditableLabel.TextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
</Style>
</views:EditableLabel.TextBoxStyle>
</views:EditableLabel>
Everything works as expected, except from the AcceptsReturn setter is not applied, which I find very strange. I've debugged ApplyStyles(): The style is assigned correctly and both setters are contained within the style.
TextWrapping and VerticalScrollBarVisibility are both set correctly:
while AcceptsReturn is not:
Any ideas, what might be the issue here?
The screenshot you posted suggests AcceptsReturn has a local value, i.e., a value set by explicitly calling the property setter or SetValue. Do you have any code in EditableLabel which explicitly sets the AcceptsReturn property? If so, the local value you set will take precedence over any style setters. You can avoid this by using SetCurrentValue to change the value while leaving the value source unchanged.
Secondly, rather assigning the style in your code behind, it is generally easier and more reliable to simply bind the style within the template, e.g.:
<TextBox x:Name="PART_TextBox" Style="{TemplateBinding TextBoxStyle}" ... />
You might try this first and see if you get better results.

How can i bind a IsSelected Property to a textblock

my treeview is populated with textblock items.
If a user clicks on a textblock, i want to set a property in my model called "isSelected".
But : the textblock have no Property IsSelected.
How can i Implement this ?
Derive from textblock and add a Property ?
You have to use the TreeViewItem.IsSelected property. You will have to specify the custom style for all items of the tree view.
<TreeView>
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</TreeView.Resources>
</TreeView>

Binding to a cutom property of the template parent

Q: How do I bind to a custom property of the template parent from a child control's style DataTrigger
I've been scratching my head over this one for a couple of days.
I have a databound TreeView which uses a Style which has a Template. The TreeView is bound to a ObservableCollection and a HierarchicalDataTemplate + DataTemplate bind to properties inside a collection item.
FontGroup -> Font(s)
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Image x:Name="ExpanderImage" Source="/Typesee;component/Resources/tree_expand.png" RenderOptions.BitmapScalingMode="NearestNeighbor" />
<ControlTemplate.Triggers>
<DataTrigger Binding="??? IsItemSelected ???" Value="True">
<Setter TargetName="ExpanderImage" Property="Source" Value="/Typesee;component/Resources/tree_collapse_selected.png" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="FontTreeViewTemplate" TargetType="{x:Type TreeViewItem}">
...
<ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" ... />
...
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsItemSelected}" Value="True">
<!-- WORKS FINE HERE -->
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
First I tried to bind like:
Binding Path=IsItemSelected, RelativeSource={RelativeSource TemplatedParent}
Then I read that might not work so I tried (including AncestorLevel 1+3):
Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=2
Have also tried combos with UpdateSourceTrigger=PropertyChanged and Mode=TwoWay
If this is a flawed design please suggest a way of doing this: I basically want to change the image of the expand toggle button based on whether the property IsItemSelected is true on the TreeViewItem -- any ideas?
Thanks so much for any help!
The viewmodel in all likelihood will be the DataContext, so the binding should be a RelativeSource binding with a respective path which needs to explicity target the DataContext as the new source is the RelativeSource:
{Binding DataContext.IsItemSelected,
RelativeSource={RelativeSource AncestorType=TreeViewItem}}
As noted in my comment it might be advisable to extract this logic from the ControlTemplate as this leaves its bounds. One method would be subclassing the ToggleButton and exposing a public property for the image which then can be changed via a Style.

WPF: Change FontSize of ComboBox without knowing items

I want to set the FontSize for some ComboBoxes in wpf. But the only solution I found, is to set the FontSize of the particular ComboBoxItems. But the items are dynamically added (so I could listen on ItemsChanged and set the style each time, but that is very ugly).
Is there a way to set the FontSize for all items of a ComboBox generally?
EDIT1: The ComboBoxes themselves are also dynamically added in code.
EDIT2: Following your answers it must be something like this:
<Style TargetType="ComboBox" x:Key="MyComboBox"">
<Setter Property="ItemContainerStyle" Value="{DynamicResource MyComboItemStyle}"/>
</Style>
<Style x:Key="MyComboItemStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="FontSize" Value="14"/>
</Style>
But it also has no effect!
EDIT3: Here's how I add the Items:
string[] strEnums = //Some dynamic magic;
foreach (string str in strEnums)
{
(input as ComboBox).Items.Add(strEnum);
}
EDIT4: And here's the initialization of the combobox:
input = new ComboBox();
input.SetResourceReference(ComboBox.StyleProperty, "MyComboBox");
That's all!
1.If you want to set the FontSize for the entire ComboBox, you can set the FontSize property of the ComboBox itself:
<ComboBox FontSize="18" ...>
...
</ComboBox>
2.If you want to change the FontSize only on items or change the font size based on some criteria, you should specify `ItemContainerStyle':
<ComboBox ...>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="FontSize" Value="18"/>
</Style>
</ComboBox.ItemContainerStyle>
...
</ComboBox>
You can define the way how all the items get rendered with defining the ItemTemplate of the ComboBox. In there you bind to the properties of the bound Items.
Example:
<ComboBox ItemsSource="{Binding whateverbinding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Content}" FontSize="12" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

WPF Databound RadioButton ListBox

I am having problems getting a databound radiobutton listbox in WPF to respond to user input and reflect changes to the data it's bound to (i.e., to making changes in the code). The user input side works fine (i.e., I can select a radiobutton and the list behaves as expected). But every attempt to change the selection in code fails. Silently (i.e., no exception).
Here's the relevant section of the XAML (I think):
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Name="theBorder" Background="Transparent">
<RadioButton Focusable="False" IsHitTestVisible="False"
IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" >
<ContentPresenter />
</RadioButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
I bind the listbox to a List of SchoolInfo objects. SchoolInfo contains a property called IsSelected:
public bool IsSelected
{
get { return isSelected; }
set
{
if( value != isSelected )
{
isSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
}
The OnPropertyChanged() stuff was something I put in during my experimentation. It doesn't solve the problem.
Things like the following fail:
((SchoolInfo) lbxSchool.Items[1]).IsSelected = true;
lbxSchool.SelectedIndex = 1;
They fail silently -- no exception is thrown, but the UI doesn't show the item being selected.
The RadioButton is binding to the ListBoxItem IsSelected property, not to your SchoolInfo IsSelected property.
(It is confusing because ListBoxItem has an "IsSelected" property, and so also does your SchoolInfo object, which is why there were no binding errors).
To fix, ListBoxItem.IsSelected needs to be bound to your SchoolInfo IsSelected property.
i.e. You need an extra setter for the ListBoxItem to bind to SchoolInfo.IsSelected, and then the list box item will work as it should, and also the RadioButton can bind correctly to ListBoxItem.IsSelected.
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
I would enable NotifyOnSourceUpdated in your RadioButton binding. Even though you are allowing a two-way binding (which is the default), notifications won't be picked up from code-behind changes unless you are explicitly listening for them.
<RadioButton Focusable="False" IsHitTestVisible="False"
IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}, NotifyOnSourceUpdated=True}" >

Resources