#c# #wpf #xaml #mvvm #binding
#c# #wpf #xaml #mvvm #привязка
Вопрос:
У меня есть приложение MVVM, в котором я хотел бы связать 3 коллекции вместе. В поле зрения у меня есть ItemsControl с временными блоками (просто текстовые поля со свойством зависимости Time).
<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
}
И тогда у меня есть другое окно с выводом, которое похоже на Views MainWindow, но ItemsControl содержит границы вместо временных рамок.
<Window x:Class="Scoreboard.Display.DisplayWindow"
...
<ItemsControl ItemsSource="{Binding Borders}"/>
...
</Window>
Что он должен делать: При нажатии на кнопку в представлении ( MainWindow
) в TimeBox
коллекции TimeBoxes
создается. Time
из этого TimeBox
привязывается к новому Time
в Times
GameModel
. И это Time
также привязано к содержимому (метке) нового Border
(у меня есть TimeToStringConverter
для этого) в Borders
выводе (дисплее) Window
. Когда Time
in GameModel
достигает нуля, его экземпляры удаляются из всех коллекций. Моя проблема в том, что я не знаю, как привязать элемент в коллекции к элементу другой коллекции. ViewModel опущен для простоты.
Подводя итог, я хочу динамически привязывать содержимое TimeBox
к Time
и это Time
к Border
в соотношении 1: 1: 1.
Ответ №1:
Здесь у вас есть проверенное решение для a, CollectionHelper
которое связывает 2 ObservableCollection
коллекции вместе. Когда элемент добавляется или удаляется из одной коллекции, другая обновляется. Bind
Метод возвращает IDisposable
, поэтому, когда вы удаляете его, автоматическое обновление прекращается. Это работает с 2 коллекциями одного и того же универсального типа. Если вам нужен метод для обработки коллекций разных типов, вам следует реализовать метод с сигнатурой, подобной методу commented:
[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);
И этого достаточно.
Комментарии:
1. Спасибо, чувак, это отличное решение. Я был настолько сосредоточен на привязке, что забыл об уведомлении: D
2. «Я знаю это чувство, братан» 😉