代码之家  ›  专栏  ›  技术社区  ›  SledgeHammer

刷为从属属性=自动冻结?

  •  0
  • SledgeHammer  · 技术社区  · 6 年前

    <RadialGradientBrush x:Key="MyCoolBrush" MappingMode="Absolute" RadiusX="70" RadiusY="70">
        <RadialGradientBrush.GradientStops>
            <GradientStop Color="#FF000000" Offset="0" />
            <GradientStop Color="#00000000" Offset="0.6" />
        </RadialGradientBrush.GradientStops>
    </RadialGradientBrush>
    

    然后我有一个从属财产:

        public static readonly DependencyProperty MyCoolBrushProperty = DependencyProperty.Register(nameof(MyCoolBrush), typeof(Brush),
            typeof(MyCoolClass), new FrameworkPropertyMetadata(GetDefaultCoolBrush()));
    

        private static Brush GetDefaultCoolBrush()
        {
            Brush brush = Application.Current.TryFindResource("MyCoolBrush") as Brush;
    
            if (brush == null)
                return null;
    
            return brush.Clone();
        }
    

    我可以理解TryFindResource返回冻结的笔刷,因为它是在Xaml中定义的,因此,我返回它的克隆()。

    问题是,当我尝试(通过DP)对MyColBrush执行操作时,我得到一个异常,它是只读的。如果我尝试直接修改GetDefaultCoolBrush()的返回值,它就可以正常工作。

    为什么将笔刷设置为DP会冻结它?这是预期的吗?我想在某种程度上,如果有人将DP设置为黑色,则无法将其更改为绿色是有意义的。例如,为什么不直接传入一个新笔刷?但是GradialRadientBrush()安装起来有点贵,不是吗?实际上,我想做的是移动画笔,所以我不想继续重新创建它,我只想更新中心点。

    1 回复  |  直到 6 年前
        1
  •  2
  •   BionicCode    6 年前

    据我所知,这是因为freezable设计与DependencyObject基础设施紧密耦合,其行为与资源系统类似,原因相同。

    Loaded 实际实例值不再冻结,因为它们已耦合到此特定实例。

    下面是Freezable.cs中的一个片段。DependencyProperty在freezable上调用DependencyObject.Seal()时,会调用ISealable.Seal()的重写,从而导致实例冻结:

    /// <summary>
    /// Seal this freezable
    /// </summary>
    void ISealable.Seal()
    {
        Freeze();
     }
    

     private static void ValidateDefaultValueCommon(
            object defaultValue,
            Type propertyType,
            string propertyName,
            ValidateValueCallback validateValueCallback,
            bool checkThreadAffinity)
        {
            // Ensure default value is the correct type
            if (!IsValidType(defaultValue, propertyType))
            {
                throw new ArgumentException(SR.Get(SRID.DefaultValuePropertyTypeMismatch, propertyName));
            }
    
            // An Expression used as default value won't behave as expected since
            //  it doesn't get evaluated.  We explicitly fail it here.
            if (defaultValue is Expression )
            {
                throw new ArgumentException(SR.Get(SRID.DefaultValueMayNotBeExpression));
            }
    
            if (checkThreadAffinity)
            {
                // If the default value is a DispatcherObject with thread affinity
                // we cannot accept it as a default value. If it implements ISealable
                // we attempt to seal it; if not we throw  an exception. Types not
                // deriving from DispatcherObject are allowed - it is up to the user to
                // make any custom types free-threaded.
    
                DispatcherObject dispatcherObject = defaultValue as DispatcherObject;
    
                if (dispatcherObject != null && dispatcherObject.Dispatcher != null)
                {
                    // Try to make the DispatcherObject free-threaded if it's an
                    // ISealable.
    
                    ISealable valueAsISealable = dispatcherObject as ISealable;
    
                    if (valueAsISealable != null && valueAsISealable.CanSeal)
                    {
                        Invariant.Assert (!valueAsISealable.IsSealed,
                               "A Sealed ISealable must not have dispatcher affinity");
    
                        valueAsISealable.Seal();
    
                        Invariant.Assert(dispatcherObject.Dispatcher == null,
                            "ISealable.Seal() failed after ISealable.CanSeal returned true");
                    }
                    else
                    {
                        throw new ArgumentException(SR.Get(SRID.DefaultValueMustBeFreeThreaded, propertyName));
                    }
                }
            }
    

    在上面的代码中,您可以找到说明:

    我们不能接受它作为默认值。如果它实现了ISealable 允许从DispatcherObject派生-由用户决定

    摘要:Style、FrameworkTemplate、Brush或Freezable(例如Brush)等类型都实现了ISealable,而Freezable提供的实现调用了Freeze()。设定 默认值 DependencyProperty的调用会导致DependencyProperty调用ISealable.Seal()。 因此,当将克隆数据(将IsFrozed设置为false)指定给PropertyMetadata作为默认值时,将再次冻结。由于您正在对该默认值进行操作,因此在修改它时将出现异常。