tag:blogger.com,1999:blog-64919598301507031472024-02-08T04:00:27.601-08:00Working with WPFSteve Lillishttp://www.blogger.com/profile/00752118450551854615noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-6491959830150703147.post-37974758794896142932011-09-21T13:11:00.000-07:002011-09-25T05:25:23.600-07:00Things You Won't Know Until You KnowDuring most of my free time over the past week or so I've been delving even deeper into WPF and building test applications here and there. I've also run into many seemingly complicated issues only to find out that they boil down to really simple WPF design nuances. To save anybody else from suffering the same trouble I've experienced I have compiled a list, as the title suggests, of things you won't know until you know (about WPF):<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>---------------------------------------</b></span><br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>The Importance of the Background Property</b></span><br />
For a <span class="Apple-style-span" style="color: #45818e;">FrameworkElement </span>to be involved in hit-testing (i.e. respond to being clicked, mouseover, etc.) it requires a <i>Background </i><span class="Apple-style-span" style="color: #45818e;">Brush </span>value. If you don't want your element to have a background colour then feel free to set it's Background property to "Transparent". Also, don't be fooled if your element has a default colour. By default the Background brush is set to null and this means no hit-testing. As a quick aside; it doesn't matter where you set the Background property. In your window xaml, a style, the code behind, wherever; it will work.<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>The Extreme Power of Binding</b></span><br />
<div style="text-align: left;"><span class="Apple-style-span" style="font-family: inherit;">Suffice it to say; binding is - in my opinion - the single most powerful concept that underpins the whole of WPF. By enabling you to make loose connections not only to properties and objects but also to <em>list of objects</em> you can harness your data with a UI that seems almost self-aware. I won't be blogging about Binding unless asked to as <a href="http://msdn.microsoft.com/en-us/library/ms752347.aspx">Microsoft have already done an amazing job</a> but I will be posting little snippets here and there about how Binding helped me get out of a jam.</span></div><br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><strong>Canvas Width and Height</strong></span><br />
<span class="Apple-style-span" style="color: #45818e;">Canvas </span>objects don't have Width or Height values unless you have set them by explicitly stating "Width=". If you need the values of a Canvas' on screen width and height then reference them with ActualWidth and ActualHeight instead to retrieve accurate values.<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>ContextMenu and the Visual Tree</b></span><br />
I've mentioned it already in both of my two previous blog posts but I'll say it here again for emphasis; <span class="Apple-style-span" style="color: #45818e;">ContextMenus</span> are on a separate Visual Tree to the rest of the application and this can cause issues. For a more in depth explanation and a fix please see my previous post: <a href="http://workingwithwpf.blogspot.com/2011/09/smartmenuitem-power-of-binding.html">SmartMenuItem - The Power of Binding</a>.<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>FindResource and generic.xaml</b></span><br />
You may not have a <i>generic.xaml </i>file but if you do; the method, FindResource, does not search your generic.xaml file for your resource. For your resource to be able to be found using that method you will need to define it in a resource dictionary that you reference explicitly from the App, Window, UserControl, etc. For more information on how to do this see the MSDN article on <a href="http://msdn.microsoft.com/en-us/library/system.windows.resourcedictionary.mergeddictionaries.aspx">ResourceDictionary.MergedDictionary</a>.<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>Microsoft Expression Blend</b></span><br />
I've only recently discovered that while Microsoft Expression Blend does cost a fair bit, the SDK doesn't. This means you can play with all the extremely powerful toys that come with Blend (<i>Behaviors</i>, to name one) directly in your xaml just without the fancy Blend front end. Download the Microsoft Expression Blend SDK for free from <a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=22829">Microsoft's Official Site</a><span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b></b></span><br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>PropertyMetaData and Double</b></span><br />
When defining <span class="Apple-style-span" style="color: #45818e;">DependencyProperties</span> in your classes you may want a dependency property of type <span class="Apple-style-span" style="color: blue;">double</span>. This is absolutely fine. However; make sure that when setting a default value you explicitly pass a double to the <span class="Apple-style-span" style="color: #45818e;">PropertyMetaData</span>. For example, if you want your Dependency Property to have a default of zero and create the PropertyMetaData like this: <span class="Apple-style-span" style="color: blue;">new </span><span class="Apple-style-span" style="color: #45818e;">PropertyMetaData</span>(0) then your code will throw an error. Why? Because you just invoked the <span class="Apple-style-span" style="color: #45818e;">PropertyMetaData </span>constructor for an <span class="Apple-style-span" style="color: blue;">int</span> and this isn't compatible with your <span class="Apple-style-span" style="color: blue;">double </span><span class="Apple-style-span" style="color: #45818e;">DependencyProperty</span>. To avoid this problem, use <span class="Apple-style-span" style="color: blue;">new </span><span class="Apple-style-span" style="color: #45818e;">PropertyMetaData</span>(0D) instead.<br />
<br />
<span class="Apple-style-span" style="color: #3d85c6; font-size: large;"><b>---------------------------------------</b></span><br />
<br />
And that's it for the moment. No doubt this list will grow as I continue my journey with the Windows Presentation Foundation. If you've experienced similar plight at the hands of WPF then please feel free to let me know what it was in the comments below and I'll add it to the list.Steve Lillishttp://www.blogger.com/profile/00752118450551854615noreply@blogger.com0tag:blogger.com,1999:blog-6491959830150703147.post-91378619570924809102011-09-15T12:07:00.000-07:002011-09-22T01:07:12.535-07:00SmartMenuItem - The Power of BindingIn my previous post I mentioned an issue that a lot of people come across with WPF regarding Commands raised by MenuItems. The problem being that when a MenuItem is nested in a ContextMenu, the Commands the MenuItem raises are not always heard by the Window or its inhabitants. This is because a ContextMenu is on a separate Visual Tree and the Commands don't get seen by the Window or its Visual Tree.<br />
<br />
The way around this is to bind the object reference that the MenuItem sends its Commands (its CommandTarget) to the ContextMenu's PlacementTarget object reference (the object that you right-clicked to bring up the context menu). You could do this in your xaml for every MenuItem by writing the following each time you add a MenuItem:<br />
<br />
<span class="Apple-style-span" style="background-color: #eeeeee; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; white-space: pre;"><span class="atn" style="background-clip: initial; background-color: transparent; background-origin: initial; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; color: red; font-size: 14px; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;">CommandTarget</span><span class="pun" style="background-clip: initial; background-color: transparent; background-origin: initial; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; color: black; font-size: 14px; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;">=</span><span class="atv" style="background-clip: initial; background-color: transparent; background-origin: initial; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; color: blue; font-size: 14px; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;">"{Binding Path="PlacementTarget", RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"</span><span class="tag" style="background-clip: initial; background-color: transparent; background-origin: initial; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; color: maroon; font-size: 14px; margin: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline;">></span></span><br />
<span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"><i>Curly braces tell xaml that this is a MarkupExtension - a link to some background code. Markup works like this: {MarkupExtensionToRun Parameter1 Parameter2 etc.}. Note the use of curly braces { }. These indicate to xaml that the the bit in between is a Markup Extension, a piece of code to be run in it's own right.</i></span><br />
<br />
Alternatively you can write a tiny helper class that means you'll never have to worry about it again. The below does exactly the same as the above but it does it for us. All you have to do is start using SmartMenuItem instead of MenuItem.<br />
<br />
<span class="Apple-style-span" style="color: #073763; font-size: large;"><b>The Code</b></span><br />
<span class="Apple-style-span" style="color: blue;">public class </span><span class="Apple-style-span" style="color: #3d85c6;">SmartMenuItem </span>: <span class="Apple-style-span" style="color: #3d85c6;">MenuItem</span><br />
{<br />
<span class="Apple-style-span" style="color: blue;">public override void</span> OnApplyTemplate()<br />
{<br />
<span class="Apple-style-span" style="color: blue;">base</span>.OnApplyTemplate();<br />
<br />
if (!<span class="Apple-style-span" style="color: #3d85c6;">BindingOperations</span>.IsDataBound(<span class="Apple-style-span" style="color: blue;">this</span>, <span class="Apple-style-span" style="color: #3d85c6;">MenuItem</span>.CommandTargetProperty))<br />
{<br />
<span class="Apple-style-span" style="color: #3d85c6;">Binding </span>b = <span class="Apple-style-span" style="color: blue;">new </span><span class="Apple-style-span" style="color: #3d85c6;">Binding</span>(<span class="Apple-style-span" style="color: #990000;">"PlacementTarget"</span>);<br />
b.RelativeSource = new <span class="Apple-style-span" style="color: #3d85c6;">RelativeSource</span>(<span class="Apple-style-span" style="color: #3d85c6;">RelativeSourceMode</span>.FindAncestor, <span class="Apple-style-span" style="color: blue;">typeof</span>(<span class="Apple-style-span" style="color: #3d85c6;">ContextMenu</span>), 1);<br />
b.UpdateSourceTrigger = <span class="Apple-style-span" style="color: #3d85c6;">UpdateSourceTrigger</span>.PropertyChanged;<br />
<span class="Apple-style-span" style="color: blue;">this</span>.SetBinding(<span class="Apple-style-span" style="color: #3d85c6;">MenuItem</span>.CommandTargetProperty, b);<br />
}<br />
}<br />
}<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b><span class="Apple-style-span" style="color: #073763;">Step by Step Breakdown</span></b></span><br />
<span class="Apple-style-span" style="color: #274e13;">Run this code when the template for a SmartMenuItem is applied</span><br />
<span class="Apple-style-span" style="color: blue;">public override void</span> OnApplyTemplate()<br />
{<br />
<span class="Apple-style-span" style="color: #274e13;"> Do all the normal template application things first</span><br />
<span class="Apple-style-span" style="color: blue;">base</span>.OnApplyTemplate();<br />
<br />
<span class="Apple-style-span" style="color: #274e13;">Check whether the CommandTarget was bound by the Template</span><br />
<span class="Apple-style-span" style="color: #274e13;"> If it was then we don't apply our binding as we assume the user was trying to override our behaviour</span><br />
if (!<span class="Apple-style-span" style="color: #3d85c6;">BindingOperations</span>.IsDataBound(<span class="Apple-style-span" style="color: blue;">this</span>, <span class="Apple-style-span" style="color: #3d85c6;">MenuItem</span>.CommandTargetProperty))<br />
{<br />
<span class="Apple-style-span" style="color: red;"> </span><span class="Apple-style-span" style="color: #274e13;">The Path is the name of the source property on our RelativeSource</span><br />
<span class="Apple-style-span" style="color: #3d85c6;">Binding </span>b = <span class="Apple-style-span" style="color: blue;">new </span><span class="Apple-style-span" style="color: #3d85c6;">Binding</span>(<span class="Apple-style-span" style="color: #990000;">"PlacementTarget"</span>);<br />
<br />
<span class="Apple-style-span" style="color: red;"> </span><span class="Apple-style-span" style="color: #274e13;">When looking for the Path of "PlacementTarget", look for it in our first ancestor of type ContextMenu</span><br />
b.RelativeSource = new <span class="Apple-style-span" style="color: #3d85c6;">RelativeSource</span>(<span class="Apple-style-span" style="color: #3d85c6;">RelativeSourceMode</span>.FindAncestor, <span class="Apple-style-span" style="color: blue;">typeof</span>(<span class="Apple-style-span" style="color: #3d85c6;">ContextMenu</span>), 1);<br />
<br />
<span class="Apple-style-span" style="color: #274e13;"> Update our source whenever the target property changes (i.e. re-find our relative source)</span><br />
b.UpdateSourceTrigger = <span class="Apple-style-span" style="color: #3d85c6;">UpdateSourceTrigger</span>.PropertyChanged;<br />
<br />
<span class="Apple-style-span" style="color: #274e13;">Apply the binding</span><br />
<span class="Apple-style-span" style="color: blue;">this</span>.SetBinding(<span class="Apple-style-span" style="color: #3d85c6;">MenuItem</span>.CommandTargetProperty, b);<br />
}<br />
}<br />
<br />
<span class="Apple-style-span" style="font-size: large;"><b><span class="Apple-style-span" style="color: #073763;">Why Use Binding?</span></b></span><br />
If we were to have written code that dug up the ancestor of type ContextMenu and then said something like mySmartMenuItem.CommandTarget = myParentContextMenu.PlacementTarget then this link would have been <i>Static</i>. What does this mean? It means that even if we moved our SmartMenuItem into a different menu then it would still reference the original ContextMenu's placement target and, fundamentally, would present behaviour that is "broken".<br />
<br />
Binding, on the other hand, is <i>Dynamic</i>. The above code doesn't find our SmartMenuItem's parent ContextMenu and use its PlacementTarget as the CommandTarget at compile time. It does it when the code is executed during runtime. As you can imagine; this is much more flexible and intuitive.<br />
<br />
It's worth mentioning in closing that as a typical rule of thumb, all things dynamic are slower than their static equivalent. You're not going to see a huge performance hit the second you start using Binding but don't go using it just for the sake of it.Steve Lillishttp://www.blogger.com/profile/00752118450551854615noreply@blogger.com1tag:blogger.com,1999:blog-6491959830150703147.post-83948458850382108912011-09-14T01:14:00.000-07:002011-09-14T11:30:05.691-07:00Where to Begin?WPF isn't easy to learn. The separation of View and Data is a tough concept to get used to if you've been working in any environment where they've been tightly knit for an extended period of time. However, once you get over the initial learning "hump", it all makes a crazy kinda sense.<br />
<br />
It's now been a week or so since I started working with the Windows Presentation Foundation to create modern, professional looking apps rather than have to strong-arm normal Windows WinForm Controls into doing what I want.<br />
<br />
It has been an enjoyable - albeit equally frustrating - journey of learning and understanding so far. The two key points I'm going to dump here that nowhere on the internet really told me until I really dug in deep and that held me up for more than eight hours each are:<br />
<ol><li>Context Menus do not belong to the visual tree of the window. Why is this important? When you bind a Menu Item in a Context Menu to a Command, the on-screen Element you're right clicking (or any of its Ancestors for that matter) will not see it. This is easily rectified by binding the Menu Item's <em>CommandTarget</em> to the parent Context Menu's <em>PlacementTarget</em>. I wrote a very small SmartMenuItem class that figures out if it is part of a Context Menu and does this binding automatically if it is so that I never have to deal with this problem again.</li>
<li>Using Data Templates for a Style's Content Template is good for presentation <em>only</em>. When working with Data Templates it's easy to think that you're creating a Template of the Visual Tree that will be copied per instance of the object that uses this Data Template. This is not the case. The Data Template represents a single instance of the visual tree that will be <strong>shared</strong> across all objects implementing that Data Template. This is where <em>Binding</em> comes in. By binding sections of the Data Template to the values in the parent you require to be unique; you can make the one Data Template represent different information depending on the context of the instance referencing it.</li>
</ol>So at the moment these are my two main revelations regarding WPF. There is absolutely no doubt in my mind that WPF is an incredibly powerful tool that, once understood, will greatly reduce the development time of any visually impressive Windows application.Steve Lillishttp://www.blogger.com/profile/00752118450551854615noreply@blogger.com0