【问题标题】:WPF Binding 3 collections togetherWPF 将 3 个集合绑定在一起
【发布时间】:2016-10-20 14:45:31
【问题描述】:

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

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

后面有代码

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

在模型中我想要一个 Time 集合。

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

然后我有另一个窗口,其输出类似于 Views MainWindow,但 ItemsControl 包含边框而不是 TimeBoxes。

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

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

总而言之,我想以 1:1:1 的比例将 TimeBox 动态绑定到 TimeTimeBorder 的内容。

【问题讨论】:

    标签: c# wpf xaml mvvm binding


    【解决方案1】:

    这里你有一个CollectionHelper 的测试解决方案,它将 2 个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);
    

    这就够了。

    【讨论】:

    • 谢谢,这是一个很好的解决方案。我太专注于绑定,以至于忘记了通知:D
    • “我知道那种感觉,兄弟”;)
    猜你喜欢
    • 1970-01-01
    • 2013-11-27
    • 1970-01-01
    • 2020-05-07
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多