XAML中的VisualCollection和ContentPropertyAttribute

时间:2010-09-27 12:15:52

标签: wpf xaml

我想编写一个托管Visuals的自定义FrameworkElement。我的第一次尝试是创建一个ContainerVisual实例,并为ContainerVisual.Children编写一个包装器属性,然后将其设置为ContentProperty,以便我可以通过XAML和Visuals。但是VisualCollection只实现ICollection而不是IList或任何支持的接口,VisualCollection是selead所以我不能自己实现IList。

我如何主持视觉并让他们使用XAML以声明方式添加?

1 个答案:

答案 0 :(得分:0)

好的,很久以前,但这是我发现时间回来的解决方案......

收藏: 注意黑客评论。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Collections.ObjectModel;
using WPF.Controls.Primitives;

namespace WPF.Controls.Core
{
    public class PrimitiveCollection : ObservableCollection<Primitive>
    {
        protected PrimitiveContainerVisual _owner;

        public PrimitiveCollection(PrimitiveContainerVisual owner)
            : base()
        {
            if (owner == null)
                throw new ArgumentNullException("owner");

            _owner = owner;
        }

        protected override void ClearItems()
        {
            foreach (var item in this)
            {
                item.IsDirtyChanged -= new IsDirtyChangedHandler(item_IsDirtyChanged);
                _owner.InternalRemoveVisualChild(item);
            }

            base.ClearItems();
        }

        protected override void InsertItem(int index, Primitive item)
        {
            if (item != null && item.Parent != null)
                throw new ArgumentNullException("Visual has parent");

            item.IsDirtyChanged += new IsDirtyChangedHandler(item_IsDirtyChanged);
            _owner.InternalAddVisualChild(item);

            base.InsertItem(index, item);
        }

        protected override void RemoveItem(int index)
        {
            Primitive item = this[index];

            item.IsDirtyChanged -= new IsDirtyChangedHandler(item_IsDirtyChanged);
            _owner.InternalRemoveVisualChild(item);
            base.RemoveItem(index);
        }

        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
        }

        void item_IsDirtyChanged(object sender, PrimitiveChangedEventArgs e)
        {
            if(e.IsDirty)
                _owner.RequestRedraw();
        }
    }
}

您可以在XAML中使用的控件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using WPF.Controls.Primitives;
using System.Windows;
using System.Reflection;

namespace WPF.Controls.Core
{
    public class PrimitiveContainerVisual : Visual
    {
        private PrimitiveCollection _primitives;
        private PropertyInfo _contentBoundsPropInfo;
        private PropertyInfo _descendantBoundsPropInfo;

        public PrimitiveCollection Children
        {
            get { return _primitives; }
            set { _primitives = value; }
        }

        public Rect ContentBounds
        {
            // HACK access internal property of Visual
            get { return (Rect)_contentBoundsPropInfo.GetValue(this, null); }
        }

        public Rect DescendantBounds
        {
            // HACK access internal property of Visual
            get { return (Rect)_descendantBoundsPropInfo.GetValue(this, null); }
        }

        public PrimitiveContainerVisual()
        {
            _primitives = new PrimitiveCollection(this);
            Type thisType = this.GetType();
            BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
            _contentBoundsPropInfo = thisType.GetProperty("VisualContentBounds", flags);
            _descendantBoundsPropInfo = thisType.GetProperty("VisualDescendantBounds", flags);
        }

        internal void InternalAddVisualChild(Primitive prim)
        {
            this.AddVisualChild(prim);
        }

        internal void InternalRemoveVisualChild(Primitive prim)
        {
            this.RemoveVisualChild(prim);
        }

        public bool RequestRedraw()
        {
            UIElement uiParent = VisualParent as UIElement;

            if (uiParent != null)
            {
                uiParent.InvalidateVisual();
                return true;
            }
            else
                return false;
        }
    }
}