书接上文,前篇文章介绍了依赖属性的原理和实现了一个简单的DependencyProperty(DP),这篇文章主要探讨一下如何使用DP以及有哪些需要注意的地方。

回顾

  依赖属性是由DependencyObject来使用的,那么一个典型的使用场景是什么样呢?

使用DependencyProperty

  一个简单的使用如下:

 1: public class SimpleDO : DependencyObject
 2: {
 3:   public static readonly DependencyProperty IsActiveProperty =
 4:      DependencyProperty.Register("IsActive", typeof(bool), typeof(SimpleDO),
 5:         new PropertyMetadata((bool)false));
 6:  
 7:   public bool IsActive
 8:   {
 9:     get { return (bool)GetValue(IsActiveProperty); }
 10	   set { SetValue(IsActiveProperty, value); }
 11:   }
 12: }
SimpleDO sDo = new SimpleDO();
sDo.IsActive = true;

  这里是使用DependencyProperty.Register来注册DP的,Register函数有很多重载,一个最全的形式如下:

public static DependencyProperty Register(string name, Type propertyType, Type ownerType, 
           PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback);

  前4个参数在前篇文章已有介绍,主要是用来确定DP在全局Map中的键值,属性的类型以及内部属性元数据。最后一个参数是一个delegate,用来验证数据的有效性。

  抛开验证的过程不说,先来看看PropertyMetadata。前篇提到,这个PropertyMetadata是可以子类化的,子类可以调用OverrideMetadata来重写PropertyMetadata。WPF属性系统对于依赖属性支持的策略就封装在Metadata中,那么这个PropertyMetada都有哪些呢?

  常见的主要有FrameworkPropertyMetadata,UIPropertyMetadata以及PropertyMetadata。他们的继承关系是F->U->P。以最复杂的来说,FrameworkPropertyMetadata都提供了哪些功能呢?

FrameworkPropertyMetadata

  FrameworkPropertyMetadata的构造函数提供了很多重载,一个最复杂的构造函数如下:

public FrameworkPropertyMetadata( object defaultValue, 
                                  FrameworkPropertyMetadataOptions flags, 
                                  PropertyChangedCallback propertyChangedCallback, 
                                  CoerceValueCallback coerceValueCallback,
                                  bool isAnimationProhibited,
                                  UpdateSourceTrigger defaultUpdateSourceTrigger);

  其中第一个参数是默认值,最后两个参数分别是是否允许动画,以及绑定时更新的策略,这个不详细解释了。重点看一下里第三、四两个参数,两个CallBack。结合前面提到的ValidateValueCallback,这三个Callback分别代表Validate(验证),PropertyChanged(变化通知)以及Coerce(强制)。当然,作为Metadata,FrameworkPropertyMetadata只是储存了策略信息,WPF属性系统会根据这些信息来提供功能并在适当的时机回调传入的delegate。

  那么WPF属性系统确定属性值的规则又是怎样呢?

处理DependencyProperty的规则

  借用一个常见的图例,介绍一下WPF属性系统对依赖属性操作的基本步骤:

  • 第一步,确定Base Value,对同一个属性的赋值可能发生在很多地方。比如控件的背景(Background),可能在Style或者控件的构造函数中都对它进行了赋值,这个Base Value就要确定这些值中优先级最高的值,把它作为Base Value。
  • 第二步,估值。如果从第一步得到的值是一个表达式值(Expression),比如说一个绑定,WPF属性系统需要把它转化成一个实际值。
  • 第三步,动画。如果当前属性正在作动画,那么因动画而产生的值会优于前面获得的值,这个也就是WPF中常说的动画优先。
  • 第四步,强制。如果我们在FrameworkPropertyMetadata中传入了CoerceValueCallback,WPF属性系统会回调我们传入的的delagate,进行数据的强制赋值。在属性赋值过程中,Coerce拥有最高的优先级,这个优先级要大于动画的优先级别。
  • 第五步,验证。如果在Register的时候传入了ValidateValueCallback,那么最后WPF会调用我们传入的delegate,来验证数据的有效性。当数据无效时会抛出异常来通知。

  那么应该如何使用这些功能呢?

一个简单的例子

  用一个简单的例子,来描述一下这个过程:

public class SimpleDO : DependencyObject
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(double), typeof(SimpleDO),
            new FrameworkPropertyMetadata((double)0.0,
                FrameworkPropertyMetadataOptions.None,
                new PropertyChangedCallback(OnValueChanged),
                new CoerceValueCallback(CoerceValue)), 
                new ValidateValueCallback(IsValidateValue));
 
    public double Value
    {
        get { return (double)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
 
    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Console.WriteLine("ValueChanged new value is {0}", e.NewValue);
    }
 
    private static object CoerceValue(DependencyObject d, object value)
    {
        Console.WriteLine("CoerceValue value is {0}", value);
        return value;
    }     
    
    private static bool IsValidateValue(object value)
    {
        Console.WriteLine("ValidateValue value is {0}", value);
        return true;
    }  
}     
SimpleDO sDo = new SimpleDO();
sDo.Value = 1;

  对应的输出:

ValidateValue value is 0
ValidateValue value is 0
ValidateValue value is 1
CoerceValue value is 1
ValueChanged new value is 1

  当属性变化后,PropertyChangeCallback最终被调用。这里Validate和Coerce的顺序有些乱,并没有完全依照前面谈到的Coerce->Validate的顺序。WPF对属性赋值进行了优化,当属性被修改时,首先会调用Validate来判断传入的值是否有效,如果无效就不调用后面的操作,以提高性能。从这里也可以看出,CoerceValue后面并没有紧跟着ValidateValue,而是直接调用PropertyChanged了。这是因为前面已经验证过value,如果在Coerce中没有改变value,那么就不用再验证了。如果在Coerce中改变了value,那么这里还会再次调用ValidateValue来验证,Valiate在最后一步的意思是指整个Value赋值的过程中,一定会保证最终值得到验证。

  当然,如果对Value作用了动画(需要修改SimpleDO继承于UIElement),比如:

DoubleAnimation animation = new DoubleAnimation(1, 20, 
     new Duration(TimeSpan.FromMilliseconds(5000)), FillBehavior.Stop);
sDo.BeginAnimation(SimpleDO.ValueProperty, animation);

  那么在动画过程中,调用sDo.Value=30是不会有作用的,当然,这个值已经被存到LocalValue上了。在动画结束后,根据FillBehavior来决定是保留动画的最后值还是回到LocalValue上。当FillBehavior是Stop,动画结束后Value的值为30;如果是HoldEnd,那么动画结束后会一直保持动画的最后值20。

  关于LocalValue(本地值),我们稍后再来细谈,先来回顾一下这个例子。在这个例子中,我们分别传入了三个delagate(PropertyChange,Coerce和Validate)。关于PropertyChangeCallback,这个再明显不过了,在属性值变化的时候调用。那么Coerce和Validate意义何在呢?

Coerce与Validate

  DependencyObject提供了两个函数以支持调用Coerce和Validate,分别是

public void CoerceValue(DependencyProperty dp);
public void InvalidateProperty(DependencyProperty dp);

  第一个函数较为常用,比如说Slider,它有三个属性相互作用,Value、Minimum和Maximum,这些属性相互作用,一个默认的规则是Minimum≤Value≤Maximum。那么当其中一个变化时,另外两个是如何响应做出调整呢?这里WPF使用的就是CoerceValue,这个实现也很简单,注册Maximum的时候加入CoerceValueCallback,在CoerceMaximum函数中判断,如果Maximum的值小于Minimum,则使Maximum值等于Minimum;同理在Value中也加入了CoerceValueCallback进行相应的强制判断。然后在Minimum的ChangedValueCallback被调用的时候,调用Maximum和Value的CoerceValue。

  用一句话来形容这个用法就是,当相互作用的几个依赖属性其中一个发生变化时,在它的PropertyChangeCallback中调用受它影响的依赖属性的CoerceValue。当然,这些依赖属性要实现CoerceValueCallback,在其中保证相互作用关系的正确性。

  后一个Validate主要是验证一下数据有效性,比如说传入的double参数是否是NaN等等。

  相对来说,Coerce和Validate并不是特别常用,在WPF属性系统为我们提供的服务中,FrameworkPropertyMetadataOptions应该算有特色,定制性最强的。

FrameworkPropertyMetadataOptions

  所谓Option(选项),肯定有相应的功能与之对应,那么这些开关都有哪些呢?

public enum FrameworkPropertyMetadataOptions
{
   None = 0,
   AffectsMeasure = 1,
   AffectsArrange = 2,
   AffectsParentMeasure = 4,
   AffectsParentArrange = 8,
   AffectsRender = 16,
   Inherits = 32,
   OverridesInheritanceBehavior = 64,
   NotDataBindable = 128,
   BindsTwoWayByDefault = 256,
   Journal = 1024,
   SubPropertiesDoNotAffectRender = 2048,
}

  这些选项分为两类,一类是标记着Affect的,表示这个依赖属性变化后,会有哪些影响,包括(要重新测量,重新绘制等等)。另一类是剩下的,表示当选择了该选项后,依赖属性会具备什么功能,包括(默认双向绑定,属性继承等)。这里介绍一下属性继承(Inherits)。

  继承是我们很熟悉的,子类可以继承父类的方法和属性等。这里是有父子关系的,那么属性继承的父子关系在哪?

  WPF的依赖属性可继承性是依附于对象树的,这个对象树,具体来说是逻辑树。比如说,Window内部放置了一个Button,那么对象树就是Window—Button,Window是Button的父节点。在Window上设置字体大小(FontSize),这个值同样会作用在Button上,这个就是所谓的属性继承。在一般情况下,属性继承会沿着逻辑树一直传下去,除非对象更改了传递的策略。FrameworkElement对象中提供了属性

protected internal InheritanceBehavior InheritanceBehavior { get; set; }

子类可以重载这个属性传递SkipToAppNow等来截断这个继承,默认情况下,Frame就断开了这个继承链。

  回过头来,说说LocalValue,这个配合EffectiveValue,构成了依赖属性中最精彩的故事。

LocalValue与EffectiveValue

  依赖属性很强大,WPF也在不遗余力的宣扬它的美,就像魔术师一样,千变万化的魔术中总有它的底,让我们来掀一下它的底牌,看看它到底是什么玩意。

  在前篇文章里,我们实现了一个EffectiveValue(EffectiveValueEntry),虽然很简陋,不过可以看出它的两大功能。一,只储存变化的值。二,内部储存多个值,根据优先级选择当前值。作为一个属性来说,任何时间,它都应该而且也只应该对外暴露一个值。那么需要解决的问题在哪里呢?第一,从空间上说,同一个依赖属性可能在很多地方被赋值,比如说在构造函数中,Style中,属性继承下来的等等。第二,从时间上说,这些在不同地方的赋值又可能在同一时间发生变化,比如说绑定正在变化的同时又在对该属性作动画。那么就要有一个清晰的规则来界定,为此引入了两个概念,BaseValue和LocalValue。

  前面谈到了处理依赖属性操作的第一步就是,确定BaseValue,这个BaseValue,翻译过来叫基本值。这个基本,是针对动画(Animation)和强制(Coerce)来说的。当依赖属性处于动画或者强制中,它显示的是动画值或者强制值,一旦这两个状态失效,那么就会回到基本值来。

  我们可以调用DependencyPropertyHelper的GetValueSource方法来获得当前依赖属性的信息:

ValueSource source = DependencyPropertyHelper.GetValueSource(sDo, SimpleDO.ValueProperty);

  其中ValueSource如下:

public struct ValueSource
{
   public BaseValueSource BaseValueSource { get; }
   public bool IsAnimated { get; }
   public bool IsCoerced { get; }
   public bool IsExpression { get; }
}

  其中的IsAnimated,IsCoerced,IsExpression用来指示当前依赖属性的状态,BaseValueSource指示当前BaseValue的优先级。它有

public enum BaseValueSource
{
   Unknown = 0,
   Default = 1,
   Inherited = 2,
   DefaultStyle = 3,
   DefaultStyleTrigger = 4,
   Style = 5,
   TemplateTrigger = 6,
   StyleTrigger = 7,
   ImplicitStyleReference = 8,
   ParentTemplate = 9,
   ParentTemplateTrigger = 10,
   Local = 11,
}

  BaseValueSource的优先级别是从小到大,Local具有最高的优先级,这里的Local指在XAML声明时显式指定的属性值或者在后台手动赋值,如 <Button x:Name=”btn” Width=”12”/>或者在后台代码中btn.Width=12。也就是说,当你在后台对一个依赖属性赋值后,这个属性在Style中的值或者Trigger都会因优先级不够高而失去作用。这种情况是很常见的,很多时候,当依赖属性发生问题(绑定没有更新,Trigger没有反应)时,都可以查看当前依赖属性的ValueSource来判断是不是错误设置了DP而导致了优先级不够高才得不到响应。

  那么这个LocalValue是从何而来,是指BaseValueSource中的Local么?

  是的,DependencyObject提供了ReadLocalValue函数来读取当前的LocalValue

public object ReadLocalValue(DependencyProperty dp);

  如果没有在XAML声明时或者在后台为依赖属性赋值,即使在Style中赋值,那么读取出的值都应为DependencyProperty.UnsetValue。如果在声明时使用了绑定,那么读出的值为BindingExpression,其他情况下会读取出当前local中的值。

  那么LocalValue和EffctiveValue的区别在哪呢?DependencyObject提供了GetValue方法来取得属性值,这个值就是EffctiveValue,也就相当于魔术千变万化最终看到的结果,而LocalValue是内部设置的值。举一个简单的例子来说明一下:

  仍然用Slider,它的Minimum,Value以及Maximum

 1: Slider slider = new Slider();
 2: slider.Minimum = 0;
 3: slider.Maximum = 10;
 4: slider.Value = 3;
 5:  
 6: slider.Minimum = 4; //After set, Value = 4; Value's Local Value = 3;
 7: slider.Minimum = 1; //After set, Value = Value's Local Value = 3;
 8: slider.Minimum = 13; //After set, Value = Maximum = 13;

  第6行,当设置了Minimum=4后,Value的Coerce会被调用,在Coerce中,因为Value值(3)小于Minmum(4),Value值被强制为4。但Value的Local值仍然被保留,使用ReadLocalValue函数可以查看到Value的LocalValue仍然为3。第7行,Minimum的值为1后,在Value的Coerce中,因为Value的LocalValue(3)大于1,所以最终取得的Value和LocalValue都为3。

  关于EffectiveValue和LocalValue,WPF对此的态度一直都是半遮半掩,一方面,在对外的函数或注释中对此有过说明;另一方面,又把它当作内部细节一语带过。但这确实是很多稀奇古怪bug的根源,希望朋友们都能看透这层画皮,更好的从内部掌握它。

  谈过了依赖属性的功能,回过头来看看如何注册依赖属性,以及WPF提出的附加(Attached)这个概念。

附加(Attached)属性

  在最前面的例子中,我们是使用DependencyProperty.Register来注册DP的,DP也对外提供了DependencyProperty.RegisterAttached方法来注册DP。这个RegisterAttached的参数和Register是完全一致的,那么Attached(附加)这个概念又从何而来呢?

  其实我们使用依赖属性,一直在Attached(附加)。我们注册(构造)一个DP,然后在DependencyObject中通过GetValue和SetValue来操作DP,也就是把这个DP通过这样的方法粘贴到DependencyObject上,只不过是通过封装CLR属性来达到的。那么RegisterAttached又是怎样呢,来看一个最简单的应用:

public class AttachedHelper
{
   public static readonly DependencyProperty IsAttachedProperty =
       DependencyProperty.RegisterAttached("IsAttached", typeof(bool), typeof(AttachedHelper),
           new FrameworkPropertyMetadata((bool)false));
 
   public static bool GetIsAttached(DependencyObject d)
   {
     return (bool)d.GetValue(IsAttachedProperty);
   }
 
   public static void SetIsAttached(DependencyObject d, bool value)
   {
     d.SetValue(IsAttachedProperty, value);
   } 
}

  在XAML中使用:

<local:SimpleDO x:Name="sDo" local:AttachedHelper.IsAttached="True"/>

  在这个AttachedHelper中,并没有使用CLR属性IsAttached来封装,而是使用了SetIsAttached和GetIsAttached来存取IsAttached值,当然内部还是调用了SetValue与GetValue。XAML Parser提供了支持,local:AttachedHelper.IsAttached="True"最终会调用到SetIsAttached函数。

  Register和RegisterAttached只是封装形式不同,内部的实现都是GetValue和SetValue,这个Attach(能力)是由DependencyObject和DependencyProperty这种分离的设计所产生的,并不是你使用RegisterAttached这个函数产生了多大魔力。当然,因为使用了静态函数封装,RegisterAttached这种方法更加动态化,我们可以在一个运行中的DependencyObject上对它调用SetIsAttached方法,把这个属性塞到DependencyObject内部的EffectiveValueEntry上去。

  因为这种动态附加的能力,使用RegisterAttached注册的依赖属性也被称为附加属性,有了附加属性,因此也衍生出了一些精彩的设计,这些略过不提,来看看使用依赖属性有哪些需要注意的地方。

使用依赖属性要注意的地方

  依赖属性,看这个词它应该也是个属性,那么传统CLR属性的一些用法(多态,Set里面Raise一些Event等)还适用么?

  江湖险恶,小心。

  拿前面SimpleDO的ValuePropety来说。你在后台代码中调用sDo.Value = 2,那么Value的set会被调用,但如果你在XAML赋值或者把它绑定到其他属性,程序运行后,Value的值正常,可是Value的set并没有被调到。这是怎么回事?

  WPF对依赖属性进行了优化,在绑定等一些场合并不是调用属性的get,set方法,而是直接读取或设置依赖属性的EffectiveValue,也就是说,为了提高性能,绕开了你封装的方法,直接在DP内部去搞了。当然,不按规矩出牌,按照传统属性设置的多态,set里的逻辑就统统失效了。

  WPF建议实现多态的方式是在PropertyChangedCallback中调用虚函数来实现,如:

private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  ((SimpleDO)d).OnValueChanged(e);
}
 
protected virtual void OnValueChanged(DependencyPropertyChangedEventArgs e)
{
}

  子类通过重载OnValueChanged来实现多态。

  当然,具体使用依赖属性出现的问题还会有很多,就不一一介绍了,接下来看看依赖属性为我们设计程序提供了哪些帮助。

使用依赖属性的新思维

  依赖属性提供了很强大的功能,附加属性,属性变化通知,可继承,多属性值等等,关于这方面的宣传也到处可见,用武功秘籍来形容,大概是这样子的。

江湖新出现了一门武功,集旧派武功之大成,练到绝处,诡异莫测,绣花针亦可为武器,杀人于无形之中。当千辛万苦夺得秘籍之后,未翻书,体先寒,只见上面四个大字,“葵花宝典”……

  确实,不是所有对象都可以使用依赖属性的,只有继承自DependencyObject的对象才可以使用DP,DependencyObject具有这样的能力源自它内部持有一个EffectiveValueEntry的数组,类似于一个百宝囊。这个也是在设计中经常被人遗忘的地方。

  比如说,有两个Panel,一个Panel上又有很多Panel、控件,我们希望按照一定条件过滤,把符合条件的控件移到另一个Panel上去,然后点击恢复按钮这些控件又可以回到原位置。这里就可以定义一个附加属性,比如说OldParent,遍历第一个Panel的逻辑树,把符合条件的控件从它的Parent中移除,并且使用附加属性OldParent记录它的Parent,这样在恢复原位置的时候就可以拿到OldParent,然后再Add回去了。

  当然,这类应用就是定义一个附加属性,然后附加到对象中去,应用的都比较简单,只是为了说明DependencyObject具有这样支持存取的能力,设计程序的时候不要浪费。

总结

  关于依赖属性,可写的东西很多,每一个点展开都能有很多故事,像RegisterReadOnly,AddOwner等都没有详细介绍。依赖属性中,属性和使用它的对象分离是它的特色,两者之间的粘合和作用是它的难点,希望朋友们都能从内到外的看待依赖属性,更好的玩转它。

http://www.cnblogs.com/Zhouyongh/archive/2009/10/20/1586278.html

一站式WPF--依赖属性(DependencyProperty)二的更多相关文章

  1. WPF依赖属性DependencyProperty

    写在之前: 依赖属性算是WPF醉醉基础的一个组成了.平时写代码的时候,简单的绑定很轻松,但是遇到复杂的层次比较多的绑定,真的是要命.所以,我觉得深刻认识依赖属性是很有必要的.本篇只是个人学习的记录,学 ...

  2. WPF 依赖属性源码 洞察微软如何实现DependencyProperty

    依赖属性DependencyProperty是wpf最重要的一个类,理解该类如何实现对学习wpf帮助很大! 终于找到了该类的源码!仔细阅读源码,看看微软如何玩的花招! File: Base\Syste ...

  3. WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性

    原文:WPF 使用依赖属性(DependencyProperty) 定义用户控件中的Image Source属性 如果你要自定义一个图片按钮控件,那么如何在主窗体绑定这个控件上图片的Source呢? ...

  4. WPF自学入门(五)WPF依赖属性

    在.NET中有事件也有属性,WPF中加入了路由事件,也加入了依赖属性.最近在写项目时还不知道WPF依赖属性是干什么用的,在使用依赖项属性的时候我都以为是在用.NET中的属性,但是确实上不是的,通过阅读 ...

  5. WPF依赖属性详解

    WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...

  6. WPF依赖属性值源(BaseValueSource)

    原文:WPF依赖属性值源(BaseValueSource)   WPF依赖属性提供一个机制,可以获取依赖属性提供值的来源 其以BaseValueSource枚举表示 1.Default public ...

  7. WPF依赖属性(续)(3)依赖属性存储

    原文:WPF依赖属性(续)(3)依赖属性存储          在之前的两篇,很多朋友参与了讨论,也说明各位对WPF/SL计数的热情,对DP系统各抒已见,当然也出现了一些分歧. 以下简称DP为依赖属性 ...

  8. WPF依赖属性(续)(2)依赖属性与附加属性的区别

    原文:WPF依赖属性(续)(2)依赖属性与附加属性的区别        接上篇,感谢各位的评论,都是认为依赖属性的设计并不是为了节省内存,从大的方面而讲是如此.样式,数据绑定,动画样样都离不开它.这篇 ...

  9. WPF依赖属性的正确学习方法

    前言 我在学习WPF的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴. 相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学 ...

  10. WPF依赖属性(续)(1)

    原文:WPF依赖属性(续)(1)                 之前有写过几篇文章,详细地介绍了依赖属性的基本使用方法,如果你不想了解其内部实现机制的话,那么通过那两篇文章的介绍,足以应付平时的应用 ...

随机推荐

  1. .htaccess 文件中详细介绍

    #如果存在rewrite_module 模块则执行里面的代码 <IfModule rewrite_module> #开启重写机制 RewriteEngine On #告诉apache这里不 ...

  2. eclipse 编译JAVA 项目导入的WEB项目 无法编译问题

    右击你的项目 选择properties  ---->java Build Path--->Default output folder新建一个classes目录就好了 watermark/2 ...

  3. Cannot make a static reference to the non-static method的解决方法

    报错原因:在一个类中写了一个public String getContent()方法和一个main()方法,getContent()方法中包含了getClass()方法,在main()方法中直接调用了 ...

  4. JavaWeb 发送post请求的2种方式(form、json)

      JavaWeb 发送post请求的2种方式(form.json) CreationTime--2018年6月20日10点15分 Author:Marydon 前提:通过HttpClient来实现 ...

  5. HttpClient4.3教程 第二章 连接管理

    2.1.持久连接 两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间.Http连接需要的三次握手开销很大,这一开销对于比较小的http消息来说更大.但是如果我们直接使用已 ...

  6. sharepoint admin svc must be running in order to create deployment timer job 若要创建计时器作业,必须执行SVC

    sharepoint admin svc must be running in order to create deployment timer job 若要创建计时器作业.必须执行SVC       ...

  7. <转>Oracle Stream Replication技术

    Stream 是Oracle 的消息队列(也叫Oracle Advanced Queue)技术的一种扩展应用. Oracle 的消息队列是通过发布/订阅的方式来解决事件管理.流复制(Stream re ...

  8. js检测回车符

    在说检测回车符之前,需要了解keydown和keypress的区别 比如你可以将检测事件绑定在input上,如下所示: <input name="remark" id=&qu ...

  9. SWFupload在IE9以上中的bug

    这几天在做图片上传的东西,是用swfupload是出现了再IE9下那选择文件的按钮无法点击的情况,在其他浏览器,例如Firefox.chrome都不会出现,后来google一下才发下这算是IE9以上和 ...

  10. C语言控制台打印3D爱心图案

    非常多程序猿都认为自己的编程工作十分的鼓噪乏味.一整天面对的都是一些写不完的代码和改不完的Bug.今天我们要给大家分享一些有趣的C语言代码,也许能够为你无聊的工作带来一丝乐趣. 这些代码能够完毕几个不 ...