将数据绑定到Windows窗体TabControl

13

我正在尝试我的第一个Windows窗体项目,之前一直是基于Web的,现在遇到了一些问题。我想将一个对象列表绑定到TabControl上,然后创建选项卡,并且希望从每个选项卡的单击事件中访问数据绑定的值。

我想要绑定的对象是:

public class TreeNodeItem
{
    private NTree<string> node;

    public TreeNodeItem(NTree<string> node)
    {
        this.node = node;
    }

    public string Value
    {
        get { return this.node.data; }
    }
}

NTree节点表示树形数据结构中的节点。我想为列表中的每个对象创建一个选项卡,将Value属性绑定到Tab Text属性。其他帖子提到绑定到控件的ItemsSource属性,但Visual Studio没有给我这个属性。

非常感谢任何帮助。

干杯

斯图尔特


2
每当我遇到这种情况,我要么构建一个自定义控件(在我的5年开发工作中,我还没有看到过一个开箱即用的),要么找另一种方式来表示数据。例如,treeList控件。 - Marty
如果你使用的是WPF而不是winforms,那么这是世界上最容易做的事情...。如果你对winforms没有特别的需求,我建议使用WPF。我经常使用我的视图模型来进行数据绑定到选项卡和选项卡页内容。如果可以使用WPF,我可以提供一个示例。 - Nathan A
@NathanA 我知道,如果我使用WPF会很容易解决这个问题,但是我必须使用WinForms。 - Rachel
@Rachel 我想这一切归结于“winforms思维方式”(与我们经常谈论的“WPF思维方式”相对比)。winforms并不真正支持数据绑定,它没有被设计为这样,因此你需要手动完成所有这些工作:S - Federico Berasategui
1个回答

2

好的,我不知道绑定是必须的。虽然我从未在Windows表单应用程序中看到过这样的操作,但我决定创建一个类来为我们完成这项工作。

它使用ObservableCollection<T>来跟踪其列表中的对象/属性是否已更改。

    public class ObservableList<T> : ObservableCollection<T>
    {
        public ObservableList() : base()
        {
            CollectionChanged += new NotifyCollectionChangedEventHandler(nObservableCollection_CollectionChanged);
        }

        public event PropertyChangedEventHandler OnPropertyChanged;

        void nObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (OnPropertyChanged != null)
            {
                OnPropertyChanged(new object[] { e.OldItems, e.NewItems }, null); // Call method to let it change the tabpages
            }
        }
    }

现在,我们需要创建一个帮助类来帮助我们跟踪:
    public class TabControlBind
    {
        public TabControlBind(TabControl tabControl)
        {
            // Create a new TabPageCollection and bind it to our tabcontrol
            this._tabPages = new TabControl.TabPageCollection(tabControl);
        }

        // Fields
        private ObservableList<TreeNodeItem> _treeNodeItems;
        private TabControl.TabPageCollection _tabPages;

        // Properties
        public ObservableList<TreeNodeItem> TreeNodeItems
        {
            get { return _treeNodeItems; }
            set
            {
                if (_treeNodeItems != value)
                {
                    _treeNodeItems = value;
                    _treeNodeItems.OnPropertyChanged += OnPropretyChanged;
                    OnPropretyChanged(null, null);
                }
            }
        }

        public TabControl.TabPageCollection TabPages
        {
            get
            {
                return this._tabPages;
            }
        }

        // Events
        private void OnPropretyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender == null) // If list got set
            {
                // Remove existing tabpages
                this._tabPages.Clear();

                // Loop through all items inside the ObservableList object and add them to the Tabpage
                foreach (TreeNodeItem _treeNodeItem in this._treeNodeItems)
                {
                    TabPage tabPage = new TabPage() { Text = _treeNodeItem.Value, Tag = _treeNodeItems };
                    this._tabPages.Add(tabPage);
                }
            }
            else if (sender is object[]) // If only one (or multiple) objects have been changed
            {
                // Get OldItems and NewItems
                object[] changedItems = (object[])sender;
                // Remove OldItems
                if (changedItems[0] != null)
                {
                    foreach (dynamic oldItems in (IList)changedItems[0])
                    {
                        foreach (TabPage tab in this._tabPages)
                        {
                            if (tab.Text == oldItems.Value)
                            {
                                this._tabPages.Remove(tab);
                                break;
                            }
                        }

                    }
                }
                // Add OldItems
                if (changedItems[1] != null)
                {
                    foreach (dynamic newItems in (IList)changedItems[1])
                    {
                        TabPage tabPage = new TabPage() { Text = newItems.Value, Tag = newItems };
                        this._tabPages.Add(tabPage);
                    }
                }
            }
        }
    }

这是一个关于如何使用它的示例:

    TabControlBind tabControlBinder;
    ObservableList<TreeNodeItem> treeNodeItems;

    private void btnAdd_Click(object sender, EventArgs e)
    {
        // This will automatically update the TabControl
        treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test3" }));
    }

    private void frmMain_Load(object sender, EventArgs e)
    {
        // Create a new list object an add items to it
        treeNodeItems = new ObservableList<TreeNodeItem>();
        treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test" }));
        treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test2" }));

        // Create a new instance of the TabControlBind class, set it to our TabControl
        tabControlBinder = new TabControlBind(tabControl);
        tabControlBinder.TreeNodeItems = treeNodeItems;
    }

1
这个问题正在寻找一种将TabPages集合绑定到分层对象集合并使它们自动生成和自动更新的方法,而不是像这样在代码后台手动创建选项卡页。 - Rachel
请再看一下我的回答。 - Jevgeni Geurtsen
感谢更新。我已经取消了我的踩赞,并将在稍后进行测试以查看是否有效。我曾希望有一种简单的方法来完成这个任务,而不需要自定义控件,但看起来这不可能实现。谢谢 :) - Rachel
我也试图寻找更简单的方法,但似乎并没有。我很想听听你是否成功了! - Jevgeni Geurtsen
谢谢,我会去看一下 :) Tab 标头是直接设置的,而不是数据绑定,因此如果我将标签的名称属性从 "New Tab" 更改为 "Tab #3",则 UI 中的更改不会生效。不过,通过使用数据绑定而不是设置属性,这可能很容易修复。 - Rachel
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接