WPF - Привязка свойства зависимости ObservableCollection в UserControl

Я имею контроль

класс DragGrid: Сетка {...}

который наследовался исходной сетке и позволяет перетащить и изменить размер ее дочерних элементов. Я должен связать пользовательский названный DP WorkItemsProperty к заметному набору типа WorkItem (реализации INotifyPropertyChanged). Каждый элемент в сетке связывается с объектом набора.

Каждый раз, когда пользователь добавляет новый объект динамично во времени выполнения (объекты не могут быть объявлены в XAML!), или удаляет объект из того набора, WorkItems DP на DragGrid должен быть обновлен, и дети в сетке (где каждый ребенок представляет a WorkItem объект набора).

Мой вопрос состоит в том, как DP уведомляет управление, о котором дочерний элемент в сетке должен быть удален, изменен ('изменение' означает, что пользователь перетащил элемент или изменил размер его с мышью), или добавил, и как я определю, какой из существующих детей тот, который потребности состоят в том, чтобы быть удалены или изменены. Я понимаю, что это - то, где DependencyPropertyChangedCallback входит. Но это только называют, когда свойство DP установлено снова, не, когда что-то в изменениях набора (как добавляют, удалите объект). Таким образом в конце, делает DragGrid управляйте так или иначе должен подписаться на событие CollectionChanged? В какой точке я поднял бы трубку обработчик событий для этого?

*РЕДАКТИРОВАНИЕ:: причина использования Сетки во-первых состоит в том, потому что я хочу смочь поддержать минимальную дельту для того, когда пользователь перетаскивает или изменяет размер управления в Сетке. Управление представляет отрезок времени, и каждый столбец сетки представляет 15 минут (который является минимальным значением). Выполнение этого в Холсте с Ползунками было трудным и ошибочным. Реализация DragGrid решила мои проблемы взаимодействия с пользователем. Кроме того, Холст не масштабируем, таким образом, отрезки времени имели бы к перерасчетному все время. С Сеткой у меня нет проблемы, потому что столбцы определяют мне время, неважно, размер. **

7
задан Adi Lester 19 November 2013 в 10:03
поделиться

2 ответа

В ответ на ваш вопрос:

​​Вы должны добавить обработчик 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 , чтобы узнать больше.

17
ответ дан 6 December 2019 в 10:48
поделиться

Извините, у меня нет решения вашей конкретной проблемы 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 . Но это уже другая история ...

Теперь вам решать, можете ли вы еще перейти к этому подходу или ваша Грид почти закончена, и вы не желаете меняться. Я знаю, что это сложно, но, на мой взгляд, описанный выше подход чище (и проще!)!

1
ответ дан 6 December 2019 в 10:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: