简体   繁体   中英

WPF TreeView Binding not updating the View

INotifyPropertyChanged implemented, DataContext set.

Here is my TreeView:

    <TreeView ItemContainerStyle="{StaticResource MaterialDesignTreeViewItemExtended}" x:Name="TestCasesTree" MinWidth="220" ItemsSource="{Binding TestCases.Children, Mode=TwoWay}">
    <i:Interaction.Behaviors>
        <behaviours:BindableSelectedItemBehavior SelectedItem="{Binding SelectedTreeViewItem, Mode=TwoWay}" />
    </i:Interaction.Behaviors>
    <TreeView.Resources>
        <con:TestAssessmentToImagePathConverter x:Key="TestAssessmentConverter" />
    </TreeView.Resources>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children, Mode=TwoWay}" DataType="{x:Type data:TestCaseNode}">
            <StackPanel Margin="0" Orientation="Horizontal">
                <Image Source="{Binding TestAssessment, Converter={StaticResource TestAssessmentConverter}}" RenderOptions.BitmapScalingMode="HighQuality" Width="20" Height="20"/>
                <TextBlock Margin="8 2 0 0" Text="{Binding Name}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Binding:

ItemsSource="{Binding TestCases.Children, Mode=TwoWay}"

BindableBase class:

public abstract class BindableBase : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected bool CanExecute
    {
        get
        {
            return true;
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Property:

    public TestCaseNode TestCases
    {
        get { return mainWindowModel.TestCases; }
        set
        {
            mainWindowModel.TestCases = value;
            SetProperty(ref _testCases, value);
        }
    }

Children:

public ObservableCollection<TestCaseNode> Children { get; set; }

The place where i edit something:

 SelectedTreeViewItem.SetTestAssessmentForCluster(testAssessment);
 OnPropertyChanged("Children");   
 OnPropertyChanged("TestCases");
 OnPropertyChanged(nameof(TestCases));
 OnPropertyChanged(nameof(TestCases.Children));

Nothing changes in View (but when i debug it in Visual Studio, i clearly see that the object changes)

This works, but it crashes another things:

SelectedTreeViewItem.SetTestAssessmentForCluster(testAssessment);
var tc = TestCases;
TestCases = null;
TestCases = tc;

UPD:

TestCaseNode Class:

public class TestCaseNode : TestCase, IEquatable<TestCaseNode>, IComparable<TestCaseNode>
{
    public TestCaseNode Parent { get; set; }
    public ObservableCollection<TestCaseNode> Children { get; set; }

    public void Add(TestCaseNode node) {...}
    public void SetTestAssessmentForCluster(TestAssessment testAssessment, ref TestScores _testScores) {...}
    public bool Equals(TestCaseNode other) {...}
    public override bool Equals(object obj) {...}
    public override int GetHashCode() {...}
    public int CompareTo(TestCaseNode other) {...}
}

TestCase Class:

public class TestCase
{
    public string Id { get; set; } = string.Empty;
    public string Name { get; set; }
    public string Description { get; set; }
    public ObservableCollection<TestStep> TestSteps { get; set; }
    public TestAssessment TestAssessment { get; set; } = TestAssessment.EMPTY;
}

The OnPropertyChanged("TestCases"); must be issued by the object that includes the property that changed. In your case, your class TestCaseNode will need to call the OnPropertyChanged method in order for the Treeview to recognize the change to the collection because that object holds the collection. That is, it is not simply some any property named "TestCases" that has changed but the property in a specific object. The reason: you could have more than one properties with this name in different classes. And, if there are multiple copies of the object, they each have a property with the same name.

What I have done, and this has worked for me, is add a public method within the TestCaseNode called UpdateProperties() that then calls OnPropertyChanged("TestCases"); This ensures that the property update is issued by the correct object. Then I call this method when I need to have the control updated.

There are several other ways to do the same thing but I find this a simple and direct approach. You could, for example, include `OnPropertyChanged("TestCases"); in your add method. I have not done this because 1) the OnPropertyChanged call occurs too often and 2) without the OnPropertyChanged call I can defer the update to the control until after I've made several or all of my Add's.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM