tag:blogger.com,1999:blog-75630896914088219672024-03-08T16:56:28.417+08:00Another Paradigm ShiftThe content in this blog is provided as-is, with no warranties and confers no rightsZhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.comBlogger51125tag:blogger.com,1999:blog-7563089691408821967.post-43063466164507623712010-12-30T14:27:00.001+08:002010-12-31T09:01:11.372+08:00Relational lifted operators are confusing to me<p><font size="2">I am glad to know that I could access BlogSpot from my workplace (although not from my rental house), which means that I could continue writing something I feel interesting and worthy sharing. </font></p> <p><font size="2">Several months ago, we had a discussion internally about the operator lifting feature in C#, and how we could leverage upon this feature in our own library. Our library is mainly used for </font><a href="http://en.wikipedia.org/wiki/International_System_of_Units"><font size="2">SI unit</font></a><font size="2"> conversion, one requirement of this library is to support the basic arithmetic operations on those unit classes, for example, we have unit classes like Length, Time and Velocity, and we should support operation like follows: </font></p> <p><font size="2">Velocity = Length / Time, Length = Velocity * Time</font></p> <p><font size="2">Apparently, we could use the operator overloading feature in C# to enable the above scenarios, so that we could overload the division or multiplication operators as follows:</font></p> <p><font size="2"><span style="color: blue">public static </span><span style="color: #2b91af">Length </span><span style="color: blue">operator </span>*(<span style="color: #2b91af">Velocity </span>velocity, <span style="color: #2b91af">Time </span>time) <br />{ <br />    <span style="color: blue">return </span>velocity.Amount * time.Amount * <span style="color: #2b91af">Length</span>.Meter; <br />}</font></p> <a href="http://11011.net/software/vspaste"></a> <p><font size="2">This looks great and okay, but how about if the “velocity” or “time” parameter is null, then we end up having NullReferenceException thrown at us. The correct implementation should be something like the following:</font></p> <p><font size="2"><span style="color: blue">public static </span><span style="color: #2b91af">Length </span><span style="color: blue">operator </span>*(<span style="color: #2b91af">Velocity </span>velocity, <span style="color: #2b91af">Time </span>time) <br />{ <br />    <span style="color: blue">if</span>(velocity == <span style="color: blue">null</span>|| time == <span style="color: blue">null</span>) <br />    { <br />        <span style="color: blue">return null</span>; <br />    } <br /> <br />    <span style="color: blue">return </span>velocity.Amount * time.Amount * <span style="color: #2b91af">Length</span>.Meter; <br />}</font></p> <a href="http://11011.net/software/vspaste"></a><style type="text/css"><br /><br /><br /><br /><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }</style> <p><font size="2">This just follows the rules when overloading operator which could be lifted as Eric Lippert illustrated in </font><a href="http://blogs.msdn.com/b/ericlippert/archive/2007/06/27/what-exactly-does-lifted-mean.aspx"><font size="2">his blog post</font></a><font size="2">:</font></p> <p><em><font size="2">3) For the binary operators + - * / % & | ^ << >>, where the operator takes an S and a T and returns a U there is a corresponding <strong>lifted</strong> operator that takes an S? and a T? and returns a U?. It returns null if either argument is null.</font></em></p> <p><font size="2">I almost totally agree with the rules described by Eric Lippert except this one:</font></p> <p><em><font size="2">1) For every equality and relational operator that compares an S to a T and returns a bool there is a corresponding <strong>lifted</strong> operator that compares an S? to a T? and returns a bool. <strong>It returns false if either argument is null. (With the additional exception that if one argument to an equality operator is the null literal then the result may be determined by checking if the nullable term has a value.)</strong></font></em></p> <p><font size="2">Consider this:</font></p> <p><font size="2"><span style="color: blue">double</span>? a = <span style="color: blue">null</span>; <br /><span style="color: blue">double</span>? b = 1; <br /><span style="color: #2b91af">Console</span>.WriteLine(a > b); <br /><span style="color: #2b91af">Console</span>.WriteLine(a == b);</font></p> <font size="2"><span style="color: #2b91af">Console</span>.WriteLine(a < b);</font><a href="http://11011.net/software/vspaste"></a><style type="text/css"><br /><br /><br /><br /><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }</style> <p><font size="2">This three comparison operation will all return false which doesn’t make any sense, since the three comparison should be mutually exclusive in logical sense.</font></p> <p><font size="2">Actually I recommend to return <font color="#0000ff">bool</font>? when overloading those relational operators as follows:</font></p> <p><span style="color: blue"><font size="2">public class </font></span><font size="2"><span style="color: #2b91af">Length <br /></span>{ <br />    <span style="color: blue">public </span>Length(<span style="color: blue">double </span>amount) <br />    { <br />        <span style="color: blue">this</span>.Amount = amount; <br />    } <br /> <br />    <span style="color: blue">public double </span>Amount <br />    { <br />        <span style="color: blue">get</span>; <br />        <span style="color: blue">private set</span>; <br />    } <br /> <br />    <span style="color: blue">public static bool</span>? <span style="color: blue">operator </span>>(<span style="color: #2b91af">Length </span>a, <span style="color: #2b91af">Length </span>b) <br />    { <br />        <span style="color: blue">if</span>(ReferenceEquals(a, <span style="color: blue">null</span>) || ReferenceEquals(b, <span style="color: blue">null</span>)) <br />        { <br />            <span style="color: blue">return null</span>; <br />        } <br /> <br />        <span style="color: blue">return </span>a.Amount > b.Amount; <br />    } <br /> <br />    <span style="color: blue">public static bool</span>? <span style="color: blue">operator </span><(<span style="color: #2b91af">Length </span>a, <span style="color: #2b91af">Length </span>b) <br />    { <br />        <span style="color: blue">if</span>(ReferenceEquals(a, <span style="color: blue">null</span>) || ReferenceEquals(b, <span style="color: blue">null</span>)) <br />        { <br />            <span style="color: blue">return null</span>; <br />        } <br /> <br />        <span style="color: blue">return </span>a.Amount < b.Amount; <br />    } <br /> <br />    <span style="color: blue">public static bool</span>? <span style="color: blue">operator </span>==(<span style="color: #2b91af">Length </span>a, <span style="color: #2b91af">Length </span>b) <br />    { <br />        <span style="color: blue">if</span>(ReferenceEquals(a, <span style="color: blue">null</span>) || ReferenceEquals(b, <span style="color: blue">null</span>)) <br />        { <br />            <span style="color: blue">return null</span>; <br />        } <br /> <br />        <span style="color: blue">return </span>a.Amount == b.Amount; <br />    } <br /> <br /> <br />    <span style="color: blue">public static bool</span>? <span style="color: blue">operator </span>!=(<span style="color: #2b91af">Length </span>a, <span style="color: #2b91af">Length </span>b) <br />    { <br />        <span style="color: blue">if </span>(ReferenceEquals(a, <span style="color: blue">null</span>) || ReferenceEquals(b, <span style="color: blue">null</span>)) <br />        { <br />            <span style="color: blue">return null</span>; <br />        } <br /> <br />        <span style="color: blue">return </span>a.Amount != b.Amount; <br />    } <br />}</font></p> <a href="http://11011.net/software/vspaste"></a> <p><font size="2">With this Length class at hand, then three comparisons shown previously will all return correct result (aka null):</font></p> <p><font size="2"><span style="color: #2b91af">Length </span>a = <span style="color: blue">null</span>; <br /><span style="color: blue">var </span>b = <span style="color: blue">new </span><span style="color: #2b91af">Length</span>(1); <br /><span style="color: #2b91af">Console</span>.WriteLine(a > b); <br /><span style="color: #2b91af">Console</span>.WriteLine(a == b);</font></p> <font size="2"><span style="color: #2b91af">Console</span>.WriteLine(a < b);</font><a href="http://11011.net/software/vspaste"></a><style type="text/css"><br /><br /><br /><br /><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }</style> <p><font size="2">I’ve no idea why nullable type in C# doesn’t follow this, but they do have </font><a href="http://blogs.msdn.com/b/csharpfaq/archive/2004/05/25/why-don-t-nullable-relational-operators-return-bool-instead-of-bool.aspx"><font size="2">their own explanation</font></a><font size="2"> which is even more confusing to me:</font></p> <p><em><font size="2">This seems like a very natural thing for a C# user to want to write. But if equality is three-valued, comparing anything to null always returns a null value (ie null isn't equal to anything), and therefore such a comparison can never succeed. Instead, the user would have to write:</font></em></p> <p><em><font size="2">if (!p.HasValue)</font></em></p> <p><em><font size="2">or something similar. We decided that the value of having a model that was consistent with the way users were used to dealing with reference null was pretty high, and therefore decided to make the relational operators return bool.</font></em></p> <p><font size="2">I personally think writing line like if(!p.HasValue) is okay for me, because otherwise I need to deal with other confusing scenario like the one I’ve shown above with three comparisons on <font color="#0000ff">double</font>?.</font></p> <p><font size="2"></font></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com3tag:blogger.com,1999:blog-7563089691408821967.post-4845180350003347822009-01-05T18:29:00.000+08:002009-01-06T18:29:56.599+08:00Streaming Media Content Over WCF RESTful Service<p>WCF RESTful service API enables you to serve <a href="http://en.wikipedia.org/wiki/Plain_Old_XML">POX</a> type of content over the http transport, and its default content-type header is application/xml if you use DataContractSerializer and application/json if you use DataContractJsonSerializer. If you need to serve up other contents for instance video/audio content, you need to explicitly control the content-type header, this could be achieved again by writing custom message formatter as demonstrated in the <a href="http://shevaspace.blogspot.com/2009/01/include-xml-declaration-in-wcf-restful.html">previous post</a>, here is the code: </p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">ContentTypeMessageFormatter</span> : <span style="color: rgb(43,145,175)">IDispatchMessageFormatter <br /></span>{ <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(43,145,175)">IDispatchMessageFormatter</span> formatter; <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(43,145,175)">String</span> contentType; <br />    <span style="color: rgb(0,0,255)">public</span> ContentTypeMessageFormatter(<span style="color: rgb(43,145,175)">IDispatchMessageFormatter</span> formatter, <span style="color: rgb(43,145,175)">String</span> contentType) <br />    { <br />        <span style="color: rgb(0,0,255)">this</span>.formatter = formatter; <br />        <span style="color: rgb(0,0,255)">this</span>.contentType = contentType; <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> DeserializeRequest(<span style="color: rgb(43,145,175)">Message</span> message, <span style="color: rgb(0,0,255)">object</span>[] parameters) <br />    { <br />        formatter.DeserializeRequest(message, parameters); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">Message</span> SerializeReply(<span style="color: rgb(43,145,175)">MessageVersion</span> messageVersion, <span style="color: rgb(43,145,175)">Object</span>[] parameters, <span style="color: rgb(43,145,175)">Object</span> result) <br />    { <br />        <span style="color: rgb(0,0,255)">if</span> (!<span style="color: rgb(43,145,175)">String</span>.IsNullOrEmpty(contentType)) <br />        { <br />            <span style="color: rgb(43,145,175)">WebOperationContext</span>.Current.OutgoingResponse.ContentType = contentType; <br />        } <br />        <span style="color: rgb(0,0,255)">return</span> formatter.SerializeReply(messageVersion, parameters, result); <br />    } <br />} <br /> <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">ContentTypeAttribute</span> : <span style="color: rgb(43,145,175)">Attribute</span>, <span style="color: rgb(43,145,175)">IOperationBehavior <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> ContentTypeAttribute(<span style="color: rgb(43,145,175)">String</span> contentType) <br />    { <br />        <span style="color: rgb(0,0,255)">this</span>.ContentType = contentType; <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">String</span> ContentType <br />    { <br />        <span style="color: rgb(0,0,255)">get</span>; <br />        <span style="color: rgb(0,0,255)">set</span>; <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> AddBindingParameters(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">BindingParameterCollection</span> bindingParameters) <br />    { <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> ApplyClientBehavior(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">ClientOperation</span> clientOperation) <br />    { <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> ApplyDispatchBehavior(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">DispatchOperation</span> dispatchOperation) <br />    { <br />        dispatchOperation.Formatter = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ContentTypeMessageFormatter</span>(dispatchOperation.Formatter, ContentType); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> Validate(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription) <br />    { <br />    } <br />} </p> <p>Then you could directly specify the ContentTypeAttribute at the service operation level, the following service could stream WMV video content over http by turning on the streamed transfer mode of WCF: </p> <p>[<span style="color: rgb(43,145,175)">ServiceContract</span>] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">interface</span> <span style="color: rgb(43,145,175)">IMediaService <br /></span>{ <br />    [<span style="color: rgb(43,145,175)">OperationContract</span>] <br />    [<span style="color: rgb(43,145,175)">ContentType</span>(<span style="color: rgb(163,21,21)">"audio/x-ms-wmv"</span>)] <br />    [<span style="color: rgb(43,145,175)">WebGet</span>(UriTemplate = <span style="color: rgb(163,21,21)">"media/{name}"</span>)] <br />    <span style="color: rgb(43,145,175)">Stream</span> GetMedia(<span style="color: rgb(43,145,175)">String</span> name); <br />} <br /> <br />[<span style="color: rgb(43,145,175)">AspNetCompatibilityRequirements</span>(RequirementsMode=<span style="color: rgb(43,145,175)">AspNetCompatibilityRequirementsMode</span>.Required)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">MediaService</span> : <span style="color: rgb(43,145,175)">IMediaService <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">Stream</span> GetMedia(<span style="color: rgb(43,145,175)">String</span> name) <br />    { <br />        <span style="color: rgb(0,0,255)">var</span> dir = <span style="color: rgb(43,145,175)">HttpContext</span>.Current.Server.MapPath(<span style="color: rgb(163,21,21)">"~"</span>); <br />        <span style="color: rgb(0,0,255)">var</span> file = <span style="color: rgb(43,145,175)">String</span>.Format(<span style="color: rgb(163,21,21)">"{0}.wmv"</span>, name); <br />        <span style="color: rgb(0,0,255)">var</span> filePath = <span style="color: rgb(43,145,175)">Path</span>.Combine(dir, file); <br />        <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(43,145,175)">File</span>.OpenRead(filePath); <br />    } <br />} </p> <p>You turn on streaming in WCF, you need to configure the service as follows: </p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">system.serviceModel</span><span style="color: rgb(0,0,255)">> <br />  <</span><span style="color: rgb(163,21,21)">serviceHostingEnvironment</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">aspNetCompatibilityEnabled</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">true</span>"<span style="color: rgb(0,0,255)">/> <br />  <</span><span style="color: rgb(163,21,21)">services</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">service <br /></span><span style="color: rgb(0,0,255)">     </span><span style="color: rgb(255,0,0)">behaviorConfiguration</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">serviceBehavior</span>" <br /><span style="color: rgb(0,0,255)">     </span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">CustomContentTypeInRESTDemo.MediaService</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">endpoint <br /></span><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">behaviorConfiguration</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">RestBehaviorConfig</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">binding</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">webHttpBinding</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">bindingConfiguration</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">HttpStreaming</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">contract</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">CustomContentTypeInRESTDemo.IMediaService</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(163,21,21)">service</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">services</span><span style="color: rgb(0,0,255)">> <br />  <</span><span style="color: rgb(163,21,21)">bindings</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">webHttpBinding</span><span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">binding</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">HttpStreaming</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">maxReceivedMessageSize</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">67108864</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">transferMode</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Streamed</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(163,21,21)">webHttpBinding</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">bindings</span><span style="color: rgb(0,0,255)">> <br />  <</span><span style="color: rgb(163,21,21)">behaviors</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">endpointBehaviors</span><span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">behavior</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">RestBehaviorConfig</span>"<span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(163,21,21)">webHttp</span><span style="color: rgb(0,0,255)">/> <br />      </</span><span style="color: rgb(163,21,21)">behavior</span><span style="color: rgb(0,0,255)">> <br />    </</span><span style="color: rgb(163,21,21)">endpointBehaviors</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">behaviors</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(163,21,21)">system.serviceModel</span><span style="color: rgb(0,0,255)">></span> </p> <p>Streaming audio/video content using RESTful service could be pretty useful, in particular if you need to use WPF’s MediaElement to playback content provided by your service, since MediaElement only allows you to specify a Uri of the media file, the DirectShow has build-in source filter to feed video/audio bits from HTTP transport or local file system, but doesn’t provide a built-in source filter to read media bits from arbitrary stream. </p> <p>In WPF, you could directly have MediaElement’s Source property pointing to the templated REST Uri: </p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">MediaElement</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Source</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://localhost:8080/MediaService.svc/media/testVideo</span>"<span style="color: rgb(0,0,255)">/></span> </p> <p>Actually, if you need VCR type of control over the streamed media content, then you need native protocol level support such as <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a>. <a href="http://en.wikipedia.org/wiki/Windows_Media_Services">Windows Media Services</a> is Microsoft’s server implementation of RTSP protocol, and is available in Windows Server 2003 and Windows Server 2008. <a href="http://www.devsource.com/c/a/Techniques/Streaming-with-Windows-Media-Services-and-ASPNET/">This link</a> illustrates how to configure WMS in Windows Server 2003.</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-30708230038253473632009-01-04T10:15:00.000+08:002009-01-05T10:16:28.778+08:00Include XML Declaration in WCF RESTful Service Response<p>When using WCF to write RESTful web services, the default XML response stream doesn’t contain the XML Declaration or XML DOCTYPE. the default XML serialization mechanism used by WCF’s data contract serializer does support emitting XML declaration, but it isn’t turned out by default. You could try using XmlSerializer instead which does support emitting XML declaration,  but if you need stick to data contract serializer, you could try hooking into the WCF serialization process by writing a custom message formatter, the following code contains the full implementation of this approach: </p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">XmlDeclarationMessage</span> : <span style="color: rgb(43,145,175)">Message <br /></span>{ <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(43,145,175)">Message</span> message; <br />    <span style="color: rgb(0,0,255)">public</span> XmlDeclarationMessage(<span style="color: rgb(43,145,175)">Message</span> message) <br />    { <br />        <span style="color: rgb(0,0,255)">this</span>.message = message; <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(43,145,175)">MessageHeaders</span> Headers <br />    { <br />        <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> message.Headers; } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,0,255)">void</span> OnWriteBodyContents(System.Xml.<span style="color: rgb(43,145,175)">XmlDictionaryWriter</span> writer) <br />    { <br />        <span style="color: rgb(0,128,0)">// WCF XML serialization doesn't support emitting XML DOCTYPE, you need to roll up your own here. <br /></span>        writer.WriteStartDocument(); <br />        message.WriteBodyContents(writer); <br />    } <br /> <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(43,145,175)">MessageProperties</span> Properties <br />    { <br />        <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> message.Properties; } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(43,145,175)">MessageVersion</span> Version <br />    { <br />        <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> message.Version; } <br />    } <br />} <br /> <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">XmlDeclarationMessageFormatter</span> : <span style="color: rgb(43,145,175)">IDispatchMessageFormatter <br /></span>{ <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(43,145,175)">IDispatchMessageFormatter</span> formatter; <br />    <span style="color: rgb(0,0,255)">public</span> XmlDeclarationMessageFormatter(<span style="color: rgb(43,145,175)">IDispatchMessageFormatter</span> formatter) <br />    { <br />        <span style="color: rgb(0,0,255)">this</span>.formatter = formatter; <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> DeserializeRequest(<span style="color: rgb(43,145,175)">Message</span> message, <span style="color: rgb(0,0,255)">object</span>[] parameters) <br />    { <br />        formatter.DeserializeRequest(message, parameters); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">Message</span> SerializeReply(<span style="color: rgb(43,145,175)">MessageVersion</span> messageVersion, <span style="color: rgb(43,145,175)">Object</span>[] parameters, <span style="color: rgb(43,145,175)">Object</span> result) <br />    { <br />        <span style="color: rgb(0,0,255)">var</span> message = formatter.SerializeReply(messageVersion, parameters, result); <br />        <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">XmlDeclarationMessage</span>(message); <br />    } <br />} <br /> <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">IncludeXmlDeclarationAttribute</span> : <span style="color: rgb(43,145,175)">Attribute</span>, <span style="color: rgb(43,145,175)">IOperationBehavior <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> AddBindingParameters(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">BindingParameterCollection</span> bindingParameters) <br />    { <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> ApplyClientBehavior(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">ClientOperation</span> clientOperation) <br />    { <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> ApplyDispatchBehavior(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription, <span style="color: rgb(43,145,175)">DispatchOperation</span> dispatchOperation) <br />    { <br />        dispatchOperation.Formatter = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">XmlDeclarationMessageFormatter</span>(dispatchOperation.Formatter); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> Validate(<span style="color: rgb(43,145,175)">OperationDescription</span> operationDescription) <br />    { <br />    } <br />} </p> <p>Then you could specify the IncludeXmlDeclarationAttribute at service operation level, if you need to enable this at service wide level, you could try implementing the IServiceBehavior interface instead. </p> <p>[<span style="color: rgb(43,145,175)">ServiceContract</span>] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">interface</span> <span style="color: rgb(43,145,175)">IDemoService <br /></span>{ <br />    [<span style="color: rgb(43,145,175)">OperationContract</span>] <br />    [<span style="color: rgb(43,145,175)">IncludeXmlDeclaration</span>] <br />    [<span style="color: rgb(43,145,175)">WebGet</span>(UriTemplate = <span style="color: rgb(163,21,21)">"customers/{name}/"</span>)] <br />    <span style="color: rgb(43,145,175)">Customer</span> GetCustomer(<span style="color: rgb(43,145,175)">String</span> name); <br />} <a href="http://11011.net/software/vspaste"></a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com10tag:blogger.com,1999:blog-7563089691408821967.post-8293245449674265542008-09-09T17:27:00.001+08:002010-11-29T14:52:27.175+08:00Display HwndHost Inside ToolTip/ContextMenu<p>This post is a further elaboration to my reply to <a href="http://forums.msdn.microsoft.com/en-US/wpf/thread/63dd0da4-c955-4bda-9d2c-3bec2086f951">this MSDN WPF forum thread</a>.</p> <p>Before digging into the details of how to solve this issue, let's start with some background information on ToolTip/ContextMenu and HwndHost.</p> <p>In WPF, ToolTip/ContextMenu is hosted inside a Popup, Popup is essentially a hwnd with WS_POPUP window style. By default, the Popup created by ToolTip and ContextMenu services will have AllowsTransparency property set to true to enable layered windows on it. WPF uses application managed layered windows which is incompatible with the win32/GDI rendering model, which means that application managed layered window doesn't support child hwnd with WS_CHILD window style. So you want to display hwnd based content (through HwndHost) inside the a ToolTip/ContextMenu, you need to disable layered windows.</p> <p>The caveat here is that the implementation of ToolTip and ContextMenu has hardcoded the AllowsTransparency property value when creating Popup, and it seems that this will not change at least in the next version of WPF. I think it might be common scenario to display content coming from other pre-WPF presentation technologies such as win32 controls, MFC/ActiveX controls or Windows Forms controls etc simply because you already have a control at hand,and it has already implemented all the functionalities you want. There are some known issues with hwnd interop in particular the airspace issue but as long as you don't overlap the hwnd content with WPF content, things should work properly at least visually.</p> <p>The solution I posted <a href="http://forums.msdn.microsoft.com/en-US/wpf/thread/63dd0da4-c955-4bda-9d2c-3bec2086f951">in that WPF forum thread</a> requires subclassing from ToolTip, and override the OnTemplateChanged protected method to wire up the code to set the Popup.AllowsTransparency at the right time before the internal implementation of ToolTip creates it for you which will be too late to set that property(setting Popup.AllowsTransparency to false after the ToolTip/ContextMenu is displayed doesn't take effect immediately, you need to hide the popup and then display it again to get this work). The ideal solution is that I could wire up those logic through the well-known WPF pattern - <a href="http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx">attached behavior pattern</a>. So I could simply write something like the following, and things start to work for both ToolTip and ContextMenu without separate subclassing:</p> <p><span style="color: blue"><</span><span style="color: #a31515">StackPanel</span><span style="color: blue">> <br />  <</span><span style="color: #a31515">Button Content</span><span style="color: blue">=</span>"<span style="color: blue">ContextMenu Demo</span>"<span style="color: blue">> <br />    <</span><span style="color: #a31515">Button.ContextMenu</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">ContextMenu </span><span style="color: red">cc:PopupBehavior.AllowsTransparency</span><span style="color: blue">=</span>"<span style="color: blue">False</span>"<span style="color: blue">> <br />        <</span><span style="color: #a31515">WebBrowser </span><span style="color: red">Width</span><span style="color: blue">=</span>"<span style="color: blue">300</span>" <span style="color: red">Height</span><span style="color: blue">=</span>"<span style="color: blue">200</span>" <span style="color: red">Source</span><span style="color: blue">=</span>"<span style="color: blue">http://www.live.com</span>" <span style="color: blue">/> <br />      </</span><span style="color: #a31515">ContextMenu</span><span style="color: blue">> <br />    </</span><span style="color: #a31515">Button.ContextMenu</span><span style="color: blue">> <br />  </</span><span style="color: #a31515">Button</span><span style="color: blue">> <br />  <</span><span style="color: #a31515">Button </span><span style="color: red">Content</span><span style="color: blue">=</span>"<span style="color: blue">ToolTip Demo</span>"<span style="color: blue">> <br />    <</span><span style="color: #a31515">Button.ToolTip</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">ToolTip </span><span style="color: red">cc:PopupBehavior.AllowsTransparency</span><span style="color: blue">=</span>"<span style="color: blue">False</span>"<span style="color: blue">> <br />        <</span><span style="color: #a31515">WebBrowser </span><span style="color: red">Width</span><span style="color: blue">=</span>"<span style="color: blue">300</span>" <span style="color: red">Height</span><span style="color: blue">=</span>"<span style="color: blue">200</span>" <span style="color: red">Source</span><span style="color: blue">=</span>"<span style="color: blue">http://www.live.com</span>" <span style="color: blue">/> <br />      </</span><span style="color: #a31515">ToolTip</span><span style="color: blue">> <br />    </</span><span style="color: #a31515">Button.ToolTip</span><span style="color: blue">> <br />  </</span><span style="color: #a31515">Button</span><span style="color: blue">> <br /></</span><span style="color: #a31515">StackPanel</span><span style="color: blue">></span></p> <style type="text/css"><br /><br /><br />.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }</style> <p>Here is the complete implementation of PopupBehavior:</p> <p><span style="color: blue">using </span>System; <br /><span style="color: blue">using </span>System.Security; <br /><span style="color: blue">using </span>System.ComponentModel; <br /> <br /><span style="color: blue">using </span>System.Windows; <br /><span style="color: blue">using </span>System.Windows.Media; <br /><span style="color: blue">using </span>System.Windows.Threading; <br /><span style="color: blue">using </span>System.Windows.Controls; <br /><span style="color: blue">using </span>System.Windows.Controls.Primitives; <br /><span style="color: blue"></span></p> <p><span style="color: blue">namespace </span>Sheva.Windows.Controls <br />{ <br />    <span style="color: gray">/// <summary> <br />    ///</span><span style="color: green">Extends Popup control with added behaviors. <br />   </span><span style="color: gray">/// </summary> <br />   </span><span style="color: blue">public class </span><span style="color: #2b91af">PopupBehavior <br />   </span>{ <br />        <span style="color: blue">public static readonly </span><span style="color: #2b91af">DependencyProperty </span>AllowsTransparencyProperty = <br />            <span style="color: #2b91af">DependencyProperty</span>.RegisterAttached( <br />            <span style="color: #a31515">"AllowsTransparency"</span>, <br />            <span style="color: blue">typeof</span>(<span style="color: #2b91af">Boolean</span>), <br />            <span style="color: blue">typeof</span>(<span style="color: #2b91af">PopupBehavior</span>), <br />            <span style="color: blue">new </span><span style="color: #2b91af">FrameworkPropertyMetadata</span>( <br />                <span style="color: blue">true</span>, <br />                <span style="color: blue">new </span><span style="color: #2b91af">PropertyChangedCallback</span>(OnAllowsTransparencyChanged), <br />                <span style="color: blue">new </span><span style="color: #2b91af">CoerceValueCallback</span>(CoerceAllowsTransparency))); <br /> <br /> <br />        <span style="color: blue">public static </span><span style="color: #2b91af">Boolean </span>GetAllowsTransparency(<span style="color: #2b91af">Control </span>element) <br />        { <br />            CheckElementType(element); <br />            <span style="color: blue">return </span>(<span style="color: #2b91af">Boolean</span>)element.GetValue(AllowsTransparencyProperty); <br />        } <br /> <br />        <span style="color: blue">public static void </span>SetAllowsTransparency(<span style="color: #2b91af">Control </span>element, <span style="color: #2b91af">Boolean </span>value) <br />        { <br />            CheckElementType(element); <br />            element.SetValue(AllowsTransparencyProperty, value); <br />        } <br /> <br />        <span style="color: blue">private static </span><span style="color: #2b91af">Object </span>CoerceAllowsTransparency(<span style="color: #2b91af">DependencyObject </span>element, <span style="color: blue">object </span>baseValue) <br />        { <br />            <span style="color: green">//WPF will force the Popup into WS_CHILD window when running under partial trust, layered windows <br />            //is only supported for top level windows, so it doesn't make any sense to set the AllowsTransparency to true <br />            //when running under partial trust. <br />           </span><span style="color: blue">return </span>IsRunningUnderFullTrust() ? baseValue : <span style="color: blue">false</span>; <br />        } <br /> <br />        <span style="color: blue">private static </span><span style="color: #2b91af">Boolean </span>IsRunningUnderFullTrust() <br />        { <br />            <span style="color: #2b91af">Boolean </span>isRunningUnderFullTrust = <span style="color: blue">true</span>; <br />            <span style="color: blue">try <br />           </span>{ <br />                <span style="color: #2b91af">NamedPermissionSet </span>permissionSet = <span style="color: blue">new</span><span style="color: #2b91af">NamedPermissionSet</span>(<span style="color: #a31515">"FullTrust"</span>); <br />                permissionSet.Demand(); <br />            } <br />            <span style="color: blue">catch</span>(<span style="color: #2b91af">SecurityException</span>) <br />            { <br />                isRunningUnderFullTrust = <span style="color: blue">false</span>; <br />            } <br /> <br />            <span style="color: blue">return </span>isRunningUnderFullTrust; <br />        } <br /> <br />        <span style="color: blue">private static void </span>OnAllowsTransparencyChanged(<span style="color: #2b91af">DependencyObject </span>sender, <span style="color: #2b91af">DependencyPropertyChangedEventArgs </span>e) <br />        { <br />            <span style="color: #2b91af">Control </span>element = (<span style="color: #2b91af">Control</span>)sender; <br />            CheckElementType(element); <br /> <br />            <span style="color: blue">if</span>(element.IsLoaded) <br />            { <br />                <span style="color: green">//Find the Popup logical element root. <br />               </span><span style="color: #2b91af">Popup </span>popup = GetPopupFromVisualChild(element); <br />                <span style="color: blue">if</span>(popup != <span style="color: blue">null</span>) popup.SetValue(<span style="color: #2b91af">Popup</span>.AllowsTransparencyProperty, e.NewValue); <br />            } <br />            <span style="color: blue">else <br />           </span>{ <br />                <span style="color: blue">var </span>templateDescriptor = <span style="color: #2b91af">DependencyPropertyDescriptor</span>.FromProperty(<span style="color: #2b91af">Control</span>.TemplateProperty, element.GetType()); <br /> <br />                <span style="color: #2b91af">EventHandler </span>handler = <span style="color: blue">null</span>; <br />                handler = (obj, args) => <br />               { <br />                   <span style="color: green">//Not clear why the BeginInvoke call is needed here, but this could effectively <br />                   //workaround cyclic reference exception when evaluating ToolTip/ContextMenu's Style property. <br />                  </span>element.Dispatcher.BeginInvoke(<span style="color: #2b91af">DispatcherPriority</span>.Send, <span style="color: blue">new</span><span style="color: #2b91af">Action</span>(<span style="color: blue">delegate <br />                  </span>{ <br />                       SetAllowsTransparencyInternal(element, e.NewValue); <br />                   })); <br /> <br />                   <span style="color: green">//Clear event handler to avoid resource leak. <br />                  </span>templateDescriptor.RemoveValueChanged(element, handler); <br />               }; <br /> <br />                templateDescriptor.AddValueChanged(element, handler); <br />            } <br />        } <br /> <br />        <span style="color: blue">private static void </span>CheckElementType(<span style="color: #2b91af">Control </span>element) <br />        { <br />            <span style="color: blue">if</span>(!(element <span style="color: blue">is </span><span style="color: #2b91af">ToolTip </span>|| element <span style="color: blue">is </span><span style="color: #2b91af">ContextMenu</span>)) <br />            { <br />                <span style="color: blue">throw new </span><span style="color: #2b91af">NotSupportedException</span>(<span style="color: #a31515">"AllowsTransparency attached property can only be applied to ToolTip or ContextMenu"</span>); <br />            } <br />        } <br /> <br />        <span style="color: blue">private static void </span>SetAllowsTransparencyInternal(<span style="color: #2b91af">Control </span>element, <span style="color: #2b91af">Object </span>value) <br />        { <br />            <span style="color: #2b91af">ToolTip </span>tooTip = element <span style="color: blue">as </span><span style="color: #2b91af">ToolTip</span>; <br />            <span style="color: #2b91af">ContextMenu </span>contextMenu = element <span style="color: blue">as </span><span style="color: #2b91af">ContextMenu</span>; <br /> <br />            <span style="color: green">// Set the IsOpen property to true to let the ToolTip/ContextMenu create Popup instance early, since <br />            // we are only interesting in Popup.AllowsTransparency property rather than <br />            // opening the ToolTip/ContextMenu, set its Visibility to Collapsed. <br />           </span>element.Visibility = <span style="color: #2b91af">Visibility</span>.Collapsed; <br />            <span style="color: blue">if</span>(tooTip != <span style="color: blue">null</span>) <br />            { <br />                tooTip.IsOpen = <span style="color: blue">true</span>; <br />            } <br />            <span style="color: blue">else if</span>(contextMenu != <span style="color: blue">null</span>) <br />            { <br />                contextMenu.IsOpen = <span style="color: blue">true</span>; <br />            } <br /> <br />            <span style="color: green">//Find the Popup logical element root. <br />           </span><span style="color: #2b91af">Popup </span>popup = GetPopupFromVisualChild(element); <br />            <span style="color: blue">if</span>(popup != <span style="color: blue">null</span>) popup.SetValue(<span style="color: #2b91af">Popup</span>.AllowsTransparencyProperty, value); <br /> <br />            <span style="color: green">//Set properties back to what it is initially. <br />           </span><span style="color: blue">if</span>(tooTip != <span style="color: blue">null</span>) <br />            { <br />                tooTip.ClearValue(<span style="color: #2b91af">ToolTip</span>.IsOpenProperty); <br />            } <br />            <span style="color: blue">else if</span>(contextMenu != <span style="color: blue">null</span>) <br />            { <br />                contextMenu.ClearValue(<span style="color: #2b91af">ToolTip</span>.IsOpenProperty); <br />            } <br />            element.ClearValue(<span style="color: #2b91af">FrameworkElement</span>.VisibilityProperty); <br />        } <br /> <br />        <span style="color: blue">private static </span><span style="color: #2b91af">Popup </span>GetPopupFromVisualChild(<span style="color: #2b91af">Visual </span>child) <br />        { <br />            <span style="color: #2b91af">Visual </span>parent = child; <br />            <span style="color: #2b91af">FrameworkElement </span>visualRoot = <span style="color: blue">null</span>; <br /> <br />            <span style="color: green">//Traverse the visual tree up to find the PopupRoot instance. <br />           </span><span style="color: blue">while</span>(parent != <span style="color: blue">null</span>) <br />            { <br />                visualRoot = parent <span style="color: blue">as </span><span style="color: #2b91af">FrameworkElement</span>; <br />                parent = <span style="color: #2b91af">VisualTreeHelper</span>.GetParent(parent) <span style="color: blue">as </span><span style="color: #2b91af">Visual</span>; <br />            } <br /> <br />            <span style="color: #2b91af">Popup </span>popup = <span style="color: blue">null</span>; <br /> <br />            <span style="color: green">// Examine the PopupRoot's logical parent to get the Popup instance. <br />            // This might break in the future since it relies on the internal implementation of Popup's element tree. <br />           </span><span style="color: blue">if</span>(visualRoot != <span style="color: blue">null</span>) <br />            { <br />                popup = visualRoot.Parent <span style="color: blue">as </span><span style="color: #2b91af">Popup</span>; <br />            } <br /> <br />            <span style="color: blue">return </span>popup; <br />        } <br />    } <br />} <br /></p> <p>I have fully commented the code so it might be a little bit more straightforward to read. All in All, the implementation is a little bit like a hack, and might be broken in the future version of WPF as I commented above:) <br />Here is the complete VS project: <a href="http://cid-fd9a0f1f8dd06954.office.live.com/self.aspx/Public/Code%20Samples/WPF/HwndHostInPopupDemo.zip">HwndHostInPopupDemo.zip</a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com6tag:blogger.com,1999:blog-7563089691408821967.post-30571139082629859072008-06-15T15:39:00.002+08:002008-12-29T10:50:55.470+08:00When Selector.IsSynchronizedWithCurrentItem will be true?<p>This looks like an obvious question, you when set it to true either through XAML or code, then the Selector's (such as ListBox and ComboBox) selection operation will drive the setting of the current item within CollectionView which presents the data bound source collection, so when you want to synchronize current item with the selected item, you could write something like the following XAML snippet: <br /><span style="color: blue"> <br /><</span><span style="color: #a31515">Page</span><span style="color: red">xmlns</span><span style="color: blue">=</span>"<span style="color: blue">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br />         <span style="color: red">xmlns:x</span><span style="color: blue">=</span>"<span style="color: blue">http://schemas.microsoft.com/winfx/2006/xaml</span>" <br />         <span style="color: red">xmlns:sys</span><span style="color: blue">=</span>"<span style="color: blue">clr-namespace:System;assembly=mscorlib</span>"<span style="color: blue">> <br />  <</span><span style="color: #a31515">Page.Resources</span><span style="color: blue">> <br />    <</span><span style="color: #a31515">x:Array</span><span style="color: red">x:Key</span><span style="color: blue">=</span>"<span style="color: blue">data</span>" <span style="color: red">Type</span><span style="color: blue">=</span>"<span style="color: blue">{x:Type sys:String}</span>"<span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Paviel Dedved<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Andriy Shevchenko<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Paolo Maldini<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Robert Baggio<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Alessandro Del Piero<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />    </</span><span style="color: #a31515">x:Array</span><span style="color: blue">> <br />  </</span><span style="color: #a31515">Page.Resources</span><span style="color: blue">> <br />  <</span><span style="color: #a31515">StackPanel </span><span style="color: red">DataContext</span><span style="color: blue">=</span>"<span style="color: blue">{StaticResource data}</span>"<span style="color: blue">> <br />    <</span><span style="color: #a31515">ListBox </span><span style="color: red">ItemsSource</span><span style="color: blue">=</span>"<span style="color: blue">{Binding}</span>" <span style="color: red">Margin</span><span style="color: blue">=</span>"<span style="color: blue">5</span>" <span style="color: red">IsSynchronizedWithCurrentItem</span><span style="color: blue">=</span>"<span style="color: blue">True</span>"<span style="color: blue">/> <br />    <</span><span style="color: #a31515">ComboBox </span><span style="color: red">ItemsSource</span><span style="color: blue">=</span>"<span style="color: blue">{Binding}</span>" <span style="color: red">Margin</span><span style="color: blue">=</span>"<span style="color: blue">5</span>" <span style="color: red">IsSynchronizedWithCurrentItem</span><span style="color: blue">=</span>"<span style="color: blue">True</span>"<span style="color: blue">/> <br />    <</span><span style="color: #a31515">TextBlock</span><span style="color: blue">></span>CurrentItem: <span style="color: blue"><</span><span style="color: #a31515">TextBlock </span><span style="color: red">Text</span><span style="color: blue">=</span>"<span style="color: blue">{Binding /}</span>"<span style="color: blue">/> <br />    </</span><span style="color: #a31515">TextBlock</span><span style="color: blue">> <br />  </</span><span style="color: #a31515">StackPanel</span><span style="color: blue">> <br /></</span><span style="color: #a31515">Page</span><span style="color: blue">></span></p> <a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a> <p>From the above code, the ListBox and ComboBox is data bound to the same source collection, but for each ItemsSource binding, a unique CollectionView is generated, so that both ListBox and ComboBox can make independent selection without affecting each other, this is what most developers expect. So if you want both ListBox and ComboBox to drive the currency, you need to explicitly set the IsSynchronizedWithCurrentItem property to true, this is pretty obvious and straightforward, isn't it?</p> <p>If you think this is an obvious thing to do, then I have another XAMLPad ready example which might confuse you: <br /><span style="color: blue"> <br /><</span><span style="color: #a31515">Page</span><span style="color: red">xmlns</span><span style="color: blue">=</span>"<span style="color: blue">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br />      <span style="color: red">xmlns:x</span><span style="color: blue">=</span>"<span style="color: blue">http://schemas.microsoft.com/winfx/2006/xaml</span>" <br />      <span style="color: red">xmlns:sys</span><span style="color: blue">=</span>"<span style="color: blue">clr-namespace:System;assembly=mscorlib</span>"<span style="color: blue">> <br />  <</span><span style="color: #a31515">Page.Resources</span><span style="color: blue">> <br />    <</span><span style="color: #a31515">x:Array</span><span style="color: red">x:Key</span><span style="color: blue">=</span>"<span style="color: blue">data</span>" <span style="color: red">Type</span><span style="color: blue">=</span>"<span style="color: blue">{x:Type sys:String}</span>"<span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Paviel Dedved<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Andriy Shevchenko<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Paolo Maldini<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Robert Baggio<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />      <</span><span style="color: #a31515">sys:String</span><span style="color: blue">></span>Alessandro Del Piero<span style="color: blue"></</span><span style="color: #a31515">sys:String</span><span style="color: blue">> <br />    </</span><span style="color: #a31515">x:Array</span><span style="color: blue">> <br />    <</span><span style="color: #a31515">CollectionViewSource </span><span style="color: red">x:Key</span><span style="color: blue">=</span>"<span style="color: blue">src</span>" <span style="color: red">Source</span><span style="color: blue">=</span>"<span style="color: blue">{StaticResource data}</span>"<span style="color: blue">/> <br />  </</span><span style="color: #a31515">Page.Resources</span><span style="color: blue">> <br />  <</span><span style="color: #a31515">StackPanel </span><span style="color: red">DataContext</span><span style="color: blue">=</span>"<span style="color: blue">{StaticResource src}</span>"<span style="color: blue">> <br />    <</span><span style="color: #a31515">ListBox </span><span style="color: red">ItemsSource</span><span style="color: blue">=</span>"<span style="color: blue">{Binding}</span>" <span style="color: red">Margin</span><span style="color: blue">=</span>"<span style="color: blue">5</span>"<span style="color: blue">/> <br />    <</span><span style="color: #a31515">ComboBox </span><span style="color: red">ItemsSource</span><span style="color: blue">=</span>"<span style="color: blue">{Binding}</span>" <span style="color: red">Margin</span><span style="color: blue">=</span>"<span style="color: blue">5</span>"<span style="color: blue">/> <br />    <</span><span style="color: #a31515">TextBlock</span><span style="color: blue">></span>CurrentItem: <span style="color: blue"><</span><span style="color: #a31515">TextBlock </span><span style="color: red">Text</span><span style="color: blue">=</span>"<span style="color: blue">{Binding /}</span>"<span style="color: blue">/> <br />    </</span><span style="color: #a31515">TextBlock</span><span style="color: blue">> <br />  </</span><span style="color: #a31515">StackPanel</span><span style="color: blue">> <br /></</span><span style="color: #a31515">Page</span><span style="color: blue">></span></p> <a href="http://11011.net/software/vspaste"></a> <p>If you run above XAML snippet in XAMLPad, you will find that both ListBox and ComBox can drive the currency as if the IsSynchronizedWithCurrentItem property is set to true. so what happens?</p> <p>If you use reflector to example the source code of the Selector, if you will the SetSynchronizationWithCurrentItem() method implementation as follows:</p> <p><span style="color: blue">private void </span>SetSynchronizationWithCurrentItem() <br />{ <br />    <span style="color: blue">bool</span>? isSynchronizedWithCurrentItem = IsSynchronizedWithCurrentItem; <br />    <span style="color: blue">bool </span>oldSync = IsSynchronizedWithCurrentItemPrivate; <br />    <span style="color: blue">bool </span>newSync; <br /> <br />    <span style="color: blue">if </span>(isSynchronizedWithCurrentItem.HasValue) <br />    { <br />        <span style="color: green">// if there's a value, use it <br />        </span>newSync = isSynchronizedWithCurrentItem.Value; <br />    } <br />    <span style="color: blue">else <br />    </span>{ <br />        <span style="color: green">// when the value is null, synchronize if selection mode is Single <br />        // and there's a non-default view. <br />        </span>SelectionMode mode = (SelectionMode)GetValue(ListBox.SelectionModeProperty); <br />        newSync = (mode == SelectionMode.Single) && <br />        !CollectionViewSource.IsDefaultView(Items.CollectionView); <br />    } <br /> <br />    IsSynchronizedWithCurrentItemPrivate = newSync; <br /> <br />    <span style="color: blue">if </span>(!oldSync && newSync) <br />    { <br />        SetSelectedToCurrent(); <br />    } <br />}</p> <a href="http://11011.net/software/vspaste"></a> <p>From the bolded line of code, it seems that this is an intentional behaviour, which means that if the SelectionMode of Selector is Single, and the CollectionView data bound to the Selector is not the implicit, default View generated by WPF's data binding engine, Selector will drive the currency. This might not make sense to you, but after the following explanation, I hope it finally makes sense to you.</p> <p>There are two aspects for this issue, the first is multi selection, and the second is non-default view.</p> <p>First, it's no brainer that when the Selector is in Multiple SelectionMode, WPF can not take on current synchronization, because there is no such concept of "multi-current items" from the perspective of each ICollectionView implementation(CollectionView for IEnumerable, ListCollectionView for IList and BindingListCollectionView for IBindingList etc), you might think that WPF can choose one item from the selected items as the current item, but it's hard to figure out which one is the best candidate for current item, because this decision usually varies tremendously in different scenarios, and it's nearly impossible for a general purpose framework like WPF to make a good decision for you.</p> <p>Second, WPF drives currency if you are using the non-default explicit CollectionView, this usually is achieved by declaring the underlying data to be CollectionViewSource, because when you use CollectionViewSource which is a XAML friendly version of CollectionView, WPF assumes that you are mostly likely wanting to enable master/detail binding, and it's obvious that you need the currency synchronization service provided by Selector. So WPF thinks that it's a good default and correct behaviour to implicitly turn on currency synchronization for you.</p> <p>Knowing that this is the heuristics WPF makes on how you are going to use the data, and it might cause some incorrect behaviour as reported in <a href="http://forums.msdn.microsoft.com/en-US/wpf/thread/e84a4c4b-38c4-489e-9ff2-4ccec3730ea6">this WPF MSDN forum thread</a>, so it might worth a blog entry to point this out:)</p> <p>BTW, for any one who thinks that the {Binding /} syntax is alien to them, you could refer to <a href="http://www.interact-sw.co.uk/iangblog/2007/09/03/bindtocurrent">this Ian Griffith's excellent blog post</a> for explanation.</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com4tag:blogger.com,1999:blog-7563089691408821967.post-35427539415672160762008-05-25T22:18:00.001+08:002008-08-14T15:21:39.672+08:00The Power of Shader - Part One<p>.NET Framework 3.5 SP1 Beta1 has been released, this release is not simply a service pack release as we've known for many years of what  SP actually means. It seems that Microsoft has changed its strategy, and start to include new features in SP release.</p> <p>One of the most exciting feature of this release is a new hardware accelerated, pixel shader based custom effect architecture which drives the software, WIC based bitmap effect architecture into obsolete. This new architecture has tackle many of the performance problems inherent with previous stale architecture. On one hand, the new custom effect will run in composition thread rather than UI thread, which means that it will not tie up the UI thread as the previous effect does, on the other hand, it will be hardware accelerated if your GPU supports pixel shader 2.0 or above. If you don't have decent GPU, the effect will be emulated using software, RenderTargetBitmap is a special case, if any visual which has effect applied to it needs to be rasterized into the RenderTargetBitmap, it will go through software rendering path.</p> <p>If you have read the <a href="http://blogs.msdn.com/greg_schechter/archive/2008/05/12/a-series-on-gpu-based-effects-for-wpf.aspx">blog series</a> written by Greg Schechter about the new shader effect feature, you could have been familiar with how to create simple effects. But those shader effect expects you to pass in the pre-compiled HLSL bytecode, which really requires you to use the shader compiler (fxc.exe) which is shipped with the DirectX SDK to compile the shader first, then pass it to the ShaderEffect derivative's constructor. How about if I want to directly pass in the source shader string, and have it get compiled and executed at runtime, this is called online compilation as compared with offline compilation with fxc.exe tool. Fortunately, I could use the Microsoft.DirectX.Direct3D.ShaderLoader class which is part of the managed DirectX API which can do exactly what I want, Here is the ShaderEffect implementation which supports online compilation:</p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">SourceShaderEffect</span> : <span style="color: rgb(43,145,175)">ShaderEffect <br /></span>{ <br />    <span style="color: rgb(0,128,0)">// The custom effect expects pixel shader 2.0 support. <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">readonly</span> <span style="color: rgb(43,145,175)">String</span> PixelShaderProfile = <span style="color: rgb(163,21,21)">"ps_2_0"</span>; <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">readonly</span> <span style="color: rgb(43,145,175)">ShaderFlags</span> ShaderFlags = <span style="color: rgb(43,145,175)">ShaderFlags</span>.None; <br /> <br />    <span style="color: rgb(0,128,0)">// We use "main" as the default function name for the shader. <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">readonly</span> <span style="color: rgb(43,145,175)">String</span> DefaultFunctionName = <span style="color: rgb(163,21,21)">"main"</span>; <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Constructs a new SourceShaderEffect instance. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="shaderFilePath"></span><span style="color: rgb(0,128,0)">path to the source shader file</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="functionName"></span><span style="color: rgb(0,128,0)">shader entry function name</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(0,0,255)">public</span> SourceShaderEffect(<span style="color: rgb(43,145,175)">String</span> shaderFilePath, <span style="color: rgb(43,145,175)">String</span> functionName) <br />    { <br />        <span style="color: rgb(43,145,175)">GraphicsStream</span> stream = <span style="color: rgb(43,145,175)">ShaderHelper</span>.TryThrowCriticalException(() => <br />        { <br />            <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(43,145,175)">ShaderLoader</span>.CompileShaderFromFile( <br />                shaderFilePath, <br />                functionName, <br />                <span style="color: rgb(0,0,255)">null</span>, <br />                PixelShaderProfile, <br />                ShaderFlags); <br />        }); <br /> <br />        <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">null</span> != stream) <br />        { <br />            <span style="color: rgb(0,0,255)">base</span>.PixelShader = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PixelShader</span>(); <br />            <span style="color: rgb(0,0,255)">base</span>.PixelShader.SetStreamSource(stream); <br />        } <br />    } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Constructs a new SourceShaderEffect instance. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="shaderStream"></span><span style="color: rgb(0,128,0)">stream which contains the shader source code.</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="functionName"></span><span style="color: rgb(0,128,0)">shader entry function name</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(0,0,255)">public</span> SourceShaderEffect(<span style="color: rgb(43,145,175)">Stream</span> shaderStream, <span style="color: rgb(43,145,175)">String</span> functionName) <br />    { <br />        <span style="color: rgb(43,145,175)">GraphicsStream</span> stream = <span style="color: rgb(43,145,175)">ShaderHelper</span>.TryThrowCriticalException(() => <br />       { <br />           <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(43,145,175)">ShaderLoader</span>.CompileShaderFromStream( <br />               shaderStream, <br />               functionName, <br />               <span style="color: rgb(0,0,255)">null</span>, <br />               PixelShaderProfile, <br />               ShaderFlags); <br />       }); <br /> <br />        <span style="color: rgb(0,0,255)">if</span> (stream != <span style="color: rgb(0,0,255)">null</span>) <br />        { <br />            <span style="color: rgb(0,0,255)">base</span>.PixelShader = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PixelShader</span>(); <br />            <span style="color: rgb(0,0,255)">base</span>.PixelShader.SetStreamSource(stream); <br />        } <br />    } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Constructs a new SourceShaderEffect instance with default shader entry function name. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="shaderFilePath"></span><span style="color: rgb(0,128,0)">path to the source shader file</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(0,0,255)">public</span> SourceShaderEffect(<span style="color: rgb(43,145,175)">String</span> shaderFilePath) : <span style="color: rgb(0,0,255)">this</span>(shaderFilePath, DefaultFunctionName) { } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Constructs a new SourceShaderEffect instance with default shader entry function name. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="shaderStream"></span><span style="color: rgb(0,128,0)">stream which contains the shader source code.</span><span style="color: rgb(128,128,128)"></param> <br /></span>    <span style="color: rgb(0,0,255)">public</span> SourceShaderEffect(<span style="color: rgb(43,145,175)">Stream</span> shaderStream) : <span style="color: rgb(0,0,255)">this</span>(shaderStream, DefaultFunctionName) { } <br />}</p> <p> </p> <p>There is no fancy stuff in this code, with the above code, you could simply pass a file path or stream to the shader source code, and you are done with it. Let's create a simple effect to see if it actually works, the following is a Sepia shader which I learnt from <a href="http://ati.amd.com/developer/shaderx/shaderx_imageprocessing.pdf">this</a> nice article:</p> <p>sampler2D s0 : register(s0); <br />float4 main(float2 tex : TEXCOORD0) : COLOR <br />{ <br />    float4 color = tex2D(s0, tex); <br />    float value = color[0] * 0.299f + color[1] * 0.587f + color[2] * 0.114f; <br />    return tex2D(s0, value); <br />}</p> <p>Let's save it into a file called Sepia.fx, and add it as a resource file into the WPF project you create from visual studio. In XAML, you could place an Image element, and at the code behind, you could write something like the following:</p> <p>image.Effect = CreateSourceShaderEffect(<span style="color: rgb(163,21,21)">"Sepia"</span>);</p> <p> <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">ShaderEffect</span> CreateSourceShaderEffect(<span style="color: rgb(43,145,175)">String</span> shaderLocation) <br />{ <br />    <span style="color: rgb(43,145,175)">ShaderEffect</span> effect = <span style="color: rgb(0,0,255)">null</span>; <br />    shaderLocation = <span style="color: rgb(163,21,21)">"pack://application:,,,/Shaders/"</span> + shaderLocation + <span style="color: rgb(163,21,21)">".fx"</span>; <br />    <span style="color: rgb(43,145,175)">Stream</span> shaderStream = <span style="color: rgb(43,145,175)">Application</span>.GetResourceStream(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Uri</span>(shaderLocation, <span style="color: rgb(43,145,175)">UriKind</span>.Absolute)).Stream; <br />    <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">null</span> != shaderStream) <br />    { <br />        effect = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">SourceShaderEffect</span>(shaderStream); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">return</span> effect; <br />}</p> <p> </p> <p>Then, it seems that you've got everything ready to run, but actually you don't, if you are running under 64 bit Windows, you need to set the visual studio build system to target X86 architecture instead, otherwise you will get a BadImageFormatException. This means that to enable online compilation, your Application should be 32 bit, and it will be emulated when running under 64 bit Windows through WOW64 layer, which might not be what you expect. so if you are targeting 64 bit Windows, and you want your WPF application to be 64 bit, this technique is not the way to go, otherwise, it could be an option for you.</p> <p>The following is a full sample code with another two simple shader effects such as Invert, and Grayscale. On the next installment, I will try to take adventure on shader effects with input parameters and with multi-sampler blending when I learnt how to do them:)</p> <p>Attachment:<a href="http://www.cnblogs.com/Files/sheva/ShaderEffectDemo.zip">ShaderEffectDemo.zip</a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com2tag:blogger.com,1999:blog-7563089691408821967.post-33575375850277080102008-04-19T17:09:00.000+08:002008-04-20T00:34:34.284+08:00Applicatons = Markup + Code<p>I was asked by one of Chinese leading ComSci book publishers to write a book review on Charles Petzold's classical WPF book <em>Applications = Markup + Code</em>, because they've just finished with translating this awesome book into Chinese. Charles is always one of my most respectable .NET gurus, and here is my humble attempt at commentting on his book:</p> <p>Windows Presentation Foundation hits the Windows programming community quite tremendously from the very beginning of its CTP release not only because it's a much better and superior technology than the stale GDI/USER based UI Framework, but also because it brings in a new programming paradigm that most Windows programmers might have never thought of, that is Applications = Markup + Code.</p> <p>Windows Presentation Foundation is Microsoft's first attempt at unifying the programming model both for Windows desktop and the Web going forward (with the introduction of Silverlight, you can employ the same programming concept and techniques to author Rich Internet Applications). Windows Presentation Foundation also enables a new workflow between developers and designers, with Windows Presentation Foundation, designers has become an integral part of the whole software development process, and they will have a vital say over the final appearance of the whole UI, because they are trained for this, and specialized at doing this. This can free the developers from concerning about something they are not good at, such as how to adjust the colour contrast to make the Button looks more esthetically pleasing.</p> <p>Windows Presentation Foundation also integrates text, imaging, graphics (both 2D and 3D), animations, audio/video, documents, printing, and resources management APIs into a single framework, those powerful APIs and toolsets really enable developers to build rich visualization applications. The ability to seamlessly incorporate 2D content over 3D scenes and vice versa is simply stunning which gives possibility to write Northface Demo type of applications.</p> <p>Charles Petzold brings in his years of Windows programming experiences to provide us with a hitchhikers' guide around Windows Presentation Foundation first from the perspective of code, then XAML (Extension Application Markup Language), which might be the ultimate UI definition language for Windows moving forward. From Chapter 1 to Chapter 17, Charles first introduces us the basic WPF controls and how they differ from traditional win32/Windows Forms counterparts, he also teaches us probably the most important core concepts of WPF such as Dependency Properties and Routed Events which is a little bit different from what we've known about properties and events in C#. He also gives us a de tour on how to write a custom layout panel to layout elements/controls in a predefined manner which is pretty useful when you want to roll up your own data presentation controls. In the unique Chapter 18, Charles aggregates what we've learnt from the previous chapters to tell us how to write a full-blown WPF application such as Notepad. From Chapter 19 to Chapter 31, the amazing XAML tour starts. Again in those Chapters, Charles still focus on the basic building block of WPF such as navigation, geometrical transformation, data binding, control templates and data templates etc, but this time, from the XAML perspective, he tells us how to declaratively specify WPF constructs, and how they relate to their code representations, which is very important, this can help developers to better understand XAML, what it can and cannot do.</p> <p>Charles' book just covers hoards of WPF contents and concept which I cannot name it all, but suffice it to say that I find the book <em>Applications = Markup + Code</em> quite complete, informative and interesting to read. I think this book should be put onto every serious WPF developer's bookshelves, what you think?</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-70220176447031569922008-04-05T01:16:00.000+08:002009-01-22T18:26:33.416+08:00WPF, Terminal Services & Bitmap Remoting<p>Everybody who has been playing around with WPF for a reasonable period of time might have known that WPF Application Processing is actually split up into two threads, they are UI thread which runs the dispatcher loop, and processes any GUI, keyboard/mouse/ink input, animation, layout and user initiated messages/events, and there is a hidden unmanaged background thread which is responsible for composing the graphical content (both in hardware or software if fallback is needed), and presenting it to frame buffer for display.</p> <p>The whole architecture of WPF is built around this two threads model, and it has some of the benefits as articulated by Chris Anderson in <a href="http://channel9.msdn.com/ShowPost.aspx?PostID=67522">the two-year long Channel video</a>. One of the benefits Chris mentioned is that with the two threads model, in terminal services scenario, we can have the UI thread running in the TS server, and have the composition thread (or render thread in other nomenclature) running at the TS client console. And the communication between the two threads are handed over to the TS/RDP protocol. This can enable one interesting scenario called primitive remoting or vector remoting. And since UI thread is running at the TS server side, and it maintains the visual tree and the composition is at the TS client side, so in order to keep the client screen up to date, the UI thread will send the edits to the composition tree over the TS/RDP protocol in a highly compressed manner, this not only saves the network bandwidth, because only the edits need to be remoted, but also speeds up the client processing. This type of server/client communication also holds true in the standalone WPF application, the difference is that those two threads are running at the same Win32 process, and the inter-thread communication mechanism is used instead of the TS/RDP wire. To enable primitive remoting, both the TS server and TS client should run under Windows Vista and with desktop composition is enabled, this requirement tremendously narrows down the scenario in which the primitive remoting could be leveraged.</p> <p>The upcoming .NET Framework 3.5 SP1 release would change all of this, in particular, bitmap remoting with sub region update support will always be used even if the TS server and the TS client are equipped to support primitive remoting, primitive remoting can be as good or bad as bitmap remoting, and the scenario in which primitive remoting is enabled is quite rare, because most existing Windows servers such as Windows 2000 or Windows 2003 server families don't support primitive remoting. So for any developer who wants your WPF applications to run reasonably well under TS scenario and running under TS is an important scenario for your applications, you need to take implication of bitmap remoting into consideration beforehand, there are a couple of ways from which you can improve the performance of your WPF application in TS scenario:</p> <ul> <li>Considering using as little animations as possible in your application, and if animations are indispensable, you could try reducing the <a href="http://msdn2.microsoft.com/en-us/library/system.windows.media.animation.timeline.desiredframerate.aspx">DesiredFrameRate</a> (The default value is 6O FPS) of each animations you need to use, usually 25~30 FPS should be enough, and if you don't need high fidelity animation experience, you can use a even lower frame rate. </li> <li>Considering using solid color brushes instead of gradient or tile brushes. </li> <li>Try reducing the number of "hidden redraws" your application needs to perform, "hidden redraws" will be overlaid by its most foreground redraws, but they will still be remoted which waste network bandwidth. You can visualize the "hidden redraws" using the Perforator (part of <a href="http://msdn2.microsoft.com/en-us/library/aa969767.aspx">WPF Performance Suite</a>) with "Show dirty-regions update overlay" CheckBox checked. </li> </ul> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-58246143333531164312008-01-03T20:08:00.000+08:002008-04-19T20:09:03.866+08:00How to Reference Named Elements Within CellTemplate?<p>Getting reference to the named elements within data template is a commonly requested feature of WPF. In the current version of WPF, there is no intuitive and straightforward way to do it, you need to either traverse the visual tree to compare the Name property of each FrameworkElements within the tree, or alternatively you can first off grab the reference to the ContentPresenter within the ControlTemplate, and call DataTemplate.FindName() passing in the ContentPresenter reference as the "templatedParent" argument. Accessing named elements within CellTemplate complicates the things a bit since each Cell will have different instantiation of CellTemplate, the following helper class gives a possible solution to this problem (I also posted it in <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2618181&SiteID=1">this WPF MSDN forum thread</a>): </p> <p><span style="color: blue">using </span>System; <br /><span style="color: blue">using </span>System.Windows; <br /><span style="color: blue">using </span>System.Windows.Media; <br /><span style="color: blue">using </span>System.Windows.Controls; <br /><span style="color: blue">using </span>System.Windows.Controls.Primitives; <br /> <br /><span style="color: blue">namespace </span>Sheva.Windows.Component <br />{ <br />    <span style="color: blue">public static class </span><span style="color: #2b91af">ListViewHelper <br />   </span>{ <br />        <span style="color: gray">/// <summary> <br />        ///</span><span style="color: green">Finds an element that has the provided identifier name within the specified cell template identified by the cellColumn and cellRow <br />       </span><span style="color: gray">/// </summary> <br />        /// <param name="listView"></span><span style="color: green">the referenced ListView control</span><span style="color: gray"></param> <br />        /// <param name="cellColumn"></span><span style="color: green">the index of column from which the CellTemplate will be retrieved</span><span style="color: gray"></param> <br />        /// <param name="cellRow"></span><span style="color: green">the index of the row from which the specified ListViewItem will be retrieved</span><span style="color: gray"></param> <br />        /// <param name="name"></span><span style="color: green">The name of the requested element.</span><span style="color: gray"></param> <br />        /// <returns></span><span style="color: green">The requested element. This can be null reference if no matching element was found.</span><span style="color: gray"></returns> <br />       </span><span style="color: blue">public static </span><span style="color: #2b91af">FrameworkElement </span>FindNameFromCellTemplate(<span style="color: #2b91af">ListView </span>listView, <span style="color: #2b91af">Int32 </span>cellColumn, <span style="color: #2b91af">Int32 </span>cellRow, <span style="color: #2b91af">String</span>name) <br />        { <br />            <span style="color: blue">if</span>(listView == <span style="color: blue">null</span>) <br />            { <br />                <span style="color: blue">throw new</span><span style="color: #2b91af">ArgumentNullException</span>(<span style="color: #a31515">"listView"</span>); <br />            } <br /> <br />            <span style="color: blue">if</span>(!listView.IsLoaded) <br />            { <br />                <span style="color: blue">throw new </span><span style="color: #2b91af">InvalidOperationException</span>(<span style="color: #a31515">"ListView is not yet loaded"</span>); <br />            } <br /> <br />            <span style="color: blue">if </span>(cellRow >= listView.Items.Count || cellRow < 0) <br />            { <br />                <span style="color: blue">throw new </span><span style="color: #2b91af">ArgumentOutOfRangeException</span>(<span style="color: #a31515">"row"</span>); <br />            } <br /> <br />            <span style="color: #2b91af">GridView </span>gridView = listView.View <span style="color: blue">as </span><span style="color: #2b91af">GridView</span>; <br />            <span style="color: blue">if </span>(gridView == <span style="color: blue">null</span>) <br />            { <br />                <span style="color: blue">return null</span>; <br />            } <br /> <br />            <span style="color: blue">if </span>(cellColumn >= gridView.Columns.Count || cellColumn < 0) <br />            { <br />                <span style="color: blue">throw new </span><span style="color: #2b91af">ArgumentOutOfRangeException</span>(<span style="color: #a31515">"column"</span>); <br />            } <br /> <br />            <span style="color: #2b91af">ListViewItem </span>item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[cellRow]) <span style="color: blue">as </span><span style="color: #2b91af">ListViewItem</span>; <br />            <span style="color: blue">if </span>(item != <span style="color: blue">null</span>) <br />            { <br />                <span style="color: blue">if </span>(!item.IsLoaded) <br />                { <br />                    item.ApplyTemplate(); <br />                } <br />                <span style="color: #2b91af">GridViewRowPresenter </span>rowPresenter = GetFrameworkElementByName<<span style="color: #2b91af">GridViewRowPresenter</span>>(item); <br /> <br />                <span style="color: blue">if</span>(rowPresenter != <span style="color: blue">null</span>) <br />                { <br />                    <span style="color: #2b91af">ContentPresenter </span>templatedParent = <span style="color: #2b91af">VisualTreeHelper</span>.GetChild(rowPresenter, cellColumn) <span style="color: blue">as </span><span style="color: #2b91af">ContentPresenter</span>; <br />                    <span style="color: #2b91af">DataTemplate </span>dataTemplate = gridView.Columns[cellColumn].CellTemplate; <br />                    <span style="color: blue">if</span>(dataTemplate != <span style="color: blue">null </span>&& templatedParent != <span style="color: blue">null</span>) <br />                    { <br />                        <span style="color: blue">return </span>dataTemplate.FindName(name, templatedParent) <span style="color: blue">as </span><span style="color: #2b91af">FrameworkElement</span>; <br />                    } <br />                } <br />            } <br /> <br />            <span style="color: blue">return null</span>; <br />        } <br /> <br />        <span style="color: blue">private static </span>T GetFrameworkElementByName<T>(<span style="color: #2b91af">FrameworkElement </span>referenceElement) <span style="color: blue">where </span>T : <span style="color: #2b91af">FrameworkElement <br />       </span>{ <br />            <span style="color: #2b91af">FrameworkElement</span>child = <span style="color: blue">null</span>; <br />            <span style="color: blue">for</span>(<span style="color: #2b91af">Int32 </span>i = 0; i < <span style="color: #2b91af">VisualTreeHelper</span>.GetChildrenCount(referenceElement); i++) <br />            { <br />                child = <span style="color: #2b91af">VisualTreeHelper</span>.GetChild(referenceElement, i) <span style="color: blue">as </span><span style="color: #2b91af">FrameworkElement</span>; <br />                <span style="color: blue">if</span>(child != <span style="color: blue">null </span>&& child.GetType() == <span style="color: blue">typeof</span>(T)) <br />                { <br />                    <span style="color: blue">break</span>; <br />                } <br />                <span style="color: blue">else if</span>(child != <span style="color: blue">null</span>) <br />                { <br />                    child = GetFrameworkElementByName<T>(child); <br />                    <span style="color: blue">if</span>(child != <span style="color: blue">null </span>&& child.GetType() == <span style="color: blue">typeof</span>(T)) <br />                    { <br />                        <span style="color: blue">break</span>; <br />                    } <br />                } <br />            } <br />            <span style="color: blue">return</span>child <span style="color: blue">as</span>T; <br />        } <br />    } <br />} <br /></p> <p>This actually requires a bit of coding, hopefully future version of WPF in particular the ListView control could has a much better content model to make it more flexible and extensible than it is right now. </p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0tag:blogger.com,1999:blog-7563089691408821967.post-4228897911542686892007-11-25T20:19:00.000+08:002008-04-19T20:20:06.086+08:00How To Search Text In WPF FlowDocument?<p>This blog article is a reply to <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2403758&SiteID=1">the recent WPF MSDN forum thread on how to efficiently search text in FlowDocument</a>. The thread starter needs to have the same performance as the search feature in Visual Studio text editor. I don't know how Visual Studio IDE implements the search feature, but in terms of search in FlowDocument, because FlowDocument enables much richer content model, It's presumably much harder to achieve the same search performance as Visual Studio text editor. </p> <p>I have to say that the code I posted in that thread apparently has a serious performance flaw, it introduces a lot of unnecessary iterations. After digging into this issue at the weekend, I finally come up with a method which can achieve the perceived performance, and I think this should be enough at most circumstance. Based on this method, I mocked up a sample code which shows how to perform find and replace feature in FlowDocument, because find and replace is a common feature every text editing tool should provide, this might help others who need this similar feature. The following shows the core code which perform the search:</p> <p><span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br />///</span><span style="color: rgb(0,128,0)"> Find the corresponding</span><span style="color: rgb(128,128,128)"><see cref="TextRange"/></span><span style="color: rgb(0,128,0)"> instance <br /></span><span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> representing the input string given a specified text pointer position. <br /></span><span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br />///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="position"></span><span style="color: rgb(0,128,0)">the current text position</span><span style="color: rgb(128,128,128)"></param> <br />///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="textToFind"></span><span style="color: rgb(0,128,0)">input text</span><span style="color: rgb(128,128,128)"></param> <br />///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="findOptions"></span><span style="color: rgb(0,128,0)">the search option</span><span style="color: rgb(128,128,128)"></param> <br />///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><returns></span><span style="color: rgb(0,128,0)">An</span><span style="color: rgb(128,128,128)"><see cref="TextRange"/></span><span style="color: rgb(0,128,0)"> instance represeneting the matching string withing the text container.</span><span style="color: rgb(128,128,128)"></returns> <br /></span><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">TextRange</span> GetTextRangeFromPosition(<span style="color: rgb(0,0,255)">ref</span> <span style="color: rgb(43,145,175)">TextPointer</span> position, <span style="color: rgb(43,145,175)">String</span> input, <span style="color: rgb(43,145,175)">FindOptions</span> findOptions) <br />{ <br />    <span style="color: rgb(43,145,175)">Boolean</span> matchCase = (findOptions & <span style="color: rgb(43,145,175)">FindOptions</span>.MatchCase) == <span style="color: rgb(43,145,175)">FindOptions</span>.MatchCase; <br />    <span style="color: rgb(43,145,175)">Boolean</span> matchWholeWord = (findOptions & <span style="color: rgb(43,145,175)">FindOptions</span>.MatchWholeWord) == <span style="color: rgb(43,145,175)">FindOptions</span>.MatchWholeWord; <br /> <br />    <span style="color: rgb(43,145,175)">TextRange</span> textRange = <span style="color: rgb(0,0,255)">null</span>; <br /> <br />    <span style="color: rgb(0,0,255)">while</span> (position != <span style="color: rgb(0,0,255)">null</span>) <br />    { <br />        <span style="color: rgb(0,0,255)">if</span> (position.CompareTo(inputDocument.ContentEnd) == 0) <br />        { <br />            <span style="color: rgb(0,0,255)">break</span>; <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">if</span> (position.GetPointerContext(<span style="color: rgb(43,145,175)">LogicalDirection</span>.Forward) == <span style="color: rgb(43,145,175)">TextPointerContext</span>.Text) <br />        { <br />            <span style="color: rgb(43,145,175)">String</span> textRun = position.GetTextInRun(<span style="color: rgb(43,145,175)">LogicalDirection</span>.Forward); <br />            <span style="color: rgb(43,145,175)">StringComparison</span> stringComparison = matchCase ? <span style="color: rgb(43,145,175)">StringComparison</span>.CurrentCulture : <span style="color: rgb(43,145,175)">StringComparison</span>.CurrentCultureIgnoreCase; <br />            <span style="color: rgb(43,145,175)">Int32</span> indexInRun = textRun.IndexOf(input, stringComparison); <br /> <br />            <span style="color: rgb(0,0,255)">if</span> (indexInRun >= 0) <br />            { <br />                position = position.GetPositionAtOffset(indexInRun); <br />                <span style="color: rgb(43,145,175)">TextPointer</span> nextPointer = position.GetPositionAtOffset(input.Length); <br />                textRange = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">TextRange</span>(position, nextPointer); <br /> <br />                <span style="color: rgb(0,0,255)">if</span> (matchWholeWord) <br />                { <br />                    <span style="color: rgb(0,0,255)">if</span> (IsWholeWord(textRange)) <span style="color: rgb(0,128,0)">// Test if the "textRange" represents a word. <br /></span>                    { <br />                        <span style="color: rgb(0,128,0)">// If a WholeWord match is found, directly terminate the loop. <br /></span>                        <span style="color: rgb(0,0,255)">break</span>; <br />                    } <br />                    <span style="color: rgb(0,0,255)">else <br /></span>                    { <br />                        <span style="color: rgb(0,128,0)">// If a WholeWord match is not found, go to next recursion to find it. <br /></span>                        position = position.GetPositionAtOffset(input.Length); <br />                        <span style="color: rgb(0,0,255)">return</span> GetTextRangeFromPosition(<span style="color: rgb(0,0,255)">ref</span> position, input, findOptions); <br />                    } <br />                } <br />                <span style="color: rgb(0,0,255)">else <br /></span>                { <br />                    <span style="color: rgb(0,128,0)">// If a none-WholeWord match is found, directly terminate the loop. <br /></span>                    position = position.GetPositionAtOffset(input.Length); <br />                    <span style="color: rgb(0,0,255)">break</span>; <br />                } <br />            } <br />            <span style="color: rgb(0,0,255)">else <br /></span>            { <br />                <span style="color: rgb(0,128,0)">// If a match is not found, go over to the next context position after the "textRun". <br /></span>                position = position.GetPositionAtOffset(textRun.Length); <br />            } <br />        } <br />        <span style="color: rgb(0,0,255)">else <br /></span>        { <br />            <span style="color: rgb(0,128,0)">//If the current position doesn't represent a text context position, go to the next context position. <br /></span>            <span style="color: rgb(0,128,0)">// This can effectively ignore the formatting or embedded element symbols. <br /></span>            position = position.GetNextContextPosition(<span style="color: rgb(43,145,175)">LogicalDirection</span>.Forward); <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">return</span> textRange; <br />}</p> <a href="http://11011.net/software/vspaste"></a> <p>The code above is part of my FindAndReplaceManager helper class implementation, you can refer to <a href="http://www.cnblogs.com/Files/sheva/SearchInFlowDocumentDemo.zip">the attachment</a> for the complete source code. The code should be pretty straightforward as I've commentted it. The FindAndReplaceManager can support search options such as <span style="color: rgb(43,145,175)">FindOptions</span>.MatchCase and <span style="color: rgb(43,145,175)">FindOptions</span>.MatchWholeWord, aka two commonly used search options. For simplicity, I don't implement reverse search, since this should be really straightforward, instead of using <span style="color: rgb(43,145,175)">LogicalDirection</span>.Forward, you could use <span style="color: rgb(43,145,175)">LogicalDirection</span>.Backward.</p> <p>As I've said, the FindAndReplaceManager should be able to achieve perceived performance at most situation, if you need hard best performance. You'd better choose a more sophisticated search algorithm instead of the bare-bones "start-to-end" search algorithm as is illustrated in the code above.</p> <p>Another alternative you could choose is the internal undocumented search API provided by WPF. The System.Windows.Documents.TextFindEngine class has a static "Find" method, this method is widely used in build-in document readers and viewers such as FlowDocumentReader, FlowDocumentPageViewer, and FlowDocumentScrollViewer. Because TextFindEngine has a much better understanding of the underlying document content structure, it should provide the hard performance benefit you expect. The following helper method shows how to use this method:</p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows; <br /><span style="color: rgb(0,0,255)">using</span> System.Reflection; <br /><span style="color: rgb(0,0,255)">using</span> System.Globalization; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Documents; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Documents <br />{ <br />    [<span style="color: rgb(43,145,175)">Flags</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">enum</span> <span style="color: rgb(43,145,175)">FindFlags <br /></span>    { <br />        FindInReverse = 2, <br />        FindWholeWordsOnly = 4, <br />        MatchAlefHamza = 0x20, <br />        MatchCase = 1, <br />        MatchDiacritics = 8, <br />        MatchKashida = 0x10, <br />        None = 0 <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">DocumentHelper <br /></span>    { <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">MethodInfo</span> findMethod = <span style="color: rgb(0,0,255)">null</span>; <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">TextRange</span> FindText(<span style="color: rgb(43,145,175)">TextPointer</span> findContainerStartPosition,<span style="color: rgb(43,145,175)">TextPointer</span> findContainerEndPosition, <span style="color: rgb(43,145,175)">String</span> input, <span style="color: rgb(43,145,175)">FindFlags</span> flags, <span style="color: rgb(43,145,175)">CultureInfo</span> cultureInfo) <br />        { <br />            <span style="color: rgb(43,145,175)">TextRange</span> textRange = <span style="color: rgb(0,0,255)">null</span>; <br />            <span style="color: rgb(0,0,255)">if</span> (findContainerStartPosition.CompareTo(findContainerEndPosition) < 0) <br />            { <br />                <span style="color: rgb(0,0,255)">try <br /></span>                { <br />                    <span style="color: rgb(0,0,255)">if</span> (findMethod == <span style="color: rgb(0,0,255)">null</span>) <br />                    { <br />                        findMethod = <span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(43,145,175)">FrameworkElement</span>).Assembly.GetType(<span style="color: rgb(163,21,21)">"System.Windows.Documents.TextFindEngine"</span>). <br />                               GetMethod(<span style="color: rgb(163,21,21)">"Find"</span>, <span style="color: rgb(43,145,175)">BindingFlags</span>.Static | <span style="color: rgb(43,145,175)">BindingFlags</span>.Public); <br />                    } <br />                    <span style="color: rgb(43,145,175)">Object</span> result = findMethod.Invoke(<span style="color: rgb(0,0,255)">null</span>, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Object</span>[] { findContainerStartPosition, <br />                    findContainerEndPosition, <br />                    input, flags, <span style="color: rgb(43,145,175)">CultureInfo</span>.CurrentCulture }); <br />                    textRange = result <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(43,145,175)">TextRange</span>; <br />                } <br />                <span style="color: rgb(0,0,255)">catch</span> (<span style="color: rgb(43,145,175)">ApplicationException</span>) <br />                { <br />                    textRange = <span style="color: rgb(0,0,255)">null</span>; <br />                } <br />            } <br /> <br />            <span style="color: rgb(0,0,255)">return</span> textRange; <br />        } <br />    } <br />}</p> <p>Because TextFindEngine.Find() is a non-public API, we should use a bit of reflection code to call it. If you are working on pesonal project, feel free to use it as an alternative, <strong>but never ever use this method in production code</strong>.</p> <p>WPF should provide a much better built-in public API to perform search operation in FlowDocument. I don't know what type of future plan WPF team has, but from my educated guess, WPF should have a much better support on this in the near future.</p> <p>Attachment: <a href="http://www.cnblogs.com/Files/sheva/SearchInFlowDocumentDemo.zip">SearchInFlowDocumentDemo.zip</a></p> <a href="http://11011.net/software/vspaste"></a> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com11tag:blogger.com,1999:blog-7563089691408821967.post-87475932322843141222007-11-25T20:16:00.000+08:002008-04-19T20:17:30.510+08:00DataErrorValidationRule - New Way To Invalidate Data In WPF<p>The WPF 3.5 has introduced a new data validation API aka DataErrorValidationRule, which you can specify on the Binding object, if the data source implements the IDataErrorInfo interface. This new feature enables some of the scenario the previous Custom ValidationRule cannot enable. </p> <p>One of the scenario the custom ValidationRule cannot support is to enable data binding on the properties of the Custom ValidationRule class. Because ValidationRule is not a DependencyOject, you cannot define dependency properties on it to enable data binding. And because ValidationRule is not a part of the element tree, any Binding expression which relies on RelativeSource or ElementName or similar things that needs to walk up the element tree to find the binding source cannot be evaluated successfully. Some community members such as <a href="http://joshsmithonwpf.wordpress.com/">Josh Smith</a> has come up with a hackery to workaround this limitation using the trick he calls "<a href="http://www.codeproject.com/WPF/AttachingVirtualBranches.asp?df=100&forumid=415065&exp=0&select=2110792">Virutal Branches</a>". Or our beloved Dr. WPF's <a href="http://drwpf.com/blog/Home/tabid/36/EntryId/5/Default.aspx">ObjectReference custom Markup Extension</a> as he "bloated" <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2410591&SiteID=1">in this MSDN WPF thread</a>.</p> <p>DataErrorValidationRule drives those "dirty tricks" to obsolete. Because You don't need to bind some values to custom ValidationRule object as validation input parameters. Because when implementing IDataErrorInfo, you do the validation logic at the source object. The following is an example of how to leverage the DataErrorValidationRule API to suppport the type of scenario "Virtual Branches" is trying to enable.</p> <p>First off, the data source class should implement IDataErrorInfo or INotifyPropertyChanged if you want to enable two way data binding as follows:</p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">Person</span> : <span style="color: rgb(43,145,175)">IDataErrorInfo</span>, <span style="color: rgb(43,145,175)">INotifyPropertyChanged <br /></span>{ <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">int</span> age; <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">int</span> min = 0; <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">int</span> max = 150; <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">int</span> Age <br />    { <br />        <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> age; } <br />        <span style="color: rgb(0,0,255)">set <br /></span>        { <br />            age = <span style="color: rgb(0,0,255)">value</span>; <br />            RaisePropertyChanged(<span style="color: rgb(163,21,21)">"Age"</span>); <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">string</span> Error <br />    { <br />        <span style="color: rgb(0,0,255)">get <br /></span>        { <br />            <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">null</span>; <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">int</span> Min <br />    { <br />        <span style="color: rgb(0,0,255)">get <br /></span>        { <br />            <span style="color: rgb(0,0,255)">return</span> min; <br />        } <br />        <span style="color: rgb(0,0,255)">set <br /></span>        { <br />            min = <span style="color: rgb(0,0,255)">value</span>; <br />            RaisePropertyChanged(<span style="color: rgb(163,21,21)">"Min"</span>); <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">int</span> Max <br />    { <br />        <span style="color: rgb(0,0,255)">get <br /></span>        { <br />            <span style="color: rgb(0,0,255)">return</span> max; <br />        } <br />        <span style="color: rgb(0,0,255)">set <br /></span>        { <br />            max = <span style="color: rgb(0,0,255)">value</span>; <br />            RaisePropertyChanged(<span style="color: rgb(163,21,21)">"Max"</span>); <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">string</span> <span style="color: rgb(0,0,255)">this</span>[<span style="color: rgb(0,0,255)">string</span> name] <br />    { <br />        <span style="color: rgb(0,0,255)">get <br /></span>        { <br />            <span style="color: rgb(0,0,255)">string</span> result = <span style="color: rgb(0,0,255)">null</span>; <br /> <br />            <span style="color: rgb(0,0,255)">if</span> (name == <span style="color: rgb(163,21,21)">"Age"</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">this</span>.age < <span style="color: rgb(0,0,255)">this</span>.Min || <span style="color: rgb(0,0,255)">this</span>.age > <span style="color: rgb(0,0,255)">this</span>.Max) <br />                { <br />                    result = <span style="color: rgb(43,145,175)">String</span>.Format(<span style="color: rgb(163,21,21)">"Age must not be less than {0} or greater than {1}."</span>, <span style="color: rgb(0,0,255)">this</span>.Min, <span style="color: rgb(0,0,255)">this</span>.Max); <br />                } <br />            } <br />            <span style="color: rgb(0,0,255)">return</span> result; <br />        } <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">event</span> <span style="color: rgb(43,145,175)">PropertyChangedEventHandler</span> PropertyChanged; <br /> <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> RaisePropertyChanged(<span style="color: rgb(0,0,255)">string</span> propertyName) <br />    { <br />        <span style="color: rgb(43,145,175)">PropertyChangedEventHandler</span> handler = PropertyChanged; <br />        <span style="color: rgb(0,0,255)">if</span> (handler != <span style="color: rgb(0,0,255)">null</span>) <br />        { <br />            handler(<span style="color: rgb(0,0,255)">this</span>, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PropertyChangedEventArgs</span>(propertyName)); <br />        } <br />    } <br />}</p> <p>You can see that Min and Max properties needs to be specified by the user, so we need to bind those two properties to corresponding UI elements as following XAML snippet shows:</p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Window</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Class</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BusinessLayerValidation.Window1</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">xmlns:x</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">Title</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">WPF IDataErrorInfo Sample</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">450</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Height</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">170</span>" <br /><span style="color: rgb(0,0,255)">       </span><span style="color: rgb(255,0,0)">xmlns:src</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">clr-namespace:BusinessLayerValidation</span>"<span style="color: rgb(0,0,255)">> <br /> <br />  <</span><span style="color: rgb(163,21,21)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">src:Person</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Key</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">data</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Key</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">textBoxInError</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">TextBox</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">Style.Triggers</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(163,21,21)">Trigger</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Validation.HasError</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">true</span>"<span style="color: rgb(0,0,255)">> <br />          <</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">ToolTip</span>" <br /><span style="color: rgb(0,0,255)">                 </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding RelativeSource={x:Static RelativeSource.Self}, <br />                        Path=(Validation.Errors)[0].ErrorContent}</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(163,21,21)">Trigger</span><span style="color: rgb(0,0,255)">> <br />      </</span><span style="color: rgb(163,21,21)">Style.Triggers</span><span style="color: rgb(0,0,255)">> <br />    </</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br /> <br />  <</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Margin</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">20</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">DataContext</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding Source={StaticResource data}}</span>"<span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Orientation</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Horizontal</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">60</span>"<span style="color: rgb(0,0,255)">> <br /></span>        Min:(<span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Text</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding Path=Value, ElementName=minSlider}</span>"<span style="color: rgb(0,0,255)">/></span>) <br /><span style="color: rgb(0,0,255)">      </</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">Slider</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Margin</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">10, 0, 0, 0</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">minSlider</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">300</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Orientation</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Horizontal</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">IsSnapToTickEnabled</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">True</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">HorizontalAlignment</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Right</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">TickPlacement</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BottomRight</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">AutoToolTipPlacement</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BottomRight</span>" <br /><span>              <strong>Value</strong></span><strong><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding Path=Min, Mode=TwoWay}</span>" <br /></strong><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Minimum</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Maximum</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">150</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">TickFrequency</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">10</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Orientation</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Horizontal</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">60</span>"<span style="color: rgb(0,0,255)">> <br /></span>        Max:(<span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Text</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding Path=Value, ElementName=maxSlider}</span>"<span style="color: rgb(0,0,255)">/></span>) <br /><span style="color: rgb(0,0,255)">      </</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">Slider</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Margin</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">10, 0, 0, 0</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">maxSlider</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">300</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Orientation</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Horizontal</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">IsSnapToTickEnabled</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">True</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">HorizontalAlignment</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Right</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">TickPlacement</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BottomRight</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">AutoToolTipPlacement</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BottomRight</span>" <br /><span>              <strong>Value</strong></span><strong><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding Path=Max, Mode=TwoWay}</span>" <br /></strong><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Minimum</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">Maximum</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">150</span>" <br /><span style="color: rgb(0,0,255)">              </span><span style="color: rgb(255,0,0)">TickFrequency</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">10</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)">></span>Enter your age:<span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">TextBlock</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">TextBox</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Style</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{StaticResource textBoxInError}</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">textBox</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">TextBox.Text</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(163,21,21)">Binding</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Path</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Age</span>" <br /><span>                <strong>ValidatesOnDataErrors</strong></span><strong><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">True</span>" <br /></strong><span style="color: rgb(0,0,255)">                </span><span style="color: rgb(255,0,0)">UpdateSourceTrigger</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">PropertyChanged</span>"<span style="color: rgb(0,0,255)">> <br />          <</span><span style="color: rgb(163,21,21)">Binding.ValidationRules</span><span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(163,21,21)">ExceptionValidationRule</span><span style="color: rgb(0,0,255)">/> <br />          </</span><span style="color: rgb(163,21,21)">Binding.ValidationRules</span><span style="color: rgb(0,0,255)">> <br />        </</span><span style="color: rgb(163,21,21)">Binding</span><span style="color: rgb(0,0,255)">> <br />      </</span><span style="color: rgb(163,21,21)">TextBox.Text</span><span style="color: rgb(0,0,255)">> <br />    </</span><span style="color: rgb(163,21,21)">TextBox</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(163,21,21)">Window</span><span style="color: rgb(0,0,255)">></span></p> <p>You can see from the XAML shown above that DataErrorValidationRule actually provide a greater flexibility when validating data. For a detailed introduction to DataErrorValidationRule, and its role in the WPF data validation model, you can refer to <a href="http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx">this WPF SDK blog article</a>. </p> <p>For completeness, I've attached full sample project here for further reference.</p> <p>Attachment: <a href="http://www.cnblogs.com/Files/sheva/WPFDataValidation.zip">WPFDataValidation.zip</a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-62951445260497112932007-11-10T20:28:00.000+08:002008-04-19T20:33:17.613+08:00WCF Trip - What Happens To BeginInvoke<p>Recently I came across Nicholas Allen's blog post talking about <a href="http://blogs.msdn.com/drnick/archive/2007/06/12/begininvoke-bugs.aspx">how BeginInvoke breaks when used against proxies generated by WCF client runtime</a>, specifically the ChannelFactory. and his conclusion to the misbehaviour exposed by BeginInvoke right here is something like this (quoted from the original article):</p> <p><em><font color="#008000">The problem is that BeginInvoke knows about and only works with specific types of proxy objects, which do not include the proxy objects generated by ChannelFactory.</font></em></p> <p>Actually Nicholas Allen's reasoning here is kinda like a "technical correct but lack of detailed explanation" statement, if you write something like the following, no one can imagine that you are actually doing something wrong:</p> <p><span style="color: rgb(43,145,175)">String</span> uri = <span style="color: rgb(163,21,21)">"net.tcp://localhost:2222/Services"</span>; <br /><span style="color: rgb(43,145,175)">ChannelFactory</span><<span style="color: rgb(43,145,175)">IEchoService</span>> factory = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ChannelFactory</span><<span style="color: rgb(43,145,175)">IEchoService</span>>(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">NetTcpBinding</span>(), uri); <br /><span style="color: rgb(43,145,175)">IEchoService</span> proxy = factory.CreateChannel(); <br /><span style="color: rgb(43,145,175)">EchoDelegate</span> d = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">EchoDelegate</span>(proxy.Echo); <br /><span style="color: rgb(43,145,175)">IAsyncResult</span> result = d.BeginInvoke(<span style="color: rgb(163,21,21)">"foo"</span>, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">AsyncCallback</span>(Callback), <span style="color: rgb(0,0,255)">null</span>);</p> <p>So what really happens here?</p> <p>Let's first add some piece of code into the original testing code to check some of presumptions I make on the proxy generated by ChannelFactory:</p> <p><span style="color: rgb(43,145,175)">Console</span>.WriteLine(System.Runtime.Remoting.<span style="color: rgb(43,145,175)">RemotingServices</span>.IsTransparentProxy(proxy)); <br /><span style="color: rgb(43,145,175)">Console</span>.WriteLine(System.Runtime.Remoting.<span style="color: rgb(43,145,175)">RemotingServices</span>.GetRealProxy(proxy).GetType()); <br /><span style="color: rgb(43,145,175)">Console</span>.WriteLine(result.IsCompleted);</p> <a href="http://11011.net/software/vspaste"></a> <p>If running the modified code, you will find some of the interesting bits:</p> <ol> <li>The proxy generated by WCF client runtime aka ChannelFactory is actually a TransparentProxy; </li> <li>The RealProxy paired with this TransparentProxy is a System.ServiceModel.Channels.ServiceChannelProxy implementation; </li> <li>When beginInvoking against a proxy generated by ChannelFactory, the call is performed as ordinary synchronous method invocation. </li> </ol> <p>So what's the happening here? How does WCF relate to the remoting architecture such as TransparentProxy and RealProxy metaphors? and specifically why does BeginInvoke break here?</p> <p>To answer those questions, let's first take on the first question, and digg into it, I've spent several hours to examine the implementation of WCF ChannelFactory implementation, one of the greatest discovery I find is that before kicking off the channel stack to process the service request, WCF client runtime will actually intercept every WCF service call by injecting a TransparentProxy and ServiceChannelProxy between WCF service call site and underlying the channel stack. The reason WCF implements the client runtime the way it is is that WCF needs to differ between normal method invocation and WCF service invocation on service proxy objects. how about if you call GetType() on the proxy generated by the ChannelFactory, what type do you expect the GetType() method will return? If you write the code to test, you will get stunned by realizing that GetType() will actually return IEchoService, WTF? How does GetType() method return the interface type object rather than the concrete type object? Actually, WCF has been intercepted the call to GetType(), and revamped it to return the underlying proxied type, thus hiding the real proxy implementation for IEchoService. Another reason WCF intercepts every method call is to differ between synchronous service calls and asynchronous service calls. Imagine you have a WCF service contract like this:</p> <p>[<span style="color: rgb(43,145,175)">ServiceContract</span>] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">interface</span> <span style="color: rgb(43,145,175)">IEchoService <br /></span>{ <br />    [<span style="color: rgb(43,145,175)">OperationContract</span>] <br />    <span style="color: rgb(43,145,175)">String</span> Echo(<span style="color: rgb(43,145,175)">String</span> text); <br />}</p> <p>   If you run the svcutil.exe tool to generate client side proxy implementation for async call just as Nicholas Allen suggested in his <a href="http://blogs.msdn.com/drnick/archive/2007/06/12/begininvoke-bugs.aspx">original article</a>:</p> <p>   <em><font color="#008000">svcutil /language:C# /config:App.config /async net.tcp://localhost:2222/Services</font></em></p> <p>   you will get something like this:</p> <p>[System.CodeDom.Compiler.<span style="color: rgb(43,145,175)">GeneratedCodeAttribute</span>(<span style="color: rgb(163,21,21)">"System.ServiceModel"</span>, <span style="color: rgb(163,21,21)">"3.0.0.0"</span>)] <br />[System.ServiceModel.<span style="color: rgb(43,145,175)">ServiceContractAttribute</span>(ConfigurationName=<span style="color: rgb(163,21,21)">"IEchoService"</span>)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">interface</span> <span style="color: rgb(43,145,175)">IEchoService <br /></span>{ <br />    <br />    [System.ServiceModel.<span style="color: rgb(43,145,175)">OperationContract</span>] <br />    <span style="color: rgb(0,0,255)">string</span> Echo(<span style="color: rgb(0,0,255)">string</span> text); <br />    <br />    [System.ServiceModel.<span style="color: rgb(43,145,175)">OperationContract</span>] <br />    System.<span style="color: rgb(43,145,175)">IAsyncResult</span> BeginEcho(<span style="color: rgb(0,0,255)">string</span> text, System.<span style="color: rgb(43,145,175)">AsyncCallback</span> callback, <span style="color: rgb(0,0,255)">object</span> asyncState); <br />    <br />    <span style="color: rgb(0,0,255)">string</span> EndEcho(System.<span style="color: rgb(43,145,175)">IAsyncResult</span> result); <br />} <br /> <br />[System.CodeDom.Compiler.<span style="color: rgb(43,145,175)">GeneratedCodeAttribute</span>(<span style="color: rgb(163,21,21)">"System.ServiceModel"</span>, <span style="color: rgb(163,21,21)">"3.0.0.0"</span>)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">interface</span> <span style="color: rgb(43,145,175)">IEchoServiceChannel</span> : <span style="color: rgb(43,145,175)">IEchoService</span>, System.ServiceModel.<span style="color: rgb(43,145,175)">IClientChannel <br /></span>{ <br />} <br /> <br />[System.Diagnostics.<span style="color: rgb(43,145,175)">DebuggerStepThroughAttribute</span>()] <br />[System.CodeDom.Compiler.<span style="color: rgb(43,145,175)">GeneratedCodeAttribute</span>(<span style="color: rgb(163,21,21)">"System.ServiceModel"</span>, <span style="color: rgb(163,21,21)">"3.0.0.0"</span>)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">partial</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">EchoServiceClient</span> : System.ServiceModel.<span style="color: rgb(43,145,175)">ClientBase</span><<span style="color: rgb(43,145,175)">IEchoService</span>>, <span style="color: rgb(43,145,175)">IEchoService <br /></span>{ <br />    <br />    <span style="color: rgb(0,0,255)">public</span> EchoServiceClient() <br />    { <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> EchoServiceClient(<span style="color: rgb(0,0,255)">string</span> endpointConfigurationName) : <span style="color: rgb(0,0,255)">base</span>(endpointConfigurationName) <br />    { <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> EchoServiceClient(<span style="color: rgb(0,0,255)">string</span> endpointConfigurationName, <span style="color: rgb(0,0,255)">string</span> remoteAddress) : <br />            <span style="color: rgb(0,0,255)">base</span>(endpointConfigurationName, remoteAddress) <br />    { <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> EchoServiceClient(<span style="color: rgb(0,0,255)">string</span> endpointConfigurationName, System.ServiceModel.<span style="color: rgb(43,145,175)">EndpointAddress</span> remoteAddress) : <span style="color: rgb(0,0,255)">base</span>(endpointConfigurationName, remoteAddress) <br />    { <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> EchoServiceClient(System.ServiceModel.Channels.<span style="color: rgb(43,145,175)">Binding</span> binding, System.ServiceModel.<span style="color: rgb(43,145,175)">EndpointAddress</span>    remoteAddress) : <span style="color: rgb(0,0,255)">base</span>(binding, remoteAddress) <br />    { <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">string</span> Echo(<span style="color: rgb(0,0,255)">string</span> text) <br />    { <br />        <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">base</span>.Channel.Echo(text); <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> System.<span style="color: rgb(43,145,175)">IAsyncResult</span> BeginEcho(<span style="color: rgb(0,0,255)">string</span> text, System.<span style="color: rgb(43,145,175)">AsyncCallback</span> callback, <span style="color: rgb(0,0,255)">object</span> asyncState) <br />    { <br />        <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">base</span>.Channel.BeginEcho(text, callback, asyncState); <br />    } <br />    <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">string</span> EndEcho(System.<span style="color: rgb(43,145,175)">IAsyncResult</span> result) <br />    { <br />        <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">base</span>.Channel.EndEcho(result); <br />    } <br />}</p> <p>From the above code, we find that WCF follows .NET's asynchronous method invocation pattern quite closely by pairing each service operation call XX with a BeginXX and EndXX async call implementation. the code shown above is really clear and standard, but how does it work out actually? How does a BeginXX call will be performed asynchronously? and how does a EndXX kicks in here to finalize the asynchronous service invocation? </p> <p>In order to let the BeginXX and EndXX work as their signature indicate, WCF actually needs to know which service invocation is going to be performed through BeginXX or EndXX calls, to put it another way, WCF needs to know if the BeginXX or EndXX has been called, so it will perform its underlying plumbing to do the magic, in order to get those invocation infomation, WCF needs to have the capability to fine-grained control over the invocation of the service operations exposed by the service contracts. Since TransparentProxy and RealProxy mechanism which is heavily used by the .NET remoting has already haven this capability directly built into the CLR, WCF can leverage this infrastructure to intercept the method calls, and perform its underlying async plumbing at the channel level according to method you are going to invoke.</p> <p>Right now, I am almost finishing answering the first question - How does WCF relate to the remoting architecture such as TransparentProxy and RealProxy metaphors? but how about the second question I raised myself, why does BeginInvoke break when all the TP and RP plumbing is in place? This question is much trickier than it seems to be, after a bit of research on the default implementation of <a href="http://msdn2.microsoft.com/en-us/library/ms973857.aspx">TP and RP mechanism</a> used by .NET remoting using both  <a href="http://www.aisto.com/roeder/dotnet/">.NET reflector</a> and the <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en">rotor 2.0 implementation</a> of CLR, I finally figure out that for the current implementation of TP and RP mechanism, it only supports asynchronous call when the default RemotingProxy is in place, since the ServiceChannelProxy is WCF's own implementation, it gets ignored by the BeginInvoke mechanism, and the BeginInvoke call against WCF's proxies will be performed synchronously.</p> <p>Up until now, all the puzzles has been demystified. BeginInvoke is probably one of the most confusing APIs in the .NET framework as this article and <a href="http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!580.entry">my previous article</a> demonstrates:)</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-49295066128468687042007-10-23T20:50:00.000+08:002008-04-19T20:51:25.603+08:00How To Globally Specify FontFamily In WPF?<p>This question is asked about seven months ago <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1400103&SiteID=1">in WPF MSDN forum</a>, From the begining, I think this should be obvious, just specify the TextElement.FontFamily in the Application level, the font shall be applied to the entire UI elements within the app:</p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(128,0,0)">Application </span><span style="color: rgb(255,0,0)">TextElement.FontFamily</span><span style="color: rgb(0,0,255)">="Constantia"/> </span></p> <p>But the thing is not that so straightforward, since Application is not a DependencyObject, you cannot specify an attached property on it, then how to do this trick?</p> <p>Just recently, I come up with an approach, since nearly every WPF UI control hosts TextBlock inside (either in data template or control template) to display text (except FlowDocument, FlowDocument has a different mechanism to render text), I can specify a Style within Application.Resources for TextBlock, then the style shall be applied to each TextBlocks within the app:</p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Application</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Class</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">GlobalFontSettings.App</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">xmlns:x</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">StartupUri</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Window1.xaml</span>"<span style="color: rgb(0,0,255)">> <br />  <</span><span style="color: rgb(163,21,21)">Application.Resources</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type TextBlock}</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">TextElement.FontFamily</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Constantia</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(163,21,21)">Application.Resources</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(163,21,21)">Application</span><span style="color: rgb(0,0,255)">></span><span style="color: rgb(0,0,255)"> <br /></span></p> <p>     </p> I've written a little test app for it, and it works like a charm, hope this can help others who also want to implement the similar thing in WPF. Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com3tag:blogger.com,1999:blog-7563089691408821967.post-34796601592167694012007-10-17T20:53:00.000+08:002008-04-19T20:54:13.529+08:00Another Way to Undo Implicit Styles (Revisited)<p>In my previous monthly long installment, I showed <a href="http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!492.entry">another way to undo the implicit style</a>, I demonstrated how to leverage the InheritanceBehaviors to break the style lookup chains so you can have a portion of element tree which will directly pick up the system default theme styles. in this post, I will show a different approach to this problem based on the trick <a href="http://blogs.msdn.com/mikehillberg/">Mike Hillberg</a> mentioned in <a href="http://blogs.msdn.com/mikehillberg/archive/2006/09/22/OverrideImplicitStyle.aspx">his blog article</a>.</p> <p>In Mike's original article, he showed that you can undo implicit style by explicitly set the interesting Element's Style property to null, based on this concept, I come up with the following code:</p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.IO; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows; <br /><span style="color: rgb(0,0,255)">using</span> System.Diagnostics; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Markup; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Controls; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Generic; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Components <br />{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">StyleManager <br /></span>    { <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">DependencyProperty</span> IsDefaultStyleEnabledProperty = <span style="color: rgb(43,145,175)">DependencyProperty</span>.RegisterAttached( <br />            <span style="color: rgb(163,21,21)">"IsDefaultStyleEnabled"</span>, <br />            <span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(43,145,175)">Boolean</span>), <br />            <span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(43,145,175)">StyleManager</span>), <br />            <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">FrameworkPropertyMetadata</span>(<span style="color: rgb(0,0,255)">false</span>, <br />                                          <span style="color: rgb(43,145,175)">FrameworkPropertyMetadataOptions</span>.Inherits, <br />                                          <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PropertyChangedCallback</span>(OnIsDefaultStyleEnabledPropertyChanged))); <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">Dictionary</span><<span style="color: rgb(43,145,175)">FrameworkElement</span>, <span style="color: rgb(43,145,175)">Style</span>> oldStyleStore = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Dictionary</span><<span style="color: rgb(43,145,175)">FrameworkElement</span>, <span style="color: rgb(43,145,175)">Style</span>>(); <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> SetIsDefaultStyleEnabled(<span style="color: rgb(43,145,175)">FrameworkElement</span> element, <span style="color: rgb(43,145,175)">Boolean</span> value) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (element == <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ArgumentNullException</span>(<span style="color: rgb(163,21,21)">"element"</span>); <br />            } <br /> <br />            <span style="color: rgb(0,0,255)">if</span> (value) <br />            { <br />                AddStyleToStore(element); <br />            } <br /> <br />            element.SetValue(IsDefaultStyleEnabledProperty, value); <br />        } <br /> <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> OnIsDefaultStyleEnabledPropertyChanged(<span style="color: rgb(43,145,175)">DependencyObject</span> sender, <span style="color: rgb(43,145,175)">DependencyPropertyChangedEventArgs</span> e) <br />        { <br />            <span style="color: rgb(43,145,175)">FrameworkElement</span> element = sender <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(43,145,175)">FrameworkElement</span>; <br />            <span style="color: rgb(0,0,255)">if</span> (element == <span style="color: rgb(0,0,255)">null</span>) <span style="color: rgb(0,0,255)">return</span>; <br /> <br />            <span style="color: rgb(0,0,255)">if</span> ((<span style="color: rgb(43,145,175)">Boolean</span>)e.NewValue) <br />            { <br />                <span style="color: rgb(0,0,255)">if</span> (!(<span style="color: rgb(43,145,175)">Boolean</span>)e.OldValue) <br />                { <br />                    AddStyleToStore(element); <br />                } <br /> <br />                element.Style = <span style="color: rgb(0,0,255)">null</span>; <br />            } <br />            <span style="color: rgb(0,0,255)">else <br /></span>            { <br />                <span style="color: rgb(0,0,255)">if</span> (oldStyleStore.ContainsKey(element)) <br />                { <br />                    element.Style = oldStyleStore[element]; <br />                } <br />            } <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> AddStyleToStore(<span style="color: rgb(43,145,175)">FrameworkElement</span> element) <br />        { <br />            <span style="color: rgb(43,145,175)">Debug</span>.Assert(element != <span style="color: rgb(0,0,255)">null</span>, <span style="color: rgb(163,21,21)">"parameter 'element' cannot be null"</span>); <br />            <span style="color: rgb(0,0,255)">if</span> (!oldStyleStore.ContainsKey(element)) <br />            { <br />                <span style="color: rgb(0,0,255)">if</span> (element.ReadLocalValue(<span style="color: rgb(43,145,175)">FrameworkElement</span>.StyleProperty) == <span style="color: rgb(43,145,175)">DependencyProperty</span>.UnsetValue) <br />                { <br />                    <span style="color: rgb(43,145,175)">Style</span> style = element.TryFindResource(element.GetType()) <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(43,145,175)">Style</span>; <br />                    oldStyleStore.Add(element, style); <br />                } <br />                <span style="color: rgb(0,0,255)">else <br /></span>                { <br />                    oldStyleStore.Add(element, element.Style); <br />                } <br />            } <br />        } <br />    } <br />}</p> <p>From the above code, you can see that I've defined a inheritable attached DependencyProperty so when this attached DP is applied in the parent element, all the children elements within its containing scope will has this attached DP inherited, and undo their implicit styles individually. <strike>The cloning of Style property using CreateStyleClone helper method is really necessary since when a style is explicitly set, and you want to turn it off, you should first cache the explicit style in the oldStyleStore, and then set the Style to null, the style within the oldStyleStore dictionary will also be nullified if you don't make a copy of the original style.</strike></p> <p>The following XAML shows how to use the StyleManager class:</p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Window</span><span style="color: rgb(255,0,0)"> x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">Class</span><span style="color: rgb(0,0,255)">="UndoImplicitStyles.Window1" <br /></span>   <span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <br /></span>   <span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">x</span><span style="color: rgb(0,0,255)">="http://schemas.microsoft.com/winfx/2006/xaml" <br /></span>   <span style="color: rgb(255,0,0)">Title</span><span style="color: rgb(0,0,255)">="Undo Implicit Styles"</span><span style="color: rgb(255,0,0)"> Height</span><span style="color: rgb(0,0,255)">="300"</span><span style="color: rgb(255,0,0)"> Width</span><span style="color: rgb(0,0,255)">="300" <br /></span>       <span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">local</span><span style="color: rgb(0,0,255)">="clr-namespace:Sheva.Windows.Components"> <br /></span><span style="color: rgb(163,21,21)">   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(255,0,0)"> x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">Key</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Type</span><span style="color: rgb(255,0,0)"> Button</span><span style="color: rgb(0,0,255)">}"</span><span style="color: rgb(255,0,0)"> TargetType</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Type</span><span style="color: rgb(255,0,0)"> Button</span><span style="color: rgb(0,0,255)">}"> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="FontFamily"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">DynamicResource</span><span style="color: rgb(0,0,255)"> {</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Static</span><span style="color: rgb(255,0,0)"> SystemFonts</span><span style="color: rgb(0,0,255)">.</span><span style="color: rgb(255,0,0)">MessageFontFamilyKey</span><span style="color: rgb(0,0,255)">}}" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="FontSize"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">DynamicResource</span><span style="color: rgb(0,0,255)"> {</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Static</span><span style="color: rgb(255,0,0)"> SystemFonts</span><span style="color: rgb(0,0,255)">.</span><span style="color: rgb(255,0,0)">MessageFontSizeKey</span><span style="color: rgb(0,0,255)">}}" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="FontStyle"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">DynamicResource</span><span style="color: rgb(0,0,255)"> {</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Static</span><span style="color: rgb(255,0,0)"> SystemFonts</span><span style="color: rgb(0,0,255)">.</span><span style="color: rgb(255,0,0)">MessageFontStyleKey</span><span style="color: rgb(0,0,255)">}}" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="FontWeight"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">DynamicResource</span><span style="color: rgb(0,0,255)"> {</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Static</span><span style="color: rgb(255,0,0)"> SystemFonts</span><span style="color: rgb(0,0,255)">.</span><span style="color: rgb(255,0,0)">MessageFontWeightKey</span><span style="color: rgb(0,0,255)">}}" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Foreground"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">DynamicResource</span><span style="color: rgb(0,0,255)"> {</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Static</span><span style="color: rgb(255,0,0)"> SystemColors</span><span style="color: rgb(0,0,255)">.</span><span style="color: rgb(255,0,0)">ControlTextBrushKey</span><span style="color: rgb(0,0,255)">}}" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="HorizontalContentAlignment"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="Center" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="VerticalContentAlignment"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="Center" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="ClipToBounds"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="True" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Padding"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="2" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Margin"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="10" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Height"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="30" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Template"> <br /></span><span style="color: rgb(163,21,21)">               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter.Value</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">ControlTemplate</span><span style="color: rgb(255,0,0)"> TargetType</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(163,21,21)">Type</span><span style="color: rgb(255,0,0)"> Button</span><span style="color: rgb(0,0,255)">}"> <br /></span><span style="color: rgb(163,21,21)">                       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Grid</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Rectangle</span><span style="color: rgb(255,0,0)"> x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">="GelBackground"</span><span style="color: rgb(255,0,0)"> Opacity</span><span style="color: rgb(0,0,255)">="1"</span><span style="color: rgb(255,0,0)"> RadiusX</span><span style="color: rgb(0,0,255)">="4"</span><span style="color: rgb(255,0,0)"> RadiusY</span><span style="color: rgb(0,0,255)">="4"</span><span style="color: rgb(255,0,0)"> Stroke</span><span style="color: rgb(0,0,255)">="Black"</span><span style="color: rgb(255,0,0)"> StrokeThickness</span><span style="color: rgb(0,0,255)">="1"> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Rectangle.Fill</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">LinearGradientBrush</span><span style="color: rgb(255,0,0)"> StartPoint</span><span style="color: rgb(0,0,255)">="0,0"</span><span style="color: rgb(255,0,0)"> EndPoint</span><span style="color: rgb(0,0,255)">="0,1"> <br /></span><span style="color: rgb(163,21,21)">                                       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">LinearGradientBrush.GradientStops</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">GradientStop</span><span style="color: rgb(255,0,0)"> Offset</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> Color</span><span style="color: rgb(0,0,255)">="White" /> <br /></span><span style="color: rgb(163,21,21)">                                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">GradientStop</span><span style="color: rgb(255,0,0)"> Offset</span><span style="color: rgb(0,0,255)">="1"</span><span style="color: rgb(255,0,0)"> Color</span><span style="color: rgb(0,0,255)">="#99ccff" /> <br /></span><span style="color: rgb(163,21,21)">                                       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">LinearGradientBrush.GradientStops</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                   </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">LinearGradientBrush</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Rectangle.Fill</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Rectangle</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Rectangle</span><span style="color: rgb(255,0,0)"> x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">="GelShine"</span><span style="color: rgb(255,0,0)"> Margin</span><span style="color: rgb(0,0,255)">="2,2,2,0"</span><span style="color: rgb(255,0,0)"> VerticalAlignment</span><span style="color: rgb(0,0,255)">="Top"</span><span style="color: rgb(255,0,0)"> RadiusX</span><span style="color: rgb(0,0,255)">="6"</span><span style="color: rgb(255,0,0)"> RadiusY</span><span style="color: rgb(0,0,255)">="6"</span><span style="color: rgb(255,0,0)"> Opacity</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> Stroke</span><span style="color: rgb(0,0,255)">="Transparent"</span><span style="color: rgb(255,0,0)"> Height</span><span style="color: rgb(0,0,255)">="15px"> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Rectangle.Fill</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">LinearGradientBrush</span><span style="color: rgb(255,0,0)"> StartPoint</span><span style="color: rgb(0,0,255)">="0,0"</span><span style="color: rgb(255,0,0)"> EndPoint</span><span style="color: rgb(0,0,255)">="0,1"> <br /></span><span style="color: rgb(163,21,21)">                                       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">LinearGradientBrush.GradientStops</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">GradientStop</span><span style="color: rgb(255,0,0)"> Offset</span><span style="color: rgb(0,0,255)">="0"</span><span style="color: rgb(255,0,0)"> Color</span><span style="color: rgb(0,0,255)">="#ccffffff" /> <br /></span><span style="color: rgb(163,21,21)">                                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">GradientStop</span><span style="color: rgb(255,0,0)"> Offset</span><span style="color: rgb(0,0,255)">="1"</span><span style="color: rgb(255,0,0)"> Color</span><span style="color: rgb(0,0,255)">="Transparent" /> <br /></span><span style="color: rgb(163,21,21)">                                       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">LinearGradientBrush.GradientStops</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                                   </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">LinearGradientBrush</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Rectangle.Fill</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Rectangle</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">ContentPresenter</span><span style="color: rgb(255,0,0)"> x</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">="ContentSite"</span><span style="color: rgb(255,0,0)"> Margin</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">TemplateBinding</span><span style="color: rgb(255,0,0)"> Padding</span><span style="color: rgb(0,0,255)">}"</span><span style="color: rgb(255,0,0)"> VerticalAlignment</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">TemplateBinding</span><span style="color: rgb(255,0,0)"> VerticalContentAlignment</span><span style="color: rgb(0,0,255)">}"</span><span style="color: rgb(255,0,0)"> HorizontalAlignment</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">TemplateBinding</span><span style="color: rgb(255,0,0)"> HorizontalContentAlignment</span><span style="color: rgb(0,0,255)">}" /> <br /></span><span style="color: rgb(163,21,21)">                       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Grid</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">ControlTemplate.Triggers</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Trigger</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="IsMouseOver"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="true"> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Rectangle.Fill"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="#99ccff"</span><span style="color: rgb(255,0,0)"> TargetName</span><span style="color: rgb(0,0,255)">="GelBackground" /> <br /></span><span style="color: rgb(163,21,21)">                               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(255,0,0)"> Property</span><span style="color: rgb(0,0,255)">="Rectangle.Opacity"</span><span style="color: rgb(255,0,0)"> Value</span><span style="color: rgb(0,0,255)">="1"</span><span style="color: rgb(255,0,0)"> TargetName</span><span style="color: rgb(0,0,255)">="GelShine" /> <br /></span><span style="color: rgb(163,21,21)">                           </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Trigger</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">ControlTemplate.Triggers</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                   </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">ControlTemplate</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">               </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Setter.Value</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Setter</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Style</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">   </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Button</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Click Me" /> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">CheckBox</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Undo Implicit Style"</span><span style="color: rgb(255,0,0)"> Name</span><span style="color: rgb(0,0,255)">="checkBox"/> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(255,0,0)"> local</span><span style="color: rgb(0,0,255)">:</span><span style="color: rgb(255,0,0)">StyleManager.IsDefaultStyleEnabled</span><span style="color: rgb(0,0,255)">="{</span><span style="color: rgb(163,21,21)">Binding</span><span style="color: rgb(255,0,0)"> ElementName</span><span style="color: rgb(0,0,255)">=checkBox,</span><span style="color: rgb(255,0,0)"> Path</span><span style="color: rgb(0,0,255)">=IsChecked}"> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Button</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Click Me" /> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Button</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Click Me" /> <br /></span><span style="color: rgb(163,21,21)">               </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">                   </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Button</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Click Me" /> <br /></span><span style="color: rgb(163,21,21)">               </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">           </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></span><span style="color: rgb(163,21,21)">       </span><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(163,21,21)">Button</span><span style="color: rgb(255,0,0)"> Content</span><span style="color: rgb(0,0,255)">="Click Me" /> <br /></span><span style="color: rgb(163,21,21)">   </span><span style="color: rgb(0,0,255)"></</span><span style="color: rgb(163,21,21)">StackPanel</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(163,21,21)">Window</span><span style="color: rgb(0,0,255)">> <br /></span></p> <p>When you run above XAML code, you will find that when checking and unchekding the CheckBox, the style of Buttons within the containing StackPanel parent will by toggled between default system style and the implicit style set in the resource.</p> <p>Disclaimer: The above solution just gives you a way to tackle the problem of undoing implicit styles, and it's not an elegant or performant way of doing things, I just come up with it here for completeness rather than as a recommendation. if you want to have a portion of your element tree to just pick up the system default styles, I strongly recommend you to <a href="http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!492.entry">use the approach I demonstrated in my previous article</a>, since presumably this approach is also taken by the Expression Blend.</p> <p>Attachment: <a title="http://www.cnblogs.com/Files/sheva/UndoImplicitStyles.zip" href="http://www.cnblogs.com/Files/sheva/UndoImplicitStyles.zip">UndoImplicitStyles.zip</a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0tag:blogger.com,1999:blog-7563089691408821967.post-7306684233767205422007-10-16T20:55:00.000+08:002008-04-19T20:56:14.541+08:00How To Enumerate All The Bindings Set On A Specfied DependencyObject?<p><a href="http://drwpf.com/blog/">Dr WPF</a> one of the most active WPF MSDN forum participant just posts a reply on <a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2256896&SiteID=1">MSDN forum on how to enumerate all the binding objects set on a specified DependencyObject</a>, <a href="http://drwpf.com/blog/">this guy</a> who seems to have the whole WPF SDK imprinted into his brilliant mind really knows something about WPF:)</p> <p>I just refactored his code a little bit, and made into my own WPF component/control library. kudos, <a href="http://drwpf.com/blog/">Dr WPF</a>:)</p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Data; <br /><span style="color: rgb(0,0,255)">using</span> System.ComponentModel; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Generic; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Components <br />{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">DependencyPropertyHelper <br /></span>    { <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">IList</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>> GetAttachedProperties(<span style="color: rgb(43,145,175)">Object</span> element) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (element == <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ArgumentNullException</span>(<span style="color: rgb(163,21,21)">"element"</span>); <br />            } <br /> <br />            <span style="color: rgb(43,145,175)">List</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>> attachedProperties = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">List</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>>(); <br /> <br />            <span style="color: rgb(0,0,255)">foreach</span> (<span style="color: rgb(43,145,175)">PropertyDescriptor</span> pd <span style="color: rgb(0,0,255)">in</span> <span style="color: rgb(43,145,175)">TypeDescriptor</span>.GetProperties(element, <br />                <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Attribute</span>[] { <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PropertyFilterAttribute</span>(<span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.SetValues | <br />                                                                             <span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.UnsetValues | <br />                                                                             <span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.Valid) })) <br />            { <br />                <span style="color: rgb(43,145,175)">DependencyPropertyDescriptor</span> dpd = <span style="color: rgb(43,145,175)">DependencyPropertyDescriptor</span>.FromProperty(pd); <br />                <span style="color: rgb(0,0,255)">if</span> (dpd != <span style="color: rgb(0,0,255)">null</span> && dpd.IsAttached) <br />                { <br />                    attachedProperties.Add(dpd.DependencyProperty); <br />                } <br />            } <br /> <br />            <span style="color: rgb(0,0,255)">return</span> attachedProperties; <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">IList</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>> GetProperties(<span style="color: rgb(43,145,175)">Object</span> element) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (element == <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ArgumentNullException</span>(<span style="color: rgb(163,21,21)">"element"</span>); <br />            } <br /> <br />            <span style="color: rgb(43,145,175)">List</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>> properties = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">List</span><<span style="color: rgb(43,145,175)">DependencyProperty</span>>(); <br /> <br />            <span style="color: rgb(0,0,255)">foreach</span> (<span style="color: rgb(43,145,175)">PropertyDescriptor</span> pd <span style="color: rgb(0,0,255)">in</span> <span style="color: rgb(43,145,175)">TypeDescriptor</span>.GetProperties(element, <br />                <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Attribute</span>[] { <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PropertyFilterAttribute</span>(<span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.SetValues |  <br />                                                                             <span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.UnsetValues | <br />                                                                             <span style="color: rgb(43,145,175)">PropertyFilterOptions</span>.Valid) })) <br />            { <br />                <span style="color: rgb(43,145,175)">DependencyPropertyDescriptor</span> dpd = <span style="color: rgb(43,145,175)">DependencyPropertyDescriptor</span>.FromProperty(pd); <br />                <span style="color: rgb(0,0,255)">if</span> (dpd != <span style="color: rgb(0,0,255)">null</span>) <br />                { <br />                    properties.Add(dpd.DependencyProperty); <br />                } <br />            } <br /> <br />            <span style="color: rgb(0,0,255)">return</span> properties; <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(43,145,175)">IEnumerable</span><<span style="color: rgb(43,145,175)">Binding</span>> EnumerateBindings(<span style="color: rgb(43,145,175)">DependencyObject</span> element) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (element == <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ArgumentNullException</span>(<span style="color: rgb(163,21,21)">"element"</span>); <br />            } <br /> <br />            <span style="color: rgb(43,145,175)">LocalValueEnumerator</span> lve = element.GetLocalValueEnumerator(); <br /> <br />            <span style="color: rgb(0,0,255)">while</span> (lve.MoveNext()) <br />            { <br />                <span style="color: rgb(43,145,175)">LocalValueEntry</span> entry = lve.Current; <br /> <br />                <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(43,145,175)">BindingOperations</span>.IsDataBound(element, entry.Property)) <br />                { <br />                    <span style="color: rgb(43,145,175)">Binding</span> binding = (entry.Value <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(43,145,175)">BindingExpression</span>).ParentBinding; <br />                    <span style="color: rgb(0,0,255)">yield</span> <span style="color: rgb(0,0,255)">return</span> binding; <br />                } <br />            } <br />        } <br />    } <br />} <br /></p> <a href="http://11011.net/software/vspaste"></a> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-75732800592448738152007-10-06T20:58:00.000+08:002008-04-19T20:59:27.971+08:00ObservableObject - Is This The Proper Implementation of INotifyPropertyChanged?<p>I've not written any blog articles on WPF for a long time, and at the same time, I missed a lot of cool blog articles written by any WPF'ers too. So recently I spent some time reading those blogs published one or two months ago, and really got a lot of new and cool tricks and tips on WPF.</p> <p>One of the article I always enjoy reading is Josh Smith's blog post on <a href="http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/">a base class which implements INotifyPropertyChanged</a> and the those awesome comments following this post. Designing a base class for general data binding purpose is always a challenge, in particular, if you take into consideration things like multi-threading, and the various business scenarios and changing customer needs. I think that's why Microsoft doesn't come up with a default implementation of INotifyPropertyChanged class which developers can subclass to design their business entities. The following is my take on this challenge after reading Josh Smith's original article and those comments following it:</p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Reflection; <br /><span style="color: rgb(0,0,255)">using</span> System.Diagnostics; <br /><span style="color: rgb(0,0,255)">using</span> System.ComponentModel; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Generic;<a href="http://11011.net/software/vspaste"></a> </p> <p><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Data <br />{ <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> This class acts as the base class for any data entity which provides notifications whenever any of its property is changed. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">abstract</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">ObservableObject</span> : <span style="color: rgb(43,145,175)">INotifyPropertyChanged <br /></span>    { <br /><span style="color: rgb(0,0,255)">        #region</span> Data & Constructors <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(43,145,175)">Int32</span> notificationDisabled = 0; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(43,145,175)">Int32</span> notificationEnabled = 1; <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(43,145,175)">String</span> Error_Msg = <span style="color: rgb(163,21,21)">"{0} is not a public property of {1}"</span>; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(43,145,175)">String</span> Error_SuspendNotification = <span style="color: rgb(163,21,21)">"Nested SuspendNotification is not supported"</span>; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(43,145,175)">String</span> Error_ResumeNotification = <span style="color: rgb(163,21,21)">"ResumeNotification without first calling SuspendNotification is not supported"</span>; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(43,145,175)">Int32</span> objectState = notificationEnabled; <br /> <br />        <span style="color: rgb(0,0,255)">protected</span> ObservableObject() <br />        { <br />        } <br /> <br /><span style="color: rgb(0,0,255)">        #endregion <br /> <br />        #region</span> Public Members <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Occurs when a property value changes. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">event</span> <span style="color: rgb(43,145,175)">PropertyChangedEventHandler</span> PropertyChanged; <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Suspend the property changed notification. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><remarks> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> After this call, the property changed notification is disabled. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></remarks> <br /></span>        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> SuspendNotification() <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(43,145,175)">Interlocked</span>.CompareExchange(<span style="color: rgb(0,0,255)">ref</span> objectState, notificationDisabled, notificationEnabled) != notificationEnabled) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">InvalidOperationException</span>(Error_SuspendNotification); <br />            } <br />        } <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Resume the property changed notification. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><remarks> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> After this call, the property changed notification is re-enabled. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></remarks> <br /></span>        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">void</span> ResumeNotification() <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(43,145,175)">Interlocked</span>.CompareExchange(<span style="color: rgb(0,0,255)">ref</span> objectState, notificationEnabled, notificationDisabled) != notificationDisabled) <br />            { <br />                <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">InvalidOperationException</span>(Error_SuspendNotification); <br />            } <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(43,145,175)">Boolean</span> IsNotificationEnabled <br />        { <br />            <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(43,145,175)">Thread</span>.VolatileRead(<span style="color: rgb(0,0,255)">ref</span> objectState) == 1; } <br />        } <br /> <br /><span style="color: rgb(0,0,255)">        #endregion <br /> <br />        #region</span> Protected Members <br /> <br />        <span style="color: rgb(128,128,128)">///<summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Invoked whenever the value of any property is changed. <br /></span>        <span style="color: rgb(128,128,128)">///</summary> <br /></span>        <span style="color: rgb(128,128,128)">///<remarks> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Derived classes can override this method to include any logic after the property is changed. <br /></span>        <span style="color: rgb(128,128,128)">///</remarks> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="propertyName"> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> The name of the property which was changed. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></param> <br /></span>        <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">virtual</span> <span style="color: rgb(0,0,255)">void</span> OnPropertyChanged(<span style="color: rgb(43,145,175)">String</span> propertyName) <br />        { <br />            <span style="color: rgb(0,128,0)">// Do nothing <br /></span>        } <br /> <br />        <span style="color: rgb(128,128,128)">///<summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)">Raise the property changed event. <br /></span>        <span style="color: rgb(128,128,128)">///</summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="propertyName"> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> The name of the property which was changed. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></param> <br /></span>        <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">void</span> RaisePropertyChangedEvent(<span style="color: rgb(43,145,175)">String</span> propertyName) <br />        { <br />            VerifyProperty(propertyName); <br />            <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(43,145,175)">Thread</span>.VolatileRead(<span style="color: rgb(0,0,255)">ref</span> objectState) == notificationDisabled) <br />            { <br />                <span style="color: rgb(0,0,255)">return</span>; <br />            } <br /> <br />            <span style="color: rgb(43,145,175)">PropertyChangedEventHandler</span> handler = PropertyChanged; <br />            <span style="color: rgb(0,0,255)">if</span> (handler != <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,128,0)">// Raise the PropertyChanged event. <br /></span>                handler(<span style="color: rgb(0,0,255)">this</span>, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">PropertyChangedEventArgs</span>(propertyName)); <br />            } <br /> <br />            OnPropertyChanged(propertyName); <br />        } <br /> <br /><span style="color: rgb(0,0,255)">        #endregion <br /> <br />        #region</span> Private Helpers <br /> <br />        [<span style="color: rgb(43,145,175)">Conditional</span>(<span style="color: rgb(163,21,21)">"DEBUG"</span>)] <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> VerifyProperty(<span style="color: rgb(43,145,175)">String</span> propertyName) <br />        { <br />            <span style="color: rgb(43,145,175)">Type</span> type = GetType(); <br />            <span style="color: rgb(43,145,175)">PropertyInfo</span> propertyInfo = type.GetProperty(propertyName, <span style="color: rgb(43,145,175)">BindingFlags</span>.Instance | <span style="color: rgb(43,145,175)">BindingFlags</span>.Public); <br /> <br />            <span style="color: rgb(43,145,175)">Debug</span>.Assert(propertyInfo != <span style="color: rgb(0,0,255)">null</span>, <span style="color: rgb(43,145,175)">String</span>.Format(Error_Msg, propertyName, type.FullName)); <br />        } <br /> <br /><span style="color: rgb(0,0,255)">        #endregion <br /></span>    } <br />}</p> <p>Note that I don't cache the PropertyChangedEventArgs objects as Josh Smith did, properly his cache scheme can improve this performance a little bit, but I think in any interactive UI programming, that kinda performance gain doesn't make any signficant difference. as a bonus, I add two pair methods called SuspendNotification and ResumeNotification, this two methods can be pretty useful when you first initialize the business objects, at this instance, you don't want the UI or the change notification subscribers to be notified about the property change. </p> <p>I've attached a sample project of how to use ObservableObject base class, you can download it from here:</p> <p>Attachment: <a title="http://www.cnblogs.com/Files/sheva/ObservableObjectDemo.zip" href="http://www.cnblogs.com/Files/sheva/ObservableObjectDemo.zip">ObservableObjectDemo.zip</a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0tag:blogger.com,1999:blog-7563089691408821967.post-47533933137510496392007-08-07T21:39:00.000+08:002008-04-19T21:42:02.539+08:00The Tricks with TreeViewItem Expansion (Revisited)<p>Last year, I posted a <a href="http://shevaspace.spaces.live.com/blog/cns%21FD9A0F1F8DD06954%21463.entry">blog article talking about how to expand TreeViewItems</a>, unfortunately, the download link to the source code of that article was broken, and what's more, I lost the original source code myself, so in today's post, I want to revisit this topic with the core piece of code which actually does the trick. </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Controls; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Controls.Primitives; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Controls <br />{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">TreeViewHelper <br /></span>    { <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> ExpanAll(<span style="color: rgb(43,145,175)">TreeView</span> treeView) <br />        { <br />            ExpandSubContainers(treeView); <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> ExpandSubContainers(<span style="color: rgb(43,145,175)">ItemsControl</span> parentContainer) <br />        { <br />            <span style="color: rgb(0,0,255)">foreach</span> (<span style="color: rgb(43,145,175)">Object</span> item <span style="color: rgb(0,0,255)">in</span> parentContainer.Items) <br />            { <br />                <span style="color: rgb(43,145,175)">TreeViewItem</span> currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(43,145,175)">TreeViewItem</span>; <br />                <span style="color: rgb(0,0,255)">if</span> (currentContainer != <span style="color: rgb(0,0,255)">null</span> && currentContainer.Items.Count > 0) <br />                { <br />                    currentContainer.IsExpanded = <span style="color: rgb(0,0,255)">true</span>; <br />                    <span style="color: rgb(0,0,255)">if</span> (currentContainer.ItemContainerGenerator.Status != <span style="color: rgb(43,145,175)">GeneratorStatus</span>.ContainersGenerated) <br />                    { <br />                        currentContainer.ItemContainerGenerator.StatusChanged += <span style="color: rgb(0,0,255)">delegate <br /></span>                        { <br />                            ExpandSubContainers(currentContainer); <br />                        }; <br />                    } <br />                    <span style="color: rgb(0,0,255)">else <br /></span>                    { <br />                        ExpandSubContainers(currentContainer); <br />                    } <br />                } <br />            } <br />        } <br />    } <br />} </p> <p>The key caveat of expanding TreeViewItems is to make sure that the container for the current TreeViewItem has been generated, so you can safely expand it to show all its sub items, that's why I make recursive call when the status of current TreeViewItem's ItemContainerGenerator is set to GeneratorStatus.ContainersGenerated.</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-85610032090236347942007-08-06T21:50:00.000+08:002008-04-19T21:51:57.537+08:00Delegate.BeginInvoke vs ThreadPool.QueueUserWorkItem<p>Today, I come across Chris Brumme's blog on <a href="http://blogs.msdn.com/cbrumme/archive/2003/07/14/51495.aspx">TransparentProxy</a>, I really enjoy his blog, in particular those dedicated to the inner-working of CLR, one of the surprising sentence I read from his <a href="http://blogs.msdn.com/cbrumme/archive/2003/07/14/51495.aspx">TransparentProxy</a> article is that: </p> <p><em>One surprising fact is that this is also why Delegate.BeginInvoke / EndInvoke are so slow compared to equivalent techniques like ThreadPool.QueueUserWorkItem (or UnsafeQueueUserWorkItem if you understand the security implications and want to be really efficient). The codepath for BeginInvoke / EndInvoke quickly turns into the common Message processing code of the general remoting pathway.</em> </p> <p><em> </em>  I have to say I get pretty stunned when reading this, so I write a little piece of code to verify his allegation: </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Diagnostics; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> DelegatePerformance <br />{ <br />    <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(43,145,175)">Program <br /></span>    { <br />        <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> Main(<span style="color: rgb(0,0,255)">string</span>[] args) <br />        { <br />            <span style="color: rgb(43,145,175)">ThreadStart</span> threadStart = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">ThreadStart</span>(() => { }); <br />            <span style="color: rgb(43,145,175)">WaitCallback</span> waitCallback = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">WaitCallback</span>(a => { }); <br />            <span style="color: rgb(43,145,175)">Stopwatch</span> stopWatch = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(43,145,175)">Stopwatch</span>(); <br /> <br />            stopWatch.Start(); <br />            <span style="color: rgb(0,0,255)">for</span> (<span style="color: rgb(0,0,255)">int</span> i = 0; i < 10000; i++) <br />            { <br />                threadStart.BeginInvoke(<span style="color: rgb(0,0,255)">null</span>, <span style="color: rgb(0,0,255)">null</span>); <br />            } <br />            stopWatch.Stop(); <br />            <span style="color: rgb(43,145,175)">Console</span>.WriteLine(<span style="color: rgb(163,21,21)">"Delegate.BeinInvoke(): {0}"</span>,stopWatch.ElapsedTicks.ToString()); <br /> <br />            stopWatch.Reset(); <br />            stopWatch.Start(); <br />            <span style="color: rgb(0,0,255)">for</span> (<span style="color: rgb(0,0,255)">int</span> i = 0; i < 10000; i++) <br />            { <br />                System.Threading.<span style="color: rgb(43,145,175)">ThreadPool</span>.QueueUserWorkItem(waitCallback); <br />            } <br />            stopWatch.Stop(); <br />            <span style="color: rgb(43,145,175)">Console</span>.WriteLine(<span style="color: rgb(163,21,21)">"ThreadPool.QueueUserWorkItem(): {0}"</span>, stopWatch.ElapsedTicks.ToString()); <br />        } <br />    } <br />} </p> <p>And the following output is what I get when running above programme: </p> <p><font color="#ff0000">Delegate.BeinInvoke(): 591441 <br />ThreadPool.QueueUserWorkItem(): 91958</font> </p> <p>From the output, there is no brainer that Delegate.BeginInvoke is way much slower than the ThreadPool.QueueUserWorkItem call. </p> <p> <br /> <a href="http://11011.net/software/vspaste"></a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com2tag:blogger.com,1999:blog-7563089691408821967.post-90239292196662436022007-04-02T21:55:00.000+08:002008-04-19T21:56:08.906+08:00Dash Animation<p>I seldom use WPF's animation capabilities, mostly because I believe the overuse of animations doesn't do any good to user experience, but animtions can be cool if you do it and use it appropriately, this bit of XAML snippet shown below just comes into my imagination, it doesn't serve any practical purpose, but is fun to play with: </p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(128,0,0)">Page</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br /><span style="color: rgb(0,0,255)">          </span><span style="color: rgb(255,0,0)">xmlns:x</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml</span>"<span style="color: rgb(0,0,255)"> <br />          </span><span style="color: rgb(255,0,0)">Title</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Dash Animation</span>"<span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Viewbox</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Margin</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">30</span>"<span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Rectangle <br /></span><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">ellipse</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">600</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">Height</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">600</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">Fill</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Green</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">Stroke</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Red</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">StrokeThickness</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">120</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">StrokeDashArray</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0,2</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">StrokeDashOffset</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>" <br /><span style="color: rgb(0,0,255)">           </span><span style="color: rgb(255,0,0)">StrokeDashCap</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Round</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Rectangle.Triggers</span><span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">EventTrigger</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">RoutedEvent</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">FrameworkElement.Loaded</span>"<span style="color: rgb(0,0,255)">> <br />                    <</span><span style="color: rgb(128,0,0)">BeginStoryboard</span><span style="color: rgb(0,0,255)">> <br />                        <</span><span style="color: rgb(128,0,0)">Storyboard</span><span style="color: rgb(0,0,255)">> <br />                            <</span><span style="color: rgb(128,0,0)">DoubleAnimation <br /></span><span style="color: rgb(0,0,255)">                               </span><span style="color: rgb(255,0,0)">To</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">20</span>" <br /><span style="color: rgb(0,0,255)">                               </span><span style="color: rgb(255,0,0)">Duration</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0:0:5</span>" <br /><span style="color: rgb(0,0,255)">                               </span><span style="color: rgb(255,0,0)">RepeatBehavior</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Forever</span>" <br /><span style="color: rgb(0,0,255)">                               </span><span style="color: rgb(255,0,0)">By</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">2</span>" <br /><span style="color: rgb(0,0,255)">                               </span><span style="color: rgb(255,0,0)">Storyboard.TargetProperty</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">StrokeDashOffset</span>"<span style="color: rgb(0,0,255)"> /> <br />                        </</span><span style="color: rgb(128,0,0)">Storyboard</span><span style="color: rgb(0,0,255)">> <br />                    </</span><span style="color: rgb(128,0,0)">BeginStoryboard</span><span style="color: rgb(0,0,255)">> <br />                </</span><span style="color: rgb(128,0,0)">EventTrigger</span><span style="color: rgb(0,0,255)">> <br />            </</span><span style="color: rgb(128,0,0)">Rectangle.Triggers</span><span style="color: rgb(0,0,255)">> <br />        </</span><span style="color: rgb(128,0,0)">Rectangle</span><span style="color: rgb(0,0,255)">> <br />    </</span><span style="color: rgb(128,0,0)">Viewbox</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(128,0,0)">Page</span><span style="color: rgb(0,0,255)">> <br /></span><a href="http://11011.net/software/vspaste"></a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com2tag:blogger.com,1999:blog-7563089691408821967.post-31211227106425339062007-04-01T21:57:00.000+08:002008-04-19T22:02:05.830+08:00Focus Navigation Using Enter Key<p>Moving focus between UI elements using Enter key is probably one of the controversial features of user interaction design, personally I think it has its own usage, and is a great complement to tab navigation, here is the two techniques of implementing enter navigation I learnt from WPF MSDN forum, both of the two methods need you to first hook up to interesting controls' KeyDown event, imagine you have such piece of XAML: </p> <p><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(128,0,0)">Window</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Class</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">EnterNavigationDemo.Window1</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">xmlns:x</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml</span>" <br /><span style="color: rgb(0,0,255)">   </span><span style="color: rgb(255,0,0)">Title</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Login</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">300</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Height</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">200</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">ResizeMode</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">NoResize</span>" <br /><span style="color: rgb(0,0,255)">    > <br />  <</span><span style="color: rgb(128,0,0)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Button}</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Width</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">60</span>"<span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Margin</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">4</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type TextBox}</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Width</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">160</span>"<span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Margin</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">4</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type PasswordBox}</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Width</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">160</span>"<span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Margin</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">4</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Label}</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Margin</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">4</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(128,0,0)">Window.Resources</span><span style="color: rgb(0,0,255)">> <br />  <</span><span style="color: rgb(128,0,0)">Grid</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">HorizontalAlignment</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Stretch</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">VerticalAlignment</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Stretch</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">documentRoot</span>"<span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Grid.RowDefinitions</span><span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">RowDefinition</span><span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">RowDefinition</span><span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">RowDefinition</span><span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">RowDefinition</span><span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">RowDefinition</span><span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">Grid.RowDefinitions</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Label</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Target</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding ElementName=txtDomainName}</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Content</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">_Domain:</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">TextBox</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">txtDomainName</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">Label</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Target</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding ElementName=txtUserName}</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Content</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">_User:</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">TextBox</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">txtUser</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">Label</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Target</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{Binding ElementName=txtPasswrod}</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Content</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">_Passwrod:</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">0</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">2</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">PasswordBox</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">txtPasswrod</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">2</span>"<span style="color: rgb(0,0,255)">/> <br />    <</span><span style="color: rgb(128,0,0)">StackPanel</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Row</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">3</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Grid.Column</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">1</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Orientation</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Horizontal</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">HorizontalAlignment</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Center</span>"<span style="color: rgb(0,0,255)">> <br />      <</span><span style="color: rgb(128,0,0)">Button</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Content</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">_Login</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">loginButton</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">IsDefault</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">True</span>"<span style="color: rgb(0,0,255)">/> <br />      <</span><span style="color: rgb(128,0,0)">Button</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Content</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">_Cancel</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">cancelButton</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">IsCancel</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">True</span>"<span style="color: rgb(0,0,255)">/> <br />    </</span><span style="color: rgb(128,0,0)">StackPanel</span><span style="color: rgb(0,0,255)">> <br />  </</span><span style="color: rgb(128,0,0)">Grid</span><span style="color: rgb(0,0,255)">> <br /></</span><span style="color: rgb(128,0,0)">Window</span><span style="color: rgb(0,0,255)">></span> </p> <p>In the code behind file, you can achieve enter navigation using the following methods: </p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">partial</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">Window1</span> : System.Windows.<span style="color: rgb(0,128,128)">Window <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> Window1() <br />    { <br />        InitializeComponent(); <br />        <span style="color: rgb(0,0,255)">this</span>.documentRoot.AddHandler(<span style="color: rgb(0,128,128)">TextBox</span>.KeyDownEvent, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">KeyEventHandler</span>(documentRoot_KeyDown)); <br />        <span style="color: rgb(0,0,255)">this</span>.loginButton.Click += <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">RoutedEventHandler</span>(loginButton_Click); <br />        <span style="color: rgb(0,0,255)">this</span>.cancelButton.Click += <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">RoutedEventHandler</span>(cancelButton_Click); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> cancelButton_Click(<span style="color: rgb(0,128,128)">Object</span> sender, <span style="color: rgb(0,128,128)">RoutedEventArgs</span> e) <br />    { <br />        <span style="color: rgb(0,128,128)">MessageBox</span>.Show(<span style="color: rgb(128,0,0)">"Cancel button is clicked"</span>); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> loginButton_Click(<span style="color: rgb(0,128,128)">Object</span> sender, <span style="color: rgb(0,128,128)">RoutedEventArgs</span> e) <br />    { <br />        <span style="color: rgb(0,128,128)">MessageBox</span>.Show(<span style="color: rgb(128,0,0)">"Login button is clicked"</span>); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> documentRoot_KeyDown(<span style="color: rgb(0,128,128)">Object</span> sender, <span style="color: rgb(0,128,128)">KeyEventArgs</span> e) <br />    { <br />        <span style="color: rgb(0,0,255)">if</span> (e.Key == <span style="color: rgb(0,128,128)">Key</span>.Return) <br />        { <br />            <span style="color: rgb(0,128,128)">UIElement</span> focusedElement = <span style="color: rgb(0,128,128)">Keyboard</span>.FocusedElement <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(0,128,128)">UIElement</span>; <br />            <span style="color: rgb(0,0,255)">if</span> (focusedElement != <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                focusedElement.MoveFocus(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">TraversalRequest</span>(<span style="color: rgb(0,128,128)">FocusNavigationDirection</span>.Next)); <br />            } <br />            e.Handled = <span style="color: rgb(0,0,255)">true</span>; <br />        } <br />    } </p> <p>} </p> <p>you can also achieve this by modifying the key down event handler to add the following code: </p> <p><span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> documentRoot_KeyDown(<span style="color: rgb(0,128,128)">Object</span> sender, <span style="color: rgb(0,128,128)">KeyEventArgs</span> e) <br />{ <br />    <span style="color: rgb(0,0,255)">if</span> (e.Key == <span style="color: rgb(0,128,128)">Key</span>.Return) <br />    { <br />        e.Handled = <span style="color: rgb(0,0,255)">true</span>; <br />        <span style="color: rgb(0,128,128)">KeyEventArgs</span> args = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">KeyEventArgs</span>(<span style="color: rgb(0,128,128)">Keyboard</span>.PrimaryDevice, <span style="color: rgb(0,128,128)">Keyboard</span>.PrimaryDevice.ActiveSource, 0, <span style="color: rgb(0,128,128)">Key</span>.Tab); <br />        args.RoutedEvent = <span style="color: rgb(0,128,128)">Keyboard</span>.KeyDownEvent; <br />        <span style="color: rgb(0,128,128)">InputManager</span>.Current.ProcessInput(args); <br />    } <br />} </p> <p>You can see that I add two buttons on the window, my initial thought is that I want enter navigate between the three TextBoxes, but I still want to tab navigate to the buttons, and the above code finally ends up working in the way I desire.</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0tag:blogger.com,1999:blog-7563089691408821967.post-82963457638219262482007-03-27T22:07:00.000+08:002008-04-19T22:11:02.592+08:00WTF of WPF [Part One]- Templating & Styling<p>Have you ever been bugged by WPF's strange behaviour for a long time and cannot stop asking:"WTF?".  <a href="http://11011.net/index.xml">Douglas Stockwell</a> just met one of those confusing aspects of WPF, I just generalize his confusion about WPF's templating and styling into the following XAML repro code: </p> <p><span style="color: rgb(0,0,255)"><span style="color: rgb(0,0,255)"><</span><span style="color: rgb(128,0,0)">Page</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">xmlns</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml/presentation</span>" <br /><span style="color: rgb(0,0,255)">         </span><span style="color: rgb(255,0,0)">xmlns:x</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">http://schemas.microsoft.com/winfx/2006/xaml</span>"<span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Page.Resources</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type TextBlock}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">TextElement.Foreground</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type ContentPresenter}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">TextElement.Foreground</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type  AccessText}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">TextElement.Foreground</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Rectangle}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Fill</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Image}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Source</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">C:\WINDOWS\Web\Wallpaper\Home.jpg</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type MediaElement}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Source</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">C:\WINDOWS\clock.avi</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type InkCanvas}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Background</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Border}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BorderThickness</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">10</span>"<span style="color: rgb(0,0,255)">/> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">BorderBrush</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Cyan</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type StackPanel}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Background</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Value</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Green</span>"<span style="color: rgb(0,0,255)">/> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">TargetType</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{x:Type Control}</span>"<span style="color: rgb(0,0,255)">> <br />            <</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Property</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">Template</span>"<span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">Setter.Value</span><span style="color: rgb(0,0,255)">> <br />                    <</span><span style="color: rgb(128,0,0)">ControlTemplate</span><span style="color: rgb(0,0,255)">> <br />                        <</span><span style="color: rgb(128,0,0)">Label</span><span style="color: rgb(0,0,255)">></span>Inside Control<span style="color: rgb(0,0,255)"></</span><span style="color: rgb(128,0,0)">Label</span><span style="color: rgb(0,0,255)">> <br />                    </</span><span style="color: rgb(128,0,0)">ControlTemplate</span><span style="color: rgb(0,0,255)">> <br />                </</span><span style="color: rgb(128,0,0)">Setter.Value</span><span style="color: rgb(0,0,255)">> <br />            </</span><span style="color: rgb(128,0,0)">Setter</span><span style="color: rgb(0,0,255)">> <br />        </</span><span style="color: rgb(128,0,0)">Style</span><span style="color: rgb(0,0,255)">> <br />        <</span><span style="color: rgb(128,0,0)">ControlTemplate</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">x:Key</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">template</span>"<span style="color: rgb(0,0,255)">> <br />            <!--</span><span style="color: rgb(0,128,0)">WTF, StackPanel, TextBlock, AccessText, ContentPresenter, InkCanvas,  Image, Rectangle, <br />              MediaElement etc cannot pick up their respective implicit styles here, <br />              one takeway from this empircal discovery is that Elements directly derived from FramworkElement cannot work in this scenario.</span><span style="color: rgb(0,0,255)">--> <br />            <</span><span style="color: rgb(128,0,0)">StackPanel</span><span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">TextBlock</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Name</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">tb</span>"<span style="color: rgb(0,0,255)">></span>inside TextBlock<span style="color: rgb(0,0,255)"></</span><span style="color: rgb(128,0,0)">TextBlock</span><span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">AccessText</span><span style="color: rgb(0,0,255)">></span>inside AccessText<span style="color: rgb(0,0,255)"></</span><span style="color: rgb(128,0,0)">AccessText</span><span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">ContentPresenter</span><span style="color: rgb(0,0,255)">> <br />                    <</span><span style="color: rgb(128,0,0)">ContentPresenter.Content</span><span style="color: rgb(0,0,255)">> <br /></span>                    inside ContentPresenter <br /><span style="color: rgb(0,0,255)">                    </</span><span style="color: rgb(128,0,0)">ContentPresenter.Content</span><span style="color: rgb(0,0,255)">> <br />                </</span><span style="color: rgb(128,0,0)">ContentPresenter</span><span style="color: rgb(0,0,255)">> <br />                <</span><span style="color: rgb(128,0,0)">InkCanvas</span><span style="color: rgb(0,0,255)">/> <br />                <</span><span style="color: rgb(128,0,0)">Image</span><span style="color: rgb(0,0,255)">/> <br />                <</span><span style="color: rgb(128,0,0)">Rectangle</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">40</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Height</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">40</span>"<span style="color: rgb(0,0,255)">/> <br />                <</span><span style="color: rgb(128,0,0)">MediaElement</span><span style="color: rgb(0,0,255)">/> <br />                <!--</span><span style="color: rgb(0,128,0)">WTF, Border can pick up implicit style here.</span><span style="color: rgb(0,0,255)">--> <br />                <</span><span style="color: rgb(128,0,0)">Border</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Width</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">40</span>"<span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Height</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">40</span>"<span style="color: rgb(0,0,255)">/> <br />                <!--</span><span style="color: rgb(0,128,0)">WTF, Control can pick up implicit style here, since Border and Control can work here, our previous hypothesis breaks.</span><span style="color: rgb(0,0,255)">--> <br />                <</span><span style="color: rgb(128,0,0)">Control</span><span style="color: rgb(0,0,255)">/> <br />            </</span><span style="color: rgb(128,0,0)">StackPanel</span><span style="color: rgb(0,0,255)">> <br />        </</span><span style="color: rgb(128,0,0)">ControlTemplate</span><span style="color: rgb(0,0,255)">> <br />    </</span><span style="color: rgb(128,0,0)">Page.Resources</span><span style="color: rgb(0,0,255)">> <br />    <</span><span style="color: rgb(128,0,0)">Control</span><span style="color: rgb(0,0,255)"> </span><span style="color: rgb(255,0,0)">Template</span><span style="color: rgb(0,0,255)">=</span>"<span style="color: rgb(0,0,255)">{StaticResource template}</span>"<span style="color: rgb(0,0,255)">/> <br /></</span><span style="color: rgb(128,0,0)">Page</span><span style="color: rgb(0,0,255)">></span></span> </p> <p>When you have above XAML code runnning in XAMLPad, you will see that StackPanel((or other Panel derivatives), TextBlock, AccessText, ContentPresenter, InkCanvas, Image, Rectangle(or other Shape derivatives), MediaElement et al cannot pick up the implicit styles, well, this gives us an empirical understanding that elements directly derived from FrameworkElement cannot pick up implicit styles when placed inside FrameworkTemplate(DataTemplate, ControlTemplate, ItemsPanelTemplate), but how about Border and Control, those two privileged guys works quite different from their siblings, Anyway, Control derivatives(including Control itself) all work pretty well in this scenario. so question comes, why those aforementioned elements aren't created equal? Can we perceive this aspect of WPF as another example of breaking the principle of consistency. </p> <p><font color="#ff0000">Edit: Thanks for Doug's great find, if you look at the implementation of System.Windows.FrameworkElement.FindImplicitStyleResource() method from reflector, you will find that if the elements aren't Controls or Control derivatives, style lookup is limited within the templated parent boundary(In my example, the templated parent is Control), and as to the Border, the Border within the template still cannot pick up its implict style, but since Page's template has Border defined there, and in the Page case, Page is the templated parent, so the implicit style in Page's resources is within the search boundary for Border, so the Border can pick up the style there, to put it simple: the resolution of non-Control element's implicit style are limited within its templated parent boundary.</font> </p> <p>But still, my confusion remains, why the logic of style lookup works the way it is now, why grant Controls more privileges than other equal citizens of WPF? </p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0tag:blogger.com,1999:blog-7563089691408821967.post-17832416610356916912007-03-25T22:05:00.000+08:002008-04-19T22:05:37.473+08:00DataBinding & Multi-Threading In WPF [Part Two]<p>In my last post, I talked about a solution to overcome the current WPF limitation to enable multi-threaded data binding. </p> <p>But there are one of drawback which I introduced in that solution. In that version of BindingCollection, I just simply marshal the collection changed notification back to UI thread, this sounds innocuous, but sometimes, this is not what we really expect, imagine that I have a bunch of listeners listening to BindingCollection.CollectionChanged events, some of them are free-threaded objects, but some of them would be single-threaded, actually CollectionView is the primary listener to this event. free threaded listeners can run on any threads, they are not bound to UI threads, so we shouldn't force them to run in the UI threads, but single-threaded listeners(DispatcherObjects etc) are actually bound to UI threads, so we should let them running inside UI threads as we previously did, to put it simple, we should treat free-threaded listeners and single-threaded listeners differently, so here comes another refined implementation of this collection, this time I call it BindableCollection, because I find that the name of BindingCollection is a bit misleading indeed, It could imply "a collection of Binding" which apparently is not what I want to do with it. </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.ComponentModel; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Generic; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Specialized; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.ObjectModel; </p> <p><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Data <br />{ <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> An ObservableCollection&lt;T&gt; enhanced with capability of free threading. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    [<span style="color: rgb(0,128,128)">Serializable</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">BindableCollection</span><T> : <span style="color: rgb(0,128,128)">ObservableCollection</span><T> <br />    { <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Initializes a new instance of the</span><span style="color: rgb(128,128,128)"><see cref="BindingCollection&lt;T&gt;"></span><span style="color: rgb(0,128,0)">BindingCollection</span><span style="color: rgb(128,128,128)"></see></span><span style="color: rgb(0,128,0)">. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(0,0,255)">public</span> BindableCollection() : <span style="color: rgb(0,0,255)">base</span>() {} <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Initializes a new instance of the</span><span style="color: rgb(128,128,128)"><see cref="BindingCollection&lt;T&gt;"></span><span style="color: rgb(0,128,0)">BindingCollection</span><span style="color: rgb(128,128,128)"></see> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> class that contains elements copied from the specified List&lt;T&gt;. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="list"></span><span style="color: rgb(0,128,0)">The list from which the elements are copied.</span><span style="color: rgb(128,128,128)"></param> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><exception cref="System.ArgumentNullException"></span><span style="color: rgb(0,128,0)">The list parameter cannot be null.</span><span style="color: rgb(128,128,128)"></exception> <br /></span>        <span style="color: rgb(0,0,255)">public</span> BindableCollection(<span style="color: rgb(0,128,128)">List</span><T> list) : <span style="color: rgb(0,0,255)">base</span>(list) {} <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Initializes a new instance of the</span><span style="color: rgb(128,128,128)"><see cref="BindingCollection&lt;T&gt;"></span><span style="color: rgb(0,128,0)">BindingCollection</span><span style="color: rgb(128,128,128)"></see> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> class that contains elements copied from the specified IEnumerable&lt;T&gt;. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="list"></span><span style="color: rgb(0,128,0)">The list from which the elements are copied.</span><span style="color: rgb(128,128,128)"></param> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><exception cref="System.ArgumentNullException"></span><span style="color: rgb(0,128,0)">The list parameter cannot be null.</span><span style="color: rgb(128,128,128)"></exception> <br /></span>        <span style="color: rgb(0,0,255)">public</span> BindableCollection(<span style="color: rgb(0,128,128)">IEnumerable</span><T> list) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (list == <span style="color: rgb(0,0,255)">null</span>) <span style="color: rgb(0,0,255)">throw</span> <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">ArgumentOutOfRangeException</span>(<span style="color: rgb(128,0,0)">"The list parameter cannot be null."</span>); <br />            <span style="color: rgb(0,0,255)">foreach</span> (T item <span style="color: rgb(0,0,255)">in</span> list) <br />            { <br />                <span style="color: rgb(0,0,255)">this</span>.Items.Add(item); <br />            } <br />        } <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Occurs when an item is added, removed, changed, moved, or the entire list is refreshed. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,0,255)">event</span> <span style="color: rgb(0,128,128)">NotifyCollectionChangedEventHandler</span> CollectionChanged; <br /> <br />        <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,0,255)">void</span> OnCollectionChanged(<span style="color: rgb(0,128,128)">NotifyCollectionChangedEventArgs</span> e) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">this</span>.CollectionChanged != <span style="color: rgb(0,0,255)">null</span>) <br />            { <br />                <span style="color: rgb(0,0,255)">using</span> (<span style="color: rgb(0,128,128)">IDisposable</span> disposable = <span style="color: rgb(0,0,255)">this</span>.BlockReentrancy()) <br />                { <br />                    <span style="color: rgb(0,0,255)">foreach</span> (<span style="color: rgb(0,128,128)">Delegate</span> del <span style="color: rgb(0,0,255)">in</span> <span style="color: rgb(0,0,255)">this</span>.CollectionChanged.GetInvocationList()) <br />                    { <br />                        <span style="color: rgb(0,128,128)">NotifyCollectionChangedEventHandler</span> handler = (<span style="color: rgb(0,128,128)">NotifyCollectionChangedEventHandler</span>)del; <br />                        <span style="color: rgb(0,128,128)">DispatcherObject</span> dispatcherInvoker = del.Target <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(0,128,128)">DispatcherObject</span>; <br />                        <span style="color: rgb(0,128,128)">ISynchronizeInvoke</span> syncInvoker = del.Target <span style="color: rgb(0,0,255)">as</span> <span style="color: rgb(0,128,128)">ISynchronizeInvoke</span>; <br />                        <span style="color: rgb(0,0,255)">if</span> (dispatcherInvoker != <span style="color: rgb(0,0,255)">null</span>) <br />                        { <br />                            <span style="color: rgb(0,128,0)">// We are running inside DispatcherSynchronizationContext, <br /></span>                            <span style="color: rgb(0,128,0)">// so we should invoke the event handler in the correct dispatcher. <br /></span>                            dispatcherInvoker.Dispatcher.Invoke(<span style="color: rgb(0,128,128)">DispatcherPriority</span>.Normal, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">ThreadStart</span>(<span style="color: rgb(0,0,255)">delegate <br /></span>                            { <br />                                handler(<span style="color: rgb(0,0,255)">this</span>, e); <br />                            })); <br />                        } <br />                        <span style="color: rgb(0,0,255)">else</span> <span style="color: rgb(0,0,255)">if</span> (syncInvoker != <span style="color: rgb(0,0,255)">null</span>) <br />                        { <br />                            <span style="color: rgb(0,128,0)">// We are running inside WindowsFormsSynchronizationContext, <br /></span>                            <span style="color: rgb(0,128,0)">// so we should invoke the event handler in the correct context. <br /></span>                            syncInvoker.Invoke(del, <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Object</span>[] { <span style="color: rgb(0,0,255)">this</span>, e }); <br />                        } <br />                        <span style="color: rgb(0,0,255)">else <br /></span>                        { <br />                            <span style="color: rgb(0,128,0)">// We are running in free threaded context, so just directly invoke the event handler. <br /></span>                            handler(<span style="color: rgb(0,0,255)">this</span>, e); <br />                        } <br />                    } <br />                } <br />            } <br />        } <br />    } <br />}</p> <a href="http://11011.net/software/vspaste"></a> <p>I've update the test sample to include this new version of BindableCollection, you can download it from here. </p> <p>attachment:<a href="http://www.cnblogs.com/Files/sheva/MultiThreadingInWPF.zip">MultiThreadingInWPF.zip</a> </p> <p> <br /> <a href="http://11011.net/software/vspaste"></a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com1tag:blogger.com,1999:blog-7563089691408821967.post-50017553462931988352007-03-24T22:13:00.001+08:002008-04-19T22:23:16.650+08:00DataBinding & Multi-Threading In WPF [Part One]<p>One of the scenario I think the current version of WPF hasn't done enough is the multi-threaded data binding. Imagine your UI is data bound to a data source collection whose items are modified(added, removed, cleared etc) from another thread rather than the UI thread. you will get the following exception: </p> <p><font color="#ff0000">This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.</font> </p> <p>Actually, generic data source collections are actually free-threaded(I mean free-threaded not thread safe), but WPF's implementations of CollectionView (such as BindingListCollectionView for ADO.NET data source and  ListCollectionView for general purpose data collections) are actually single-threaded, If you look at the base class of CollectionView, you can see that It directly derives from DispatcherObject, this tells us that CollectionViews are actually Disptacher affinitized, because Dispatchers are tied to the thread which creates them, so CollectionViews end up tying to the their spawning threads. This type of restriction makes multi-threaded data binding a bit more cumbersome and complicated. Beatriz Costa who's working on WPF's data binding feature wrote up a great article ages ago talking about <a href="http://www.beacosta.com/2006/09/how-can-i-propagate-changes-across.html">how to do multi-threaded data binding using current version of WPF</a>. She sincerely admit the drawbacks and limitations of current WPF data binding mechanism when it comes to multi-threading. And she came up with an approach to make this type of binding work properly. But one of the things I don't like about her solution is to marshal all the collection modification operations back to the UI thread. I don't think this type of technique can fully leverage the capabilities of worker threads, and much of the benefits inherent with multi-threading are depreciated. How about if the operation you post to UI thread takes a long time to complete, the UI thread will get busy processing this operation, and your UI gets frozen for quite a bit. In this post, I will show another way of doing multi-threaded data binding, and my solution will make as best use of worker threads as possible, enter BindingCollection. </p> <p>The following code illustrates the implementation of BindingCollection: </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Generic; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.Specialized; <br /><span style="color: rgb(0,0,255)">using</span> System.Collections.ObjectModel; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.CompilerServices; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> Sheva.Windows.Data <br />{ <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> An ObservableCollection&lt;T&gt; enhanced with cross threads marshaling. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    [<span style="color: rgb(0,128,128)">Serializable</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">BindingCollection</span><T> : <span style="color: rgb(0,128,128)">ObservableCollection</span><T> <br />    { <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,128,128)">SynchronizationContext</span> creationSyncContext; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,128,128)">Thread</span> creationThread; <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Initializes a new instance of the</span><span style="color: rgb(128,128,128)"><see cref="BindingCollection&lt;T&gt;"></span><span style="color: rgb(0,128,0)">BindingCollection</span><span style="color: rgb(128,128,128)"></see></span><span style="color: rgb(0,128,0)">. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(0,0,255)">public</span> BindingCollection() : <span style="color: rgb(0,0,255)">base</span>() <br />        { <br />            InstallSynchronizationContext(); <br />        } <br /> <br />        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Initializes a new instance of the</span><span style="color: rgb(128,128,128)"><see cref="BindingCollection&lt;T&gt;"></span><span style="color: rgb(0,128,0)">BindingCollection</span><span style="color: rgb(128,128,128)"></see> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> class that contains elements copied from the specified list. <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><param name="list"></span><span style="color: rgb(0,128,0)">The list from which the elements are copied.</span><span style="color: rgb(128,128,128)"></param> <br /></span>        <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><exception cref="System.ArgumentNullException"></span><span style="color: rgb(0,128,0)">The list parameter cannot be null.</span><span style="color: rgb(128,128,128)"></exception> <br /></span>        <span style="color: rgb(0,0,255)">public</span> BindingCollection(<span style="color: rgb(0,128,128)">List</span><T> list) : <span style="color: rgb(0,0,255)">base</span>(list) <br />        { <br />            InstallSynchronizationContext(); <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,0,255)">void</span> OnCollectionChanged(<span style="color: rgb(0,128,128)">NotifyCollectionChangedEventArgs</span> e) <br />        { <br />            <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">this</span>.creationThread == <span style="color: rgb(0,128,128)">Thread</span>.CurrentThread) <br />            { <br />                <span style="color: rgb(0,128,0)">// We are running in the same thread, so just directly fire the collection changed notification. <br /></span>                <span style="color: rgb(0,0,255)">this</span>.OnCollectionChangedInternal(e); <br />            } <br />            <span style="color: rgb(0,0,255)">else</span> <span style="color: rgb(0,0,255)">if</span> (<span style="color: rgb(0,0,255)">this</span>.creationSyncContext.GetType() == <span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(0,128,128)">SynchronizationContext</span>)) <br />            { <br />                <span style="color: rgb(0,128,0)">// We are running in free threaded context, also directly fire the collection changed notification. <br /></span>                <span style="color: rgb(0,0,255)">this</span>.OnCollectionChangedInternal(e); <br />            } <br />            <span style="color: rgb(0,0,255)">else <br /></span>            { <br />                <span style="color: rgb(0,128,0)">// We are running in WindowsFormsSynchronizationContext or DispatcherSynchronizationContext, <br /></span>                <span style="color: rgb(0,128,0)">// so we should marshal the collection changed notification back to the creation thread. <br /></span>                <span style="color: rgb(0,128,0)">// Note that we should use the blocking SynchronizationContext.Send operation to marshal the call, <br /></span>                <span style="color: rgb(0,128,0)">// because SynchronizationContext.Post will fail under heavy usage scenario. <br /></span>                <span style="color: rgb(0,0,255)">this</span>.creationSyncContext.Send(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">SendOrPostCallback</span>(<span style="color: rgb(0,0,255)">delegate <br /></span>                { <br />                    <span style="color: rgb(0,0,255)">this</span>.OnCollectionChangedInternal(e); <br />                }), <span style="color: rgb(0,0,255)">null</span>); <br />            } <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> InstallSynchronizationContext() <br />        { <br />            <span style="color: rgb(0,0,255)">this</span>.creationSyncContext = <span style="color: rgb(0,128,128)">SynchronizationContext</span>.Current; <br />            <span style="color: rgb(0,0,255)">this</span>.creationThread = <span style="color: rgb(0,128,128)">Thread</span>.CurrentThread; <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">internal</span> <span style="color: rgb(0,0,255)">void</span> OnCollectionChangedInternal(<span style="color: rgb(0,128,128)">NotifyCollectionChangedEventArgs</span> e) <br />        { <br />            <span style="color: rgb(0,0,255)">base</span>.OnCollectionChanged(e); <br />        } <br />    } <br />} </p> <p>Note that the reason I use SynchronizationContext rather than Dispatcher here is that I want this collection to be usable in different presentation technologies such as Windows Forms, WPF, and ASP.NET(You might think I am too of frameworkitis, actually I am). I really hope that the logic I create in the OnCollectionChanged method can make into the future implementation of CollectionViews, so that we don't need to worry about the threading problem illustrated above. I've created a simple WPF application to demonstrate how to use this collection, here is the <a href="http://www.cnblogs.com/Files/sheva/MultiThreadingInWPF.zip">vs project</a> for this little app. </p> <p>You can make this test more demanding by pressing the "Add Items" and "Remove Items" buttons multiple times to create more threads for testing. <br /> <br /></p> <a href="http://11011.net/software/vspaste"></a> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com6tag:blogger.com,1999:blog-7563089691408821967.post-72755144570168917922007-03-23T22:29:00.000+08:002008-04-19T22:30:06.361+08:00Window Message Based Communication Between Different Applications<p>There are a number of ways to make inter-process communications on Windows platform, some of which are memory mapped files, mailslots, named pipes etc. But there is another way of doing inter-process communications. the <a href="http://msdn2.microsoft.com/en-us/library/ms649011.aspx">WM_COPYDATA</a> window message enables you to send data to other applications, the only requirement on the receiving applications is that the receiving application should have a thread running message pump, running message pump is the only way to process window messages. I've created a sample application on how to use this technique to send string messages between two Windows Forms applications. </p> <p>I have two Windows Forms applications, one is called SenderApp which is responsible to send messages, it typically uses the Win32 SendMessage to send WM_COPYDATA messages to the receiving application called ReceiverApp, in the SendMessage method, you should specify lParam arugment a pointer to a <a href="http://msdn2.microsoft.com/en-us/library/ms649010.aspx">COPYDATASTRUCT</a> structure, COPYDATASTRUCT carries on the data you want to send to other applications. </p> <p>[<span style="color: rgb(0,128,128)">StructLayout</span>(<span style="color: rgb(0,128,128)">LayoutKind</span>.Sequential)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">struct</span> <span style="color: rgb(0,128,128)">COPYDATASTRUCT</span>{ <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Specifies data to be passed to the receiving application. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,128,128)">Int32</span> dwData; <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Specifies the size, in bytes, of the data pointed to by the lpData member. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,128,128)">Int32</span> cbData; <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Pointer to data to be passed to the receiving application. This member can be IntPtr.Zero. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,128,128)">IntPtr</span> lpData; <br />} </p> <p>[<span style="color: rgb(0,128,128)">DllImport</span>(<span style="color: rgb(128,0,0)">"user32.dll"</span>)] <br /><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">extern</span> <span style="color: rgb(0,128,128)">Boolean</span> SendMessage(<span style="color: rgb(0,128,128)">IntPtr</span> hwnd, <span style="color: rgb(0,128,128)">UInt32</span> Msg, <span style="color: rgb(0,128,128)">IntPtr</span> wParam, <span style="color: rgb(0,0,255)">ref</span> <span style="color: rgb(0,128,128)">COPYDATASTRUCT</span> lParam); </p> <p>Here is the code for the SenderApp: </p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">partial</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">SenderForm</span> : <span style="color: rgb(0,128,128)">Form <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(0,128,128)">UInt32</span> WM_COPYDATA = 0x004a; <br /> <br />    <span style="color: rgb(0,0,255)">public</span> SenderForm() <br />    { <br />        InitializeComponent(); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">void</span> sendButton_Click(<span style="color: rgb(0,128,128)">Object</span> sender, <span style="color: rgb(0,128,128)">EventArgs</span> e) <br />    { <br />        <span style="color: rgb(0,128,128)">IntPtr</span> receiverHwnd = FindWindow(<span style="color: rgb(0,0,255)">null</span>, <span style="color: rgb(128,0,0)">"Receiver Form"</span>); <br />        <span style="color: rgb(0,0,255)">if</span> (receiverHwnd == <span style="color: rgb(0,128,128)">IntPtr</span>.Zero) <br />        { <br />            <span style="color: rgb(0,128,128)">MessageBox</span>.Show(<span style="color: rgb(128,0,0)">"Receiver App hasn't start yet!"</span>); <br />            <span style="color: rgb(0,0,255)">return</span>; <br />        } <br /> <br />        <span style="color: rgb(0,128,128)">String</span> msg = msgTextBox.Text; <br /> <br />        <span style="color: rgb(0,128,0)">// Allocate unmanaged memory. <br /></span>        <span style="color: rgb(0,128,128)">IntPtr</span> msgPtr = <span style="color: rgb(0,128,128)">Marshal</span>.AllocHGlobal(msg.Length); <br /> <br />        <span style="color: rgb(0,128,0)">// Convert string to a sequence of bytes. <br /></span>        <span style="color: rgb(0,128,128)">Byte</span>[] msgData = <span style="color: rgb(0,128,128)">Encoding</span>.Default.GetBytes(msg); <br />        <br />        <span style="color: rgb(0,128,0)">// Copy the bytes from managed heap to unmanaged memory. <br /></span>        <span style="color: rgb(0,128,128)">Marshal</span>.Copy(msgData, 0, msgPtr, msgData.Length); <br /> <br />        <span style="color: rgb(0,128,128)">COPYDATASTRUCT</span> copyData = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">COPYDATASTRUCT</span>(); <br />        copyData.dwData = <span style="color: rgb(0,128,128)">Encoding</span>.Default.CodePage; <br />        copyData.cbData = msgData.Length + 1; <span style="color: rgb(0,128,0)">//We should take the null-terminator into consideration. <br /></span>        copyData.lpData = msgPtr; <br />        SendMessage(receiverHwnd, WM_COPYDATA, <span style="color: rgb(0,0,255)">this</span>.Handle, <span style="color: rgb(0,0,255)">ref</span> copyData); <br />        <span style="color: rgb(0,128,128)">Marshal</span>.FreeHGlobal(msgPtr); <br />    } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> The main entry point for the SenderApp. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    [<span style="color: rgb(0,128,128)">STAThread</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> Main() <br />    { <br />        <span style="color: rgb(0,128,128)">Application</span>.EnableVisualStyles(); <br />        <span style="color: rgb(0,128,128)">Application</span>.SetCompatibleTextRenderingDefault(<span style="color: rgb(0,0,255)">false</span>); <br />        <span style="color: rgb(0,128,128)">Application</span>.Run(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">SenderForm</span>()); <br />    } <br />} </p> <p>And this is code for ReceiverApp: </p> <p><span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">partial</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">ReceiverForm</span> : <span style="color: rgb(0,128,128)">Form <br /></span>{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">const</span> <span style="color: rgb(0,128,128)">UInt32</span> WM_COPYDATA = 0x004a; <br /> <br />    <span style="color: rgb(0,0,255)">public</span> ReceiverForm() <br />    { <br />        InitializeComponent(); <br />    } <br /> <br />    <span style="color: rgb(0,0,255)">protected</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,0,255)">void</span> WndProc(<span style="color: rgb(0,0,255)">ref</span> <span style="color: rgb(0,128,128)">Message</span> m) <br />    { <br />        <span style="color: rgb(0,0,255)">if</span> (m.Msg == WM_COPYDATA) <br />        { <br />            <span style="color: rgb(0,128,128)">COPYDATASTRUCT</span> copyData = (<span style="color: rgb(0,128,128)">COPYDATASTRUCT</span>)m.GetLParam(<span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(0,128,128)">COPYDATASTRUCT</span>)); <br />            <span style="color: rgb(0,128,128)">Byte</span>[] data = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Byte</span>[copyData.cbData - 1]; <span style="color: rgb(0,128,0)">//CLR doesn't recognize the null-terminator, so we should strip it. <br /></span>            <span style="color: rgb(0,128,128)">Marshal</span>.Copy(copyData.lpData, data, 0, data.Length); <br />            <span style="color: rgb(0,128,128)">String</span> msg = <span style="color: rgb(0,128,128)">Encoding</span>.GetEncoding(copyData.dwData).GetString(data, 0, data.Length); <br />            msgTextBox.Text = msg; <br />        } <br />        <span style="color: rgb(0,0,255)">base</span>.WndProc(<span style="color: rgb(0,0,255)">ref</span> m); <br />    } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> The main entry point for the ReceiverApp. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    [<span style="color: rgb(0,128,128)">STAThread</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> Main() <br />    { <br />        <span style="color: rgb(0,128,128)">Application</span>.EnableVisualStyles(); <br />        <span style="color: rgb(0,128,128)">Application</span>.SetCompatibleTextRenderingDefault(<span style="color: rgb(0,0,255)">false</span>); <br />        <span style="color: rgb(0,128,128)">Application</span>.Run(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">ReceiverForm</span>()); <br />    } <br />}<a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a> </p> <p>Put the above code up and running, you get the following screenshot: </p> <a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a> <p><img src="http://www.cnblogs.com/images/cnblogs_com/sheva/48805/r_SendingMsgBetweenApps.JPG" /> </p> <p>You can download the <a href="http://www.cnblogs.com/Files/sheva/SendingMsgBetweenApps.zip">full vs project</a> from here.</p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com2tag:blogger.com,1999:blog-7563089691408821967.post-57341879738018272982007-03-21T22:31:00.001+08:002008-04-19T22:38:00.131+08:00AOP Style Thread Synchronization<p>Whenever I encounter with the problem of threading, I always think that this aspect of programming problem should be directly built into the framework, so we shouldn't need to understand things like STA, MTA, cross threading marshaling etc. the <a href="http://msdn2.microsoft.com/en-us/library/system.threading.synchronizationcontext.aspx">System.Threading.SynchronizationContext</a> and <a href="http://msdn2.microsoft.com/en-us/library/system.componentmodel.asyncoperationmanager%28VS.80%29.aspx">System.ComponentModel.AsyncOperationManager</a> are designed just for this very purpose. But this solution is far from optimal, so I dig into it, and with the help of Don Box's Book <a href="http://www.amazon.com/Essential-NET-Common-Language-Runtime/dp/0201734117">Essential .NET-Common Language Runtime</a>, I come up with an approach using CLR's method interception mechanism. </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting.Proxies; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting.Services; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting.Contexts; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting.Activation; <br /><span style="color: rgb(0,0,255)">using</span> System.Runtime.Remoting.Messaging; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> ThreadBoundObjectDemo <br />{ <br />    <span style="color: rgb(0,0,255)">internal</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">SynchronizationProxy</span> : <span style="color: rgb(0,128,128)">RealProxy <br /></span>    { <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,128,128)">SynchronizationContext</span> constructionContext; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">readonly</span> <span style="color: rgb(0,128,128)">MarshalByRefObject</span> target; <br />        <span style="color: rgb(0,0,255)">private</span> <span style="color: rgb(0,0,255)">readonly</span> <span style="color: rgb(0,128,128)">Object</span> syncRoot; <br />        <span style="color: rgb(0,0,255)">public</span> SynchronizationProxy(<span style="color: rgb(0,128,128)">MarshalByRefObject</span> target) : <span style="color: rgb(0,0,255)">base</span>(<span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(0,128,128)">ThreadBoundObject</span>)) <br />        { <br />            <span style="color: rgb(0,0,255)">this</span>.target = target; <br />            <span style="color: rgb(0,0,255)">this</span>.syncRoot = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Object</span>(); <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,128,128)">IMessage</span> Invoke(<span style="color: rgb(0,128,128)">IMessage</span> msg) <br />        { <br />            <span style="color: rgb(0,128,128)">IMethodCallMessage</span> call = (<span style="color: rgb(0,128,128)">IMethodCallMessage</span>)msg; <br />            <span style="color: rgb(0,128,128)">IMessage</span> response = <span style="color: rgb(0,0,255)">null</span>; <br />            <span style="color: rgb(0,0,255)">if</span> (call <span style="color: rgb(0,0,255)">is</span> <span style="color: rgb(0,128,128)">IConstructionCallMessage</span>) <br />            { <br />                <span style="color: rgb(0,128,128)">IConstructionCallMessage</span> ctor = (<span style="color: rgb(0,128,128)">IConstructionCallMessage</span>)msg; <br />                constructionContext = <span style="color: rgb(0,128,128)">SynchronizationContext</span>.Current; <br />                <span style="color: rgb(0,128,128)">RealProxy</span> defaultProxy = <span style="color: rgb(0,128,128)">RemotingServices</span>.GetRealProxy(target); <br />                defaultProxy.InitializeServerObject(ctor); <br />                response = <span style="color: rgb(0,128,128)">EnterpriseServicesHelper</span>.CreateConstructionReturnMessage(ctor, (<span style="color: rgb(0,128,128)">MarshalByRefObject</span>)<span style="color: rgb(0,0,255)">this</span>.GetTransparentProxy()); <br />            } <br />            <span style="color: rgb(0,0,255)">else <br /></span>            { <br />                <span style="color: rgb(0,0,255)">if</span> (constructionContext == <span style="color: rgb(0,0,255)">null</span> || constructionContext.GetType() == <span style="color: rgb(0,0,255)">typeof</span>(<span style="color: rgb(0,128,128)">SynchronizationContext</span>)) <br />                { <br />                    <span style="color: rgb(0,0,255)">lock</span> (<span style="color: rgb(0,0,255)">this</span>.syncRoot) <br />                    { <br />                        response = <span style="color: rgb(0,128,128)">RemotingServices</span>.ExecuteMessage(target, call); <br />                    } <br />                } <br />                <span style="color: rgb(0,0,255)">else</span> <span style="color: rgb(0,0,255)">if</span> (constructionContext != <span style="color: rgb(0,128,128)">SynchronizationContext</span>.Current) <br />                { <br />                    constructionContext.Send(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">SendOrPostCallback</span>(<span style="color: rgb(0,0,255)">delegate <br /></span>                    { <br />                        response = <span style="color: rgb(0,128,128)">RemotingServices</span>.ExecuteMessage(target, call); <br />                    }), <span style="color: rgb(0,0,255)">null</span>); <br />                } <br />                <span style="color: rgb(0,0,255)">else <br /></span>                { <br />                    response = <span style="color: rgb(0,128,128)">RemotingServices</span>.ExecuteMessage(target, call); <br />                } <br />            } <br /> <br />            <span style="color: rgb(0,0,255)">return</span> response; <br />        } <br />    } <br /> <br />    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"><summary> <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> Indicates that all access on an object members(including fields, methods, properties, indexers etc) is thread safe. <br /></span>    <span style="color: rgb(128,128,128)">///</span><span style="color: rgb(0,128,0)"> </span><span style="color: rgb(128,128,128)"></summary> <br /></span>    [<span style="color: rgb(0,128,128)">AttributeUsage</span>(<span style="color: rgb(0,128,128)">AttributeTargets</span>.Class)] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">SynchronizedAttribute</span> : <span style="color: rgb(0,128,128)">ProxyAttribute <br /></span>    { <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">override</span> <span style="color: rgb(0,128,128)">MarshalByRefObject</span> CreateInstance(<span style="color: rgb(0,128,128)">Type</span> serverType) <br />        { <br />            <span style="color: rgb(0,128,128)">MarshalByRefObject</span> target = <span style="color: rgb(0,0,255)">base</span>.CreateInstance(serverType); <br />            <span style="color: rgb(0,128,128)">SynchronizationProxy</span> rp = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">SynchronizationProxy</span>(target); <br />            <span style="color: rgb(0,0,255)">return</span> (<span style="color: rgb(0,128,128)">MarshalByRefObject</span>) rp.GetTransparentProxy(); <br />        } <br />    } <br />}</p> <p>So if you want an object to be thread safe, all you need to do is to have a class subclass ContextBoundObject and apply the SynchronizedAttribute to it: </p> <p><span style="color: rgb(0,0,255)">using</span> System; <br /><span style="color: rgb(0,0,255)">using</span> System.Threading; <br /><span style="color: rgb(0,0,255)">using</span> System.Windows.Forms; <br /> <br /><span style="color: rgb(0,0,255)">namespace</span> ThreadBoundObjectDemo <br />{ <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">Program <br /></span>    { <br />        [<span style="color: rgb(0,128,128)">STAThread</span>] <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">static</span> <span style="color: rgb(0,0,255)">void</span> Main() <br />        { <br />            <span style="color: rgb(0,128,128)">Button</span> btn = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Button</span>(); <br />            btn.Text = <span style="color: rgb(128,0,0)">"Text set on creation thread"</span>; <br />            <span style="color: rgb(0,128,128)">Form</span> frm = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Form</span>(); <br />            frm.Controls.Add(btn); <br />            btn.Dock = <span style="color: rgb(0,128,128)">DockStyle</span>.Top; <br />            <span style="color: rgb(0,128,128)">ThreadBoundObject</span> obj = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">ThreadBoundObject</span>(btn); <br />            btn.Click += <span style="color: rgb(0,0,255)">delegate <br /></span>            { <br />                <span style="color: rgb(0,128,128)">Thread</span> t = <span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">Thread</span>(<span style="color: rgb(0,0,255)">new</span> <span style="color: rgb(0,128,128)">ThreadStart</span>(<span style="color: rgb(0,0,255)">delegate <br /></span>                { <br />                    obj.ButtonText = <span style="color: rgb(128,0,0)">"Text set from another thread"</span>; <br />                })); <br />                t.Start(); <br />            }; <br />            <span style="color: rgb(0,128,128)">Application</span>.Run(frm); <br />        } <br />    } <br /> <br />    [<span style="color: rgb(0,128,128)">SynchronizedAttribute</span>] <br />    <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,0,255)">class</span> <span style="color: rgb(0,128,128)">ThreadBoundObject</span> : <span style="color: rgb(0,128,128)">ContextBoundObject <br /></span>    { <br />        <span style="color: rgb(0,128,128)">Button</span> btn; <br />        <span style="color: rgb(0,0,255)">public</span> ThreadBoundObject(<span style="color: rgb(0,128,128)">Button</span> btn) <br />        { <br />            <span style="color: rgb(0,0,255)">this</span>.btn = btn; <br />        } <br /> <br />        <span style="color: rgb(0,0,255)">public</span> <span style="color: rgb(0,128,128)">String</span> ButtonText <br />        { <br />            <span style="color: rgb(0,0,255)">get</span> { <span style="color: rgb(0,0,255)">return</span> <span style="color: rgb(0,0,255)">this</span>.btn.Text; } <br />            <span style="color: rgb(0,0,255)">set</span> { <span style="color: rgb(0,0,255)">this</span>.btn.Text = <span style="color: rgb(0,0,255)">value</span>; } <br />        } <br />    } <br />} <br /></p> <p> </p> <p>To be honest, I don't like the design of ContextBoundObject, but suffice it to say, with the power of CLR's method interception mechanism, you can write AOP styled programme much easier, It looks like that you need to write more plumbing code to implement the AOP components, but you would wind up getting the benefit of easy usage pattern.<a href="http://11011.net/software/vspaste"></a><a href="http://11011.net/software/vspaste"></a></p> Zhou Yonghttp://www.blogger.com/profile/14116780818653691348noreply@blogger.com0