Я имею контроль
класс DragGrid: Сетка {...}
который наследовался исходной сетке и позволяет перетащить и изменить размер ее дочерних элементов. Я должен связать пользовательский названный DP WorkItemsProperty
к заметному набору типа WorkItem
(реализации INotifyPropertyChanged
). Каждый элемент в сетке связывается с объектом набора.
Каждый раз, когда пользователь добавляет новый объект динамично во времени выполнения (объекты не могут быть объявлены в XAML!), или удаляет объект из того набора, WorkItems
DP на DragGrid должен быть обновлен, и дети в сетке (где каждый ребенок представляет a WorkItem
объект набора).
Мой вопрос состоит в том, как DP уведомляет управление, о котором дочерний элемент в сетке должен быть удален, изменен ('изменение' означает, что пользователь перетащил элемент или изменил размер его с мышью), или добавил, и как я определю, какой из существующих детей тот, который потребности состоят в том, чтобы быть удалены или изменены. Я понимаю, что это - то, где DependencyPropertyChangedCallback входит. Но это только называют, когда свойство DP установлено снова, не, когда что-то в изменениях набора (как добавляют, удалите объект). Таким образом в конце, делает DragGrid
управляйте так или иначе должен подписаться на событие CollectionChanged? В какой точке я поднял бы трубку обработчик событий для этого?
*РЕДАКТИРОВАНИЕ:: причина использования Сетки во-первых состоит в том, потому что я хочу смочь поддержать минимальную дельту для того, когда пользователь перетаскивает или изменяет размер управления в Сетке. Управление представляет отрезок времени, и каждый столбец сетки представляет 15 минут (который является минимальным значением). Выполнение этого в Холсте с Ползунками было трудным и ошибочным. Реализация DragGrid решила мои проблемы взаимодействия с пользователем. Кроме того, Холст не масштабируем, таким образом, отрезки времени имели бы к перерасчетному все время. С Сеткой у меня нет проблемы, потому что столбцы определяют мне время, неважно, размер. **
В ответ на ваш вопрос:
Вы должны добавить обработчик DepencyPropertyChanged, как вы упомянули. В этом обработчике вы должны добавить обработчик событий к свойству CollectionChanged в новой коллекции и удалить обработчик из старой коллекции, например:
public ObservableCollection<WorkItem> WorkItems
{
get { return (ObservableCollection<WorkItem>)GetValue(WorkItemsProperty); }
set { SetValue(WorkItemsProperty, value); }
}
// Using a DependencyProperty as the backing store for WorkItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty WorkItemsProperty =
DependencyProperty.Register("WorkItems", typeof(ObservableCollection<WorkItem>), typeof(DragGrid), new FrameworkPropertyMetadata(null, OnWorkItemsChanged));
private static void OnWorkItemsChanged(object sender, DependencyPropertyChangedEventArgs e)
{
DragGrid me = sender as DragGrid;
var old = e.OldValue as ObservableCollection<WorkItem>;
if (old != null)
old.CollectionChanged -= me.OnWorkCollectionChanged;
var n = e.NewValue as ObservableCollection<WorkItem>;
if (n != null)
n.CollectionChanged += me.OnWorkCollectionChanged;
}
private void OnWorkCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
// Clear and update entire collection
}
if (e.NewItems != null)
{
foreach (WorkItem item in e.NewItems)
{
// Subscribe for changes on item
item.PropertyChanged += OnWorkItemChanged;
// Add item to internal collection
}
}
if (e.OldItems != null)
{
foreach (WorkItem item in e.OldItems)
{
// Unsubscribe for changes on item
item.PropertyChanged -= OnWorkItemChanged;
// Remove item from internal collection
}
}
}
private void OnWorkItemChanged(object sender, PropertyChangedEventArgs e)
{
// Modify existing item in internal collection
}
Как объяснил gehho, похоже, что вы используете класс Grid не так, как предполагалось изначально. , хотя вы, возможно, уже слишком далеко в разработке, чтобы захотеть начать все сначала. Классы, производные от Panel, на самом деле предназначены только для визуального рисования / упорядочивания своих дочерних элементов, а не для их манипулирования и улучшения. Ознакомьтесь с ItemsControl и WPF Content Model , чтобы узнать больше.
Извините, у меня нет решения вашей конкретной проблемы Grid
, но у меня есть только предложение, как вы могли бы сделать это проще ( и, я полагаю, как это подразумевают дизайнеры WPF). Фактически, Grid
не является элементом управления для размещения элементов . Это Панель
, которая упорядочивает Элементы управления
. Итак, я полагаю, это (одна из) причин, по которым у вас возникают проблемы с вашим решением.
Вместо этого я бы использовал ItemsControl
(например, ListBox
) с Canvas
как ItemsPanel
.
<ListBox ItemsSource="{Binding WorkItemsProperty}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Теперь вы определяете соответствующие свойства в своем классе WorkItem
(или WorkItemViewModel
), например XPos
и YPos
, которые будут привязаны к Canvas.Left
и Canvas.Верхние
свойства, подобные этому:
<Style x:Key="WorkItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding XPos, Mode=TwoWay}"/>
<Setter Property="Canvas.Top" Value="{Binding YPos, Mode=TwoWay}"/>
</Style>
Затем вы можете использовать этот стиль элемента, назначив свойство ItemContainerStyle
для ListBox
:
ItemContainerStyle="{StaticResource WorkItemStyle}"
Я не знаю, как реализовать перетаскивайте вещи, потому что я никогда этого не делал, но, очевидно, вы уже сделали это для своей пользовательской Grid
, поэтому не должно быть большой проблемой использовать его в ListBox
как хорошо. Однако, если вы обновите свойства своего WorkItem
, он должен автоматически изменить положение элемента. Кроме того, если вы добавляете / удаляете элемент в / из своей коллекции ( WorkItemsProperty
), он будет автоматически добавлен / удален, потому что ListBox
привязан к данным коллекции.
Возможно, вам придется изменить свой WorkItemStyle
в зависимости от вашего сценария. Например, если размер ListBox изменяется во время выполнения, вам может потребоваться установить позиции относительно размера контейнера (Canvas '). Следовательно, вам потребуется MultiBinding
вместо простого Binding
. Но это уже другая история ...
Теперь вам решать, можете ли вы еще перейти к этому подходу или ваша Грид
почти закончена, и вы не желаете меняться. Я знаю, что это сложно, но, на мой взгляд, описанный выше подход чище (и проще!)!