Download source files - here
Download bin files - here
The best way for use child/children in WPF is customize any control with property for child/children that inherit from Control (for example ContentControl or ItemsControl). Control has wide possibility of customizing through ControlTemplate. But sometime we want to have a specific behaviour of child/children (for example if we want to implement MDI container any other specific container). What should you do for implementing this behaviour? I will show major steps in sample of simple custom panel that is inherited from FrameworkElement.
- First of all you should add property according type that will be contain child/children. I use Children property in my sample. It is list of UIElement. I use ObservableCollection
because I need to react to collection change.
- You should see to show child/children in your control, you should override int VisualChildrenCount and Visual GetVisualChild( int). These method and property should get value accordingly with child/children property (child/children's count and this/these element/s according to index).
- Your app can correct build now but still nothing shows in app when it is run. It is happened because child/children doesn't/don't know their size and position on parent. You should override Size MeasureOverride(Size) for pass size (and calculate DesiredSize in child) to child's element and Size ArrangeOverride(Size) for pass position on parent. When you run app again you can see child/children in your custom control. You should point out to sizes that are returned in these method, MeasureOverride's size says to parent DesiredSize of your custom control and ArrangeOverride says to parent square that were taken.
- Now you can see that your custom control is run correct, but when you try to interact with their child/children you can see that interact is fail. It is happened because child/children property isn't/aren't added to visual tree of your custom control. AddVisualChild(Visual) method helps you in this. The best way is realized this behavior in property changed method for child or collection changed method for children. Pay attention that property changed isn't called for default value for your child/children property. We have some other reason set null as default value, but I say below about it.
- If you test your custom control more careful you can observe that interaction isn't complete. Yeah, visual interaction you have already implemented, but logical isn't complete. How can you observe it? Easy, try to use attached and inherit properties or DataContext - they don't work. You should add child/children to logical tree of your custom control. AddLogicalChild(Object) helps. You can call this method together with AddVisualChild.
- Be careful, if you want to remove element from child/children you shouldn't remember that this element should be removed from visual tree and logical tree. RemoveVisualChild(Visual) and RemoveLogicalChild(Object) help you in this.
- Interaction works correct now, but you can see new trouble, if you add/remove child/children in run-time you can't see any visual effect until do any change of size in app. It is happen because your custom control knows nothing about this add/remove action. If you are implementing child you can add AffectsArrange and AffectsMeasure flag to metadata of child property, they mean that custom control should do arrange and measure after each property change. If you are implementing children you should call InvalidateVisual() after each collection change. Added/Removed item shows/disappears immediately now.
- If you want set child/children property through XAML you should explicitly set your child/children property. You can apply to your custom control attribute ContentPropertyAttribute. So you can implicitly set your child/children property.
- In point #4 I have promised to say why you can't set default value no null. Pay attention that Dependency property is static member of class, so refer of one default value is added to all instances of your custom class. So you should use type that inherit form ValueType as type of dependency property or reinitialize default value in constructor (You should chose second way:).
- My coworker have found interesting trouble with RightToLeft support. His custom control can correct support RightToLeft only after he overrides LogicalChildren property. I can't reproduce it, but want to inform about possible trouble and solution for it.