windows-phone-8listboxfindancestor

FindAncestor implementation in WP8 ListBox


I want to implement a Listbox binding directly and here is the code i used in WPF syntax

<ListBox  Name="lboxData" ItemsSource="{Binding}">
<ListBox.ItemTemplate >
    <DataTemplate>
        <StackPanel>                        
            <ToggleButton x:Name="toggleChild"  Style="{StaticResource ChapterHeadingStyle}"
                  IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"   // This is what i have to change . I want to set it based on the status of the ListBoxItem & Given code is the one i used in WPF app 
            />
            <ListBox Visibility="{Binding IsChecked, ElementName=toggleChild, Converter={StaticResource boolToVis}}" ItemsSource="{Binding pages}" Margin="10,0,0,0"  >
                        <ListBox.ItemTemplate >
                            <DataTemplate>
                              //here is displaying child items one by one ..
                            </DataTemplate>
                        </ListBox.ItemTemplate >
            </ListBox>
         </ListBox.ItemTemplate >
    </DataTemplate>
</StackPanel>       
</ListBox>

The problem is that in WP8 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem} is not supported . So how can i achieve the same thing in WP8. I want to set the toggle button as Checked if the container ListboxItem is selected , else i want to set the IsChecked as False.


Solution

  • I'll start by writing a comparer class

    public  class ElementComparer : FrameworkElement
    {
        public object Element1
        {
            get { return (object)GetValue(Element1Property); }
            set { SetValue(Element1Property, value); }
        }
    
        // Using a DependencyProperty as the backing store for Element1.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty Element1Property =
            DependencyProperty.Register("Element1", typeof(object), typeof(ElementComparer), new PropertyMetadata(null, UpdateResult));
    
        public object Element2
        {
            get { return (object)GetValue(Element2Property); }
            set { SetValue(Element2Property, value); }
        }
    
        // Using a DependencyProperty as the backing store for Element2.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty Element2Property =
            DependencyProperty.Register("Element2", typeof(object), typeof(ElementComparer), new PropertyMetadata(null, UpdateResult));
    
        public bool Result
        {
            get { return (bool)GetValue(ResultProperty); }
            set { SetValue(ResultProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Result.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ResultProperty =
            DependencyProperty.Register("Result", typeof(bool), typeof(ElementComparer), new PropertyMetadata(false, OnResultChanged)); //added changed handler
    
        private static void OnResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ElementComparer ec = d as ElementComparer;
            //if true then set the element 2 to element 1 of the comparer otherwise null
            ec.Element2 = ((bool)e.NewValue) ? ec.Element1 : null;
        }
    
        private static void UpdateResult(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ElementComparer ec = d as ElementComparer;
            ec.Result = object.ReferenceEquals(ec.Element1, ec.Element2);
        }
    }
    

    then I'll bind IsChecked from togglebutton to the Result of ElementComparer and will bind the Element1 and Element2 of the comaprer with the current item and the SelectedItem of lboxData (ListBox)

    <ListBox Name="lboxData" ItemsSource="{Binding}">
        <ListBox.ItemTemplate >
            <DataTemplate>
                <StackPanel>
                    <!--added  Mode=TwoWay to binding of Element2-->
                    <local:ElementComparer x:Name="ElementComparer"
                                        Element1="{Binding}"
                                        Element2="{Binding SelectedItem, ElementName=bookTOCBox, Mode=TwoWay}" />
                    <!--removed Mode=OneWay from binding of IsChecked and added Mode=TwoWay-->
                    <ToggleButton x:Name="toggleChild" Content="{Binding name}" 
                                  Style="{StaticResource ChapterHeadingStyle}" 
                                  IsChecked="{Binding Result, ElementName=ElementComparer, Mode=TwoWay}"/>
                    ...
                </StackPanel>
    

    the trick is to compare the selected item of the list to the current item to detect if it is selected as long as the name of parent listbox is "lboxData", this will work in WP8 too

    Update summary

    Extras

    additionally if you don't want to see ugly looking blue selection in list items then you may add the following to your resources too

    <Style TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Border Background="Transparent">
                        <ContentPresenter/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>