1

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.

Sebastian
  • 4,625
  • 17
  • 76
  • 145

1 Answers1

1

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

  • Removed one way binding from IsChecked property of toggle to Result of ElementComparer
  • Added two way binding to SelectedItem to Element2 of ElementComparer
  • added property changed handler for the Result property in ElementComparer
  • when the result is changes and is true (toggle is checked) push the value of Element1 to Element2
  • since the Element2 is bound to SelectedItem it force other items's result to false hence turn off the toggle and collapse the child list
  • added Mode=TwoWay to IsChecked property of toggle as seems quit unpredictable without it

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>
pushpraj
  • 13,458
  • 3
  • 33
  • 50
  • I tried the samples , but getting exceptions at 2 points 1. MultiBinding is not a valid xaml tag and also IMultiValueConverter namespace missing . How can i override these 2 issues? – Sebastian Jun 09 '14 at 04:32
  • apologies for previous approach, as i was not aware of multi bindings and converters are excluded from metro apps. I modified my solution to work without it so this is more likely to work on WP8 – pushpraj Jun 09 '14 at 05:45
  • Yes worked . But not in a full way and i am struggling to make it the way i wanted. I will upload detailed problem description soon. – Sebastian Jun 10 '14 at 04:59
  • The way i expected the listbox to behave - First all main items are collapsed (Not selected) Once select the item (say first item in list), the underlying items should expand. Once select another item (say third item in list) , all other selected items should collapse and new item ie third one should expand. But now the behaviour is more like , at first all items are not selcted. I select item 1 , and it expands all the subitems of it. I select item 3, Now both item 1 and 3 are in expanded state The xaml structre used is here http://pastebin.com/FJDWRiVu – Sebastian Jun 10 '14 at 05:36
  • the xaml code you've posted have many dependencies, so is it possible to have a look on a working sample code? – pushpraj Jun 10 '14 at 05:59
  • https://onedrive.live.com/redir?resid=529BE5C1D41B4A28%21231 The sample files needed is uploaded here. A wp8 page xaml and cs page and also a text file that contains the data that produce the needed model – Sebastian Jun 10 '14 at 11:52
  • updated my answer, see if this is what you are looking for, now all other items will collapse except the selected one. – pushpraj Jun 10 '14 at 15:13
  • Seems to be no difference with latest code. I applied the changed still i multiple items are selected at a time. ie. if i select chapter1 (its child are expanded) and if i select chapter 3 both chapter 1 and 3 are expanded. But i want to expand chapter 3 and collapse chapter 1. It would be nice if u can post the page xaml as it is to check properly – Sebastian Jun 11 '14 at 04:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/55407/discussion-between-pushpraj-and-jmat). – pushpraj Jun 11 '14 at 05:32
  • 1
    , Mode=TwoWay is needed for toggle button as wel – Sebastian Jun 11 '14 at 06:55