WPF - Binding an Observable Collection Dependency Property in UserControl

I have a control

DragGrid class: mesh {...}

which inherits from the original mesh and allows you to drag and resize its children. I need to bind a custom DP named WorkItemsProperty to an observable collection of type WorkItem (implements INotifyPropertyChanged ). Each grid item is attached to a collection item.

Each time a user dynamically adds a new element at run time (elements cannot be declared in XAML!) Or removes an element from this collection, you should update WorkItems DP to a DragGrid, and child elements in the grid (where each child element represents an element of the WorkItem collection )

My question is how does the DP notify the control about which child element in the grid should be deleted , changed ('change' means the user dragged the element or resized it with the mouse) or added , and how would I determine which of the existing subsidiaries is the one that needs to be removed or changed. I understand that this is where DependencyPropertyChangedCallback happens. But this is caused only when the DP property is set again, and not when something inside the collection changes (for example, add, delete an item). So, in the end, does the DragGrid to subscribe to the CollectionChanged event? At what point will I hook up an event handler for this?

* EDIT :: The reason for using Grid in the first place is because I want to be able to maintain a minimum delta when the user drags or resizes the control in the Grid. The control represents a time interval, and each grid column represents 15 minutes (which is the minimum value). Doing this on canvas using Thumbs was complicated and buggy. DragGrid implementation solved my user interaction problems. In addition, Canvas does not scale, so time intervals will need to be recounted all the time. I have no problem with the Grid, because the columns tell me the time regardless of size. **

+7
c # data-binding wpf dependency-properties observablecollection
source share
2 answers

In answer to your real question:

You should add a DepencyPropertyChanged handler, as you mentioned. In this handler, you must add the event handler to the CollectionChanged property in the new collection and remove the handler from the old collection, for example:

  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 } 

As gehho explained, it looks like you are not using the Grid class as originally intended, although you can go too far to start from now on. The classes obtained from Panel are actually intended only for visual drawing / organization of their children, and not for their manipulation and improvement. Check out the ItemsControl and WPF Content Model to learn more.

+16
source share

Sorry, I don’t have a solution to your specific user-specific Grid problem, but I only have a suggestion on how you could make this simpler (and, I suppose, how WPF designers understand this). In fact, the Grid not a control element. This is the Panel that organizes the Controls . So, I suppose this is (one of) the reason (s) why you are having problems with your solution.

Instead, I would use an ItemsControl (e.g. ListBox ) with a Canvas as an ItemsPanel .

 <ListBox ItemsSource="{Binding WorkItemsProperty}"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <Canvas IsItemsHost="True"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> 

Now you define the appropriate properties in the WorkItem class (or WorkItemViewModel ) of type XPos and YPos , which will be bound to the Canvas.Left and Canvas.Top as follows:

 <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> 

You can then use this element style by setting the ItemContainerStyle ListBox property:

 ItemContainerStyle="{StaticResource WorkItemStyle}" 

I don’t know how to implement drag and drop because I never did it, but obviously you already did it for your custom Grid , so it should not be a big problem to use it in a ListBox . However, if you update the properties of your WorkItem , it should automatically move the item. In addition, if you add / remove an item to / from your collection ( WorkItemsProperty ), it will be automatically added / removed, since the ListBox bound to the data in the collection.

You may need to modify your WorkItemStyle depending on your scenario. For example, if the ListBox changes at runtime, you may need to make a position relative to the size of the container (Canvas). Therefore, you will need MultiBinding instead of a simple Binding . But this other story ...

Now, this is your decision whether you can move on to this approach or your Grid almost done and you do not want to change. I know this is difficult, but in my eyes the above approach is cleaner (and easier!) One!

+1
source share

All Articles