WPF ItemsControl and Togglebuttons weird behaviour - c#

I've been working on a WPF application but encountered some weird behaviour when using ItemsControl to display a list of togglebuttons with binding. It works just fine when it is displaying a collection of 1 element, but when I have two elements it behaves very strange. It will not show the image of both togglebuttons at once if they are in the same state (i.e. toggled on).
The relevant XAML. I have checked that the property behind is updated correctly, but let me know if there is any code that is needed.
<ItemsControl x:Name="ServiceItemsControl" Grid.Row="0" ItemsSource="{Binding DraftMessage.Services}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding Path=Selected}">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<!--Setter Property="BorderThickness" Value="0"/-->
<Setter Property="Opacity" Value="1"/>
<Setter Property="Content">
<Setter.Value>
<Image Source="{Binding Path=Service.OtherLogo}" Width="25" Height="25"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{Binding Path=Service.Logo}" Width="25" Height="25"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

You need to use a DataTrigger on the Image to change its Source property when the ToggleButton.IsChecked value is changed instead. Try this:
<DataTemplate>
<ToggleButton IsChecked="{Binding Path=IsSelected}">
<Image Width="25" Height="25">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding Service.OtherLogo}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked,
RelativeSource={RelativeSource AncestorType={x:Type
ToggleButton}}}" Value="True">
<Setter Property="Source" Value="{Binding Service.Logo}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<!--Setter Property="BorderThickness" Value="0"/-->
<Setter Property="Opacity" Value="1"/>
</Style>
</ToggleButton.Style>
</ToggleButton>
</DataTemplate>

Related

Change listbox item style when selected in WPF

I'm not entirely sure what I'm doing incorrect but it appears that my style trigger is not being recognized. I want to change the color of the Stroke when the listbox item is selected.
<ListBox ItemsSource="{Binding CityList}" DisplayMemberPath="Name" SelectionMode="Extended"
VirtualizingPanel.IsVirtualizing="true"
VirtualizingPanel.VirtualizationMode="Recycling"
Background="Brown">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding Longitude, Converter={StaticResource longValueConverter}, ConverterParameter={StaticResource mapWidth}}"/>
<Setter Property="Canvas.Top" Value="{Binding Latitude, Converter={StaticResource latValueConverter}, ConverterParameter={StaticResource mapHeight}}"/>
<Setter Property="BorderThickness" Value="3" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Ellipse x:Name="indicator"
Fill="#FF000000"
Height="10"
Width="10"
Stroke="Transparent"
StrokeThickness="2"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="indicator" Property="Stroke" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"
Width="{StaticResource mapWidth}"
Height="{StaticResource mapHeight}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
You can't use TargetName in a Style Setter.
Instead of setting the ContentTemplate property, you may set the Template property and add the Trigger to the ControlTemplate.Triggers collection:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Ellipse x:Name="indicator"
Fill="#FF000000"
Height="10"
Width="10"
Stroke="Transparent"
StrokeThickness="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="indicator" Property="Stroke" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
You may also add a ContentPresenter to the Grid in the ControlTemplate, which would display the elements of the ItemTemplate (if you later decide to declare one).

Collapse GroupBox border without collapsing content

Is it possible to collapse the border of a GroupBox control in XAML (i.e. bind to a property in the VM) without also collapsing the content?
I don't just want to remove the border, which can be achieved by setting BorderThickness to 0 and Header to an empty string. I also want the GroupBox content to stretch out over where the border was.
<DataTemplate DataType="{x:Type config:ElementGroup}">
<DataTemplate.Resources>
<Style TargetType="{x:Type GroupBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HideBorder}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
<Setter Property="Header" Value="{Binding Path=ItemLabel}" />
<Setter Property="Margin" Value="5,0,5,0" />
</Style>
</DataTemplate.Resources>
<GroupBox>
<ItemsControl ItemsSource="{Binding Path=ElementList}" Visibility="Visible">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Path=Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
</DataTemplate>
Change your GroupBox Style to this:
<Style TargetType="{x:Type GroupBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HideBorder}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
<Setter Property="Header" Value="{Binding Path=ItemLabel}" />
<Setter Property="Margin" Value="5,0,5,0" />
</Style>
Collapsing a GroupBox, i.e. setting its Visibility property to Hidden or Collapse, will also collapse its Content.
If you don't want this, you could define another ItemsControl that you display when the GroupBox gets collapsed:
<DataTemplate DataType="{x:Type config:ElementGroup}">
<DataTemplate.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
<Setter Property="Header" Value="{Binding Path=ItemLabel}" />
<Setter Property="Margin" Value="5,0,5,0" />
</Style>
</DataTemplate.Resources>
<Grid>
<GroupBox x:Name="gb">
<ItemsControl ItemsSource="{Binding Path=ElementList}" Visibility="Visible">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Path=Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</GroupBox>
<ItemsControl x:Name="ic" ItemsSource="{Binding Path=ElementList}" Visibility="Collapsed">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding Path=Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=HideBorder}" Value="True">
<Setter TargetName="gb" Property="Visibility" Value="Collapsed" />
<Setter TargetName="ic" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

DataGridRowDetails - Toggling issue

I am having a DataGrid containing some Jobs.
What every job is doing is shown in the RowDetails.
Here I got multiple problems:
Problem: Why is the Imageon the ToggleButton only shown on the Selected and the last Row ?
This is my xaml:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=DetailsVisibility, Converter={StaticResource BoolToVisConverter}, Mode=TwoWay}">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="..\Resources\ic_expand_less_48px.png" Height="16" Width="16" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter Property="Content">
<Setter.Value>
<Image Source="..\Resources\ic_expand_more_48px.png" Height="16" Width="16" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="..\Resources\ic_expand_more_48px.png" Height="16" Width="16" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
when Style uses a UIElement for Content property (Image in this case), it creates a single instance of that element. When Style is applied to multiple controls only one of them display Content because UIElement cannot have multiple parents.
That is why in most cases custom templates are used. ToggleButton has ContentTemplate property to customize the appearance of content part: (the same code for other two triggers)
<Trigger Property="IsChecked" Value="False">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="..\Resources\ic_expand_more_48px.png" Height="16" Width="16" />
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
I know another method with non-shared resource:
<Image x:Key="ExpandMoreImg" x:Shared="False"
Source="..\Resources\ic_expand_more_48px.png" Height="16" Width="16" />
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" Value="{StaticResource ExpandMoreImg}"/>
</Trigger>
Shared is set to false and there will be an instance of Image for each usage

ToggleButton style not working when in treeview

I have a treeview with buckets and parts. Everything looks great except one feature. A property on bucket (bool IsEditable) allows the user to toggle editing on or off within the tree view for that node. But for some reason when wpf displays the collection of buckets in the treeview the togglebuttons do not show the proper text
Here is my xaml:
<Style TargetType="{x:Type ToggleButton}" x:Key="Editor">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Edit"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="Done"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="HierarchialItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BucketQty, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding PartNum}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DisplayBucket">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Sequence}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Description}"/>
<ToggleButton IsChecked="{Binding IsEditable}" Width="25" Style="{StaticResource Editor}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="EditBucket">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Sequence}"/>
<TextBlock Text=" "/>
<TextBox Text="{Binding Description}"/>
<ToggleButton IsChecked="{Binding IsEditable}" Width="25" Style="{StaticResource Editor}"/>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="TreeTemplate"
DataType="{x:Type domain:Bucket}"
ItemsSource="{Binding Parts}"
ItemTemplate="{StaticResource HierarchialItemTemplate}"
>
<ContentPresenter Content="{Binding}"
Style="{DynamicResource TreeNodeStyle}"/>
</HierarchicalDataTemplate>
<Style TargetType="{x:Type ContentPresenter}" x:Key="TreeNodeStyle">
<Setter Property="ContentTemplate" Value="{StaticResource DisplayBucket}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditable}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditBucket}"/>
</DataTrigger>
</Style.Triggers>
</Style>
The above is window.resources. In the Grid is
<TreeView x:Name="Buckets"
ItemsSource="{Binding Model.Job.Buckets}"
ItemTemplate="{StaticResource TreeTemplate}"
>
So for whatever reason when I add nodes I get a bucket with a toggle button, but the toggle button text only shows up for the last node added. If I click a toggle button, the text disappears out of the previous toggle button and shows up in the one just clicked. The effect is similar to what this person experienced, but I have no idea what the guy was talking about who answered the question ToggleButton Style only works on last ToggleButton.
Any thoughts?
You have couple of options (ordered from least to most dramatic):
1) Content
<Style TargetType="{x:Type ToggleButton}" x:Key="Editor">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" Value="Edit" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Done" />
</Trigger>
</Style.Triggers>
</Style>
2) ContentTemplate
<Style TargetType="{x:Type ToggleButton}" x:Key="Editor">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="Edit"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="Done"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
3) ControlTemplate
<Style TargetType="{x:Type ToggleButton}" x:Key="Editor">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<TextBlock Text="Edit"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<TextBlock Text="Done"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
The reason why your approach didn't work is that TextBlock control is created only once (i.e. not created as part of template - ControlTemplate or DataTemplate).

Use an image for a menu option in WPF

I use the following XAML to achieve a menu using radio buttons:
<Grid.Resources>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<TextBlock Text="{TemplateBinding Content}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Foreground" Value="Gold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Margin="25,74,644,78" Background="{x:Null}">
<RadioButton Content="1. Do something." Click="RadioButton1_Click" FontSize="16" Margin="5"/>
<RadioButton Content="2. Do something else." Click="RadioButton2_Click" FontSize="16" Margin="5"/>
</StackPanel>
This works fine but I want to include an image as a menu option instead of text. (The image is for 'Home' and it will have a normal image and a hover over image)
Here is what I want to achieve:
How do I do this?
You can define another style for displaying Radio Button as an Image based on the existing style posted in question, just need slight changes in ControlTemplate's content part. Following is an example of such a Style and the usage :
<StackPanel Orientation="Horizontal" Background="Black">
<StackPanel.Resources>
<Style x:Key="PictSTyle" TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Image Width="50" Height="50">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="home_default.png" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type RadioButton}}}" Value="True">
<Setter Property="Source" Value="home_hovered.png" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<RadioButton Margin="5" Style="{StaticResource ResourceKey=PictSTyle}"/>
</StackPanel>
To give a menu item an image:
<MenuItem>
<MenuItem.Icon>
<Image Source=""/>
</MenuItem.Icon>
</MenuItem>
for your trigger just change the MenuItem's icon to the "highlighted one", when the user hovers over the menu item (IsMouseOver), by changing the Icon's image source.
Put an image element as the "Content" property for your radio button
<RadioButton Click=HomRadioButtonClick>
<RadioButton.Content>
<Image Source=""/>
</RadioButton.Content>
</RadioButton>
You'll need some extra logic for the hover behavior, but that should at least get the image in your menu.

Categories