C# – Refreshing UI with databind in WPF

cdata-bindingwpf

I have a 3 layer deep treeview,

-MAIN
 ->:SUB1
   >:SUB2
   >:SUB2
 -X:SUB1
   X:SUB2
 SUB1
 SUB1

where, > and X represent graphics denoting the status of that specific item (determined from backend).

I'm using an Observable Dictionary to bind to this tree (and it has an ICollectionChanged event). The structure is like this:


ObservableDictionary<string,CustomClass> mainitems;

public class CustomClass{
    ObservableDictionary<string, InnerClass> sub1item;
    // Bunch of properties and methods in this class
    // INotify not implemented
}

public class InnerClass{
    // Bunch of properties and methods in this class
    // INotify not implemented
    public SomeEnum Status{
        get{ return this.status; }
    }
}

The graphics, mentioned above, are binded using a custom converter which converts the Status enum to a path so that it can be binded (ie. <img source="{Binding Path=something, Converter={StaticResource someconverter}, Mode=OneWay" /> ).

QUESTION:

My problem is, when I update the CustomClass's sub1item dictionary with new statuses, it doesn't update it in the UI. I think implementing INotify stuff might work but I don't know where I need to update it and exactly how to do so.

Edit:
My XAML template for the treeview is as follows:


<TreeView Name="tvInstance" ItemsSource="{Binding}" TreeViewItem.Selected="tviSelected" IsTextSearchEnabled="True">
    <TreeView.ItemContainerStyle>
        <Style>
            <Setter Property="TreeViewItem.IsExpanded" Value="{Binding Path=Value.Expanded, Mode=TwoWay}" />
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Value.CustomClass}" ItemContainerStyle="{x:Null}">
                <StackPanel Orientation="Horizontal">
                <Label Content="{Binding Path=Key}"/>
            </StackPanel>
            <HierarchicalDataTemplate.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=Value.AnotherClass}">
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Path=Value.Status, Converter={StaticResource convertstatus} }"
                            Width="10" Height="10"/>
                        <Label Content="{Binding Path=Key}" />
                    </StackPanel>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{Binding Path=Value, Converter={StaticResource convertstatus} }"
                            Width="10" Height="10"/>
                                <Label Content="{Binding Path=Key}" />
                            </StackPanel>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

EDIT: After adding all INotifyProperty events in my mainclass, my CustomClass, and my InnerClass, it still doesn't work. I'm using the Dr. WPF version of ObservableDictionary (and using a dictionary is crucial to my application since I need to do lots of lookups). Help!

Epilogue

The answers in this page are correct in that INotifyPropertyChanged needs to be implemented on properties I want updated in the UI.

I found that binding the dictionary was too much trouble so I kept both an ObservableCollection and a Dictionary. I used the dictionary for lookup and the collection for binding (since both use the same reference to the object, removing was easy with the collection and the only O(n) operation).

With regards to updating in the UI, please refer to the other posts on this page.

Best Answer

This may be a little long, here would be my best guess:

public class CustomClass : INotifyPropertyChanged
{
  public CustomClass()
  {
    sub1item = new ObservableDictionary<string, InnerClass>();
    // This next line may not be necessary... Changes might propogate up.
    sub1item.CollectionChanged += () => NotifyPropertyChange("Sub1Item");
  }

  private ObservableDictionary<string, InnerClass> sub1item;
  public ObservableDictionary<string, InnerClass> Sub1Item
  {
    get { return sub1item; }
    private set { sub1item = value; NotifyPropertyChange("Sub1Item"); }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void NotifyPropertyChanged(String info)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
  }
}

public class InnerClass : INotifyPropertyChanged
{
  public SomeEnum Status
  {
    get { return this.status; }
    private set { this.status = value; NotifyPropertyChange("Status"); }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void NotifyPropertyChanged(String info)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
  }
}

Just make sure you update your status by calling Status = something, and not directly through this.status

Edit: If you're just looking to ONLY update the single object that got the updated status, I'm not sure that this will do it. I suspect this will signal that Sub1Item changed, but mainitems will likely not know about the individual object. It depends on your implementation.

If you created a DataTemplate for CustomClass, which had a binding to Sub1Item, then your binding will properly update for only the updated status

<DataTemplate DataType="{x:Type myClrNamespace:InnerClass}">
    <Grid>
        <TextBlock Text={Binding Path=Status}/>
    </Grid>
</DataTemplate>
...
<ListBox x:Name="listStatus"/>

Then in the C# somewhere, you could have: listStatus = mainlist[0].Sub1Item; After seeing your example of your TreeView ItemTemplate though, I'm not sure anymore.

Related Topic