WPF将3个集合绑定在一起

本文关键字:绑定 在一起 集合 3个 WPF | 更新日期: 2023-09-27 18:14:13

我有一个MVVM应用程序,我想将3个集合绑定在一起。在视图中,我有ItemsControl与TimeBoxes(只是TextBoxes与依赖属性时间)。

<Window x:Class="Scoreboard.View.MainWindow"
    ...
    <ItemsControl ItemsSource="{Binding TimeBoxes}"/>
    ...
</Window>

后面有代码

public class Mainwindow
{
    //...
    var Timeboxes = new ObservableCollection<TimeBox>();
}

在模型中,我想有一个时间集合。

public class GameModel
{
    var Times = new ObservableCollection<Time>();
    // Don't know if this is how it should be
}

然后我有另一个窗口的输出类似于视图主窗口,但ItemsControl保存边界而不是时间框。

<Window x:Class="Scoreboard.Display.DisplayWindow"
    ...
    <ItemsControl ItemsSource="{Binding Borders}"/>
    ...
</Window>

它应该做的是:当点击视图(MainWindow)中的按钮时,TimeBox将在TimeBoxes集合中创建。TimeBox中的TimeGameModel中绑定到Times中的新Time。并且Time也绑定到输出(显示)WindowBorders中的新Border(我为此使用TimeToStringConverter)的内容(标签)。当GameModel中的Time达到零时,它的实例将从所有集合中删除。我的问题是我不知道如何将一个集合中的一个项目绑定到另一个集合的一个项目。为简单起见,省略了ViewModel。

总而言之,我想动态绑定TimeBoxTime, TimeBorder的内容以1:1:1的比例

WPF将3个集合绑定在一起

这里有一个CollectionHelper的测试解决方案,它将两个ObservableCollection绑定在一起。当从一个集合中添加或删除项时,另一个集合也会更新。Bind方法返回一个IDisposable,因此当您处置它时,自动更新将终止。这适用于具有相同泛型类型的2个集合。如果您需要一个方法来处理不同类型的集合,您应该实现一个带有签名的方法,如注释的方法:

[TestClass]
public class BindTwoObservableCollections_test
{
    [TestMethod]
    public void BindTwoObservableCollections()
    {
        var c1 = new ObservableCollection<int>();
        var c2 = new ObservableCollection<int>();
        c1.Add(1);
        Assert.AreEqual(0, c2.Count);
        var subscription = CollectionHelper.Bind(c1, c2);
        c1.Add(2);
        Assert.AreEqual(1, c2.Count);
        Assert.AreEqual(2, c2[0]);
        c2.Add(3);
        Assert.AreEqual(3, c1.Count);
        Assert.AreEqual(3, c1[2]);
        c2.Remove(2);
        Assert.AreEqual(2, c1.Count);
        subscription.Dispose();
        c2.Remove(3);
        Assert.AreEqual(2, c1.Count);
    }
}
public static class CollectionHelper
{
    public static IDisposable Bind<T>(
        ObservableCollection<T> c1,
        ObservableCollection<T> c2)
    {
        var fromC1Subscription = InternalBind(c1, c2);
        var fromC2Subscription = InternalBind(c2, c1);
        return new Disposable(() =>
        {
            fromC1Subscription?.Dispose();
            fromC2Subscription?.Dispose();
        });
    }
    private static IDisposable InternalBind<T>(
        ObservableCollection<T> from,
        ObservableCollection<T> to)
    {
        NotifyCollectionChangedEventHandler onFromChanged =
            (s, e) =>
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (T added in e.NewItems)
                            if (!to.Contains(added))
                                to.Add(added);
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        foreach (T removed in e.OldItems)
                            to.Remove(removed);
                        break;
                    //other cases...
                    default:
                        break;
                }
            };
        from.CollectionChanged += onFromChanged;
        return new Disposable(() => { from.CollectionChanged -= onFromChanged; });
    }
    //public static IDisposable Bind<T1, T2>(
    //    ObservableCollection<T1> c1,
    //    ObservableCollection<T2> c2,
    //    Func<T1, T2> converter1,
    //    Func<T2, T1> converter2)
    //{
    //    todo...
    //}
}
public class Disposable : IDisposable
{
    public Disposable(Action onDispose)
    {
        _onDispose = onDispose;
    }
    public void Dispose()
    {
        _onDispose?.Invoke();
    }
    private Action _onDispose;
}

显然,如果您需要绑定c1, c2和c3,您可以这样写:

CollectionHelper.Bind(c1, c2);
CollectionHelper.Bind(c2, c3);

这就够了