I finally found a workable way around it, but there's a big caveat which I put at the end of this answer. Here's my CustomAttachManager:
public class CustomAttachManager : DependencyObject
{
#region Object CustomAttachManager.Attached = null
public static IAttachedObject GetAttached(DependencyObject obj) { return (IAttachedObject)obj.GetValue(AttachedProperty); }
public static void SetAttached(DependencyObject obj, IAttachedObject value) { obj.SetValue(AttachedProperty, value); }
public static readonly DependencyProperty AttachedProperty =
DependencyProperty.RegisterAttached("Attached", typeof(IAttachedObject), typeof(CustomAttachManager), new PropertyMetadata(null, StaticHandleAttachedChanged));
static void StaticHandleAttachedChanged(DependencyObject self, DependencyPropertyChangedEventArgs args)
{
var ov = (IAttachedObject)args.OldValue;
var nv = (IAttachedObject)args.NewValue;
if (ov != null) ov.Detach();
if (nv != null) nv.Attach(self);
}
#endregion
}
You can then use it to attach behaviors like this:
<my:CustomAttachManager.Attached>
<my:RedBackgroundBehavior></my:RedBackgroundBehavior>
</my:CustomAttachManager.Attached>
My example behavior changes the background of a panel to red, which is visible in the designer.
The remaining problem is the case of multiple behaviors. The best solution I could come up with is a proxy:
[ContentProperty("Children")]
public class MultipleBehavior : Behavior<DependencyObject>
{
DependencyObjectCollection<IAttachedObject> children = new DependencyObjectCollection<IAttachedObject>();
public DependencyObjectCollection<IAttachedObject> Children { get { return children; } }
protected override void OnAttached()
{
foreach (var child in Children) child.Attach(AssociatedObject);
}
protected override void OnDetaching()
{
foreach (var child in Children) child.Detach();
}
}
which can be used this way:
<my:CustomAttachManager.Attached>
<my:MultipleBehavior>
<my:RedBackgroundBehavior />
<my:SupriseBehavior />
</my:MultipleBehavior>
</my:CustomAttachManager.Attached>
The proxy has a defect in that it won't properly handle behaviors added after something already got attached, but that is not going to happen in the classic use case.
The caveat is that I don't know a way to make Blend display the behaviors in the object tree. The "AlternateContentProperty" can't be used because it doesn't work for attached properties, which is what CustomAttachManager uses.
I will update this answer should I find an answer to this problem.