NAGASREE
NAGASREE

Reputation: 374

ListBox ItemTemplate update using ToggleButton and attached Property

I want to implement a ListBox with ToggleButton when I press then the template of ListBox should be updated. The template one will have only icons and one will have both icons and text as shown in the image.

Collapsed ListBox, icons only Expanded ListBox, text and icons.

I have created one attached property for this and binded it to the ToggleButton IsChecked property inside control template, but this attached property is not updating and my template is not changing. First time when I load the application it is working.

<Style x:Key="NavigationListBoxStyle" TargetType="{x:Type ListBox}">
   <Setter Property="Background" Value="{StaticResource PrimaryDarkBrush}"/>
   <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
   <Setter Property="HorizontalContentAlignment" Value="Center"/>
   <Setter Property="SelectedIndex" Value="0"/>
   <Setter Property="Width" Value="Auto"/>

   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type ListBox}">
            <Border Background="{TemplateBinding Background}"
                                BorderThickness="0"
                                Padding="0"
                                SnapsToDevicePixels="true">
               <ScrollViewer Padding="{TemplateBinding Padding}"
                                          Focusable="false">
                  <StackPanel>
                     <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                     <ToggleButton IsChecked="{Binding local:IsOpenInfo.IsOpen,Mode=TwoWay}" Style="{DynamicResource ToggleButtonstyle}" Foreground="White">
                     </ToggleButton>
                  </StackPanel>
               </ScrollViewer>
            </Border>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
   <Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate}"/>
   <Style.Triggers>
      <Trigger Property="local:IsOpenInfo.IsOpen" Value="True">
         <Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate1}"/>
      </Trigger>
      <Trigger Property="local:IsOpenInfo.IsOpen" Value="False">
         <Setter Property="ItemTemplate" Value="{DynamicResource NavigationDataTemplate}"/>
      </Trigger>
   </Style.Triggers>
</Style>

<Style x:Key="ToggleButtonstyle" TargetType="{x:Type ToggleButton}">
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type ToggleButton}">
            <StackPanel Orientation="Horizontal">
               <iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center" 
                                              HorizontalAlignment="Center" Margin="12" Kind="ArrowBack"/>
               <TextBlock Text="Collapse" VerticalAlignment="Center"  HorizontalAlignment="Center" />
            </StackPanel>
            <ControlTemplate.Triggers>
               <Trigger Property="IsChecked" Value="true">
                  <Setter TargetName="MenuItemIcon" Property="Kind" Value="ArrowForward"/>
                  <Setter TargetName="txtBlock" Property="Visibility" Value="Collapsed"/>
               </Trigger>
               <Trigger Property="IsChecked" Value="false">
                  <Setter TargetName="MenuItemIcon" Property="Kind" Value="ArrowBack"/>
                  <Setter TargetName="txtBlock" Property="Visibility" Value="Visible"/>
               </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

<DataTemplate x:Key="NavigationDataTemplate">
   <iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center" 
                                              HorizontalAlignment="Center" Margin="12"/>

   <DataTemplate.Triggers>
      <DataTrigger Binding="{Binding}" Value="Home">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Home"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding}" Value="Email">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Email"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding}" Value="Cloud">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Cloud"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding}" Value="Collapse">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Mail"/>
      </DataTrigger>
   </DataTemplate.Triggers>
</DataTemplate>

<DataTemplate x:Key="NavigationDataTemplate1">
   <StackPanel Orientation="Horizontal">
      <iconPacks:PackIconMaterialDesign x:Name="MenuItemIcon" VerticalAlignment="Center" 
                                              HorizontalAlignment="Center" Margin="12"/>
      <TextBlock Text="{Binding}"  HorizontalAlignment="Center" VerticalAlignment="Center"/>
   </StackPanel>
   <DataTemplate.Triggers>
      <DataTrigger Binding="{Binding}" Value="Home">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Home"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding}" Value="Email">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Email"/>
      </DataTrigger>
      <DataTrigger Binding="{Binding}" Value="Cloud">
         <Setter TargetName="MenuItemIcon" Property="Kind" Value="Cloud"/>
      </DataTrigger>
   </DataTemplate.Triggers>
</DataTemplate>

The attached property class as below:

public class IsOpenInfo
{
   public static bool GetIsOpen(DependencyObject obj)
   {
      return (bool)obj.GetValue(IsOpenProperty);
   }

   public static void SetIsOpen(DependencyObject obj, bool value)
   {
      obj.SetValue(IsOpenProperty, value);
   }

   // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty IsOpenProperty =
      DependencyProperty.RegisterAttached("IsOpen", typeof(bool), typeof(IsOpenInfo), new PropertyMetadata(true));
}

I am clueless why the attached property not updating when IsChecked property is changed and style triggers not working?
Is there any simple way to achieve this?

Anyone guide me to handle this case?

Upvotes: 0

Views: 78

Answers (1)

thatguy
thatguy

Reputation: 22089

Your style is defined with TargetType set to ListBox, the triggers will resolve the value of your custom local:IsOpenInfo.IsOpen property for the ListBox instance that your style is applied to.

However, the ToggleButton is defined inside the ControlTemplate of the ListBox. Its IsChecked binding will not automatically set the value of the attached property on its templated parent control. It will be set on the ToggleButton itself. So practically, you are setting the value on the ToggleButton, but you are checking it on the ListBox in the style, so you are operating on different values.

You can make it work by explicitly binding to the TemplatedParent, which is a ListBox instance. Both the style and the toggle button will resolve the attached property value for the same ListBox instance.

<ToggleButton IsChecked="{Binding (local:IsOpenInfo.IsOpen), RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
              Style="{DynamicResource ToggleButtonstyle}"
              Foreground="White"/>

Upvotes: 1

Related Questions