当标记的对象发生变化时,如何更新 WinForms TreeView?

How can I have a WinForms TreeView update when the tagged objects change?

我有一个 WinForms TreeView 来表示我的计算器数据。当我读入我的数据时,我创建了我的 TreeView 并用相应的对象标记每个 TreeView 节点。下面是一个示例 TreeView。

SUM
    SUM
        TIMES
            UnitsSold
            UnitPrice
        IncomeFromContracts
    NEGATIVE SUM
        Rent
        Wages
        TIMES
            UnitsSold
            UnitMaterialCost

我从如下数据结构创建 TreeView:

class Operand {
    bool IsNegated {get; set;}
}

class Operator : Operand {
    enum OperatorEnum { SUM, DIFFERENCE, TIMES }
    OperatorEnum MyEnum {get; set;}
    IList<Operand> Children {get; set;}
}

class VariableName : Operand {
    string Name {get; set;}
}

每个 TreeNode 都标记有相应的操作数。当 TreeView 中的项目被 selected 时,标记的项目会显示在面板中,以便您可以更改其属性。例如。当我 select 一个项目时,您会看到一个复选框,您可以在其中设置 IsNegated 值。我已成功将该控件绑定到 Operand 对象,使用:

negateCheckBox.DataBindings.Add("Checked", requirementTree.SelectedNode.Tag, "IsNegated");

单击复选框会适当地更新基础数据,但不会导致 TreeView 刷新其数据,因此即使项目的 ToString() 方法的 return 值已更改(它现在已"NEGATIVE" 前缀),因为 TreeNode 已经用该字符串创建,它不会更新。

我还有一些 "Add" 和 "Remove" 按钮,它们可以在 TreeView 中创建额外的节点并删除它们,但这些都是手动运行的——更新底层数据结构,然后重建整个 TreeView (这会导致视图刷新,因此我可以验证我的其他绑定是否正常工作)。

我需要采取什么步骤才能使 TreeView 不是手动填充,而是完全绑定到我的数据结构,以便立即反映此类更改?在 WinForms 中绑定数据的方法似乎有很多种,我不清楚哪种方法适用于哪种情况。

非常感谢您的帮助!

根据 Reza 关于 TreeView 不支持数据绑定的评论,我想出了一个合理的解决方法。

每个 "model" class 实现 INotifyPropertyChanged 并在其任何属性更改时引发事件。

class Operand : INotifyPropertyChanged
{
    public bool IsNegated
    {
        get { return m_isNegated; }
        set { m_isNegated = value; RaisePropertyChanged(); }
    }
    private bool m_isNegated = false;

    private void RaisePropertyChanged([CallerMemberName]string prop = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

然后我添加了一个虚拟控制器,它将模型链接到 TreeView 节点:

public class OperandTreeNodeController

{
    private Requirement m_model;
    private TreeNode m_view;

    public OperandTreeNodeController(Requirement model, TreeNode view)
    {
        m_model = model;
        m_view = view;

        m_model.PropertyChanged += Model_PropertyChanged;
    }

    private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        m_view.Text = m_model.ToString();
    }
}

现在,每当我创建一个 TreeNode 时,我也会创建一个控制器。这里有一点内存 "leak" 因为它们可以继续创建并附加到模型的 PropertyChanged 事件,垃圾收集器可能永远不会清理它们,但我在我的代码中通过添加 Destroy( ) 函数并手动 tracking/destroying 它们。

这似乎实现了我的大部分目标,所以除非有人有其他想法,否则我会坚持下去。