TreeView Oddity

May 10, 2011 at 5:28 AM

First let me say I'm not sure who's problem this is. I'm thinking WPF, but in-case it might be a  CM issue I thought I'd ask here. I have a TreeView bound with some items.. When i add 1 item, then remove that item, then add a new item and try to remove that item I get the old ViewModel for the first Item I removed. This causes my remove logic to not work properly as the returned ViewModel belongs to a already removed item. I have a sample application that repro's the problem if anyone's interested in taking a stab. I've tried everything on the forums relating to the memory leak in the .net 3.5 TreeView control thinking it might have something to do with this issue. However, nothing worked. So maybe it's CM ;)

Anyways.. here's how the treeview is defined. 

 

Thanks in advance.

 

<Grid>
        <Grid.Resources>
            <ContextMenu x:Key="RequestMenu">
                <MenuItem Header="Remove">
                    <MenuItem.Style>
                        <Style TargetType="{x:Type MenuItem}">
                            <Setter Property="cal:Message.Attach" 
                                            Value="[Event Click] = [Action RemoveItem($executionContext)]" />

                            <Setter Property="cal:Action.TargetWithoutContext"
                                            Value="{Binding Path=PlacementTarget, 
                                                            RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"/>
                        </Style>
                    </MenuItem.Style>
                </MenuItem>
            </ContextMenu>
        </Grid.Resources>
        <UniformGrid Rows="1">
            <TreeView x:Name="Items" BorderThickness="0">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:ItemTreeItemViewModel}"  ItemsSource="{Binding Children, UpdateSourceTrigger=PropertyChanged}">
                        <Label x:Name="TheLabel" Content="{Binding Path=Item.DisplayName}" ContextMenu="{StaticResource RequestMenu}" >
                        </Label>
                    </HierarchicalDataTemplate >
                    <DataTemplate DataType="{x:Type local:SubItemTreeItemViewModel}" >
                        <Label x:Name="TheLabel" Content="{Binding Path=SubItem.DisplayName}"  />
                    </DataTemplate>
                </TreeView.Resources>
                <TreeView.ItemContainerStyle>
                    <!-- This Style binds a TreeViewItem to a TreeViewItemViewModel. -->
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                        <Setter Property="ItemsSource" Value="{Binding Children}" />
                        <Setter Property="FontWeight" Value="Normal" />
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="FontWeight" Value="Bold" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </TreeView.ItemContainerStyle>
            </TreeView>
            <StackPanel>
                <Button x:Name="AddItem" Content="Add Item" />
            </StackPanel>
        </UniformGrid>
    </Grid>

May 16, 2011 at 4:55 PM

Still having a problem with this and I cannot figure it out =(.. Any help would be greatly appreciated. Here's a link to the sample application. To repro the issue you must click AddItem.. then right click the added item and remove it.. Then add it again.. and try to remove it again. you'll see that the Remove method that is getting called is bound to the item previously removed..I'm not sure what is going on. I thought it was the "bound" selected value after the remove, but i used some reflection trickery to unbind that and i still end up with the old viewModel.

link:

https://cid-3beb862062439095.office.live.com/self.aspx/Public/TreeViewSampleApp.zip

Thanks

May 16, 2011 at 5:18 PM

There is an earlier discussion about datagrid and virtualization returning old viewModels.  Perhaps this is the same scenario with your treeview control.

jack

May 16, 2011 at 5:25 PM

Thanks I'll look into that =)

May 16, 2011 at 5:59 PM

I took a look, but it doesn't appear to be the problem. Any other suggestions?

May 20, 2011 at 2:31 AM

Ok well i finally getting some time to dig through properties while debugging =)..Setting the TargetwithDataContext gets you to the viewmodel of the item type bound to the TreeView using the placement target.. However, it doesn't always take you to the correct instance of the ViewModel (related to the virtulization issue I think, searching the datagrid problem). However, if you dig in the properties of the message you can get the real TreeViewItemViewModel out digging through properties provided you pass the full executionContext.   Hopefully this is useful for someone else ;)

 

 var realVM = (((executionContext as Caliburn.Micro.ActionExecutionContext).EventArgs as System.Windows.RoutedEventArgs).Source as System.Windows.Controls.MenuItem).DataContext as TreeViewItemViewModel;