Pavan's profilethe WPF way...BlogLists Tools Help

Blog


    1/21/2008

    Moved my blog to "Pixel in Gene"

    Hello everyone!

               I have decided to move my blog to http://blog.pixelingene.com. This allows me greater control over making posts, adding images, attachments etc. Also I have the flexibility to play around with Page templates, CSS etc. Moving forward all posts will be made on "Pixel in Gene". I'll retain this blog as there are still links pointing here.

    I am in the process of moving all my posts. Once done I'll have a complete copy of "the WPF Way...".

    Please update your feeds by visiting the blog !!

    (Pixel in Gene)

     

    Thanks!

    Pavan Podila

    1/9/2008

    Community input on Write - Speak - Collaborate

    Over the past couple of months I have received fairly good response and feedback to some of the posts that I have made. I have also received some criticism that I don't share enough code.

    Moving forward in 2008, I would like to change that, at least by making small steps. I would like to take the community's feedback and input on topics that are of supreme interest (eg. ElementFlow). Here is a short list I came up with:

     

    Writing

    • Blog posts on interesting ways of applying the WPF concepts/ideas
    • Custom control development (topics of interest to community)
    • Advanced topics in custom control development (mixing 3D, Adorners, virtualization, etc)
    • Design patterns
    • Creative experiments
    • Write a book ??

    I will definitely be publishing more source code!

     

    Speaking

    • Start off with presentations at local User groups. I am in the North East area of US. I guess I could easily drive within a 3-4 hr radius. So that would include New Jersey, New York, Connecticut, Maryland, Delaware, etc. If you run a User Group or know of some, please do let me know.
    • Speak at conferences

     

    Collaboration

    • This could include some exclusive work
    • Side projects

     

    I am sure I can benefit from what the community thinks and it will also help me share / collaborate better. Looking forward to your views...

     

    Technorati Tags: , ,
    1/7/2008

    Degrafa goes live

    Although WPF development is my full-time activity, I also spend a considerable amount of time with Adobe Flex. Over the past couple of weeks I have been involved in an exciting Open Source project called Degrafa. Degrafa is a Declarative Graphics Framework for Flex that simplifies access to the Flash/Flex graphics API. People familiar with Flex know that creating simple shapes, paths requires Actionscript programming and cannot be done directly in MXML. This becomes very cumbersome especially when creating programmatic skins for Flex.

    Degrafa aims to solve this by exposing the Flex graphics API in MXML. This opens up a whole new world of opportunities and creativity. If you think this is useful to you, make sure to drop by www.degrafa.com. We have recently announced a Degrafa Derby contest to award the most creative application built using the framework.

     

    image

     

    We have lot of interesting features planned for the coming releases. There is also a converter app that will be made available for converting the juicy Degrafa graphics to XAML. Umm, I wonder who is writing that ;-)

     

    Technorati Tags: , , ,
    1/3/2008

    Smooth animations, at the Window level

    If you have ever tried animating the Width / Height of a Window control, you would notice some jerkiness during the animation. This happens because the Window control is not really living entirely in the WPF sandbox. It is part WPF and part Native. To achieve a really smooth animation, you have GOT TO BE inside the WPF sandbox. But how do I do it?

     

    The AnimationWindow

    Creating animations directly on the Window is definitely not a desired solution. You have to first push the window contents (both the client-area and the non-client-area) into the WPF sandbox. You can do this by deriving from the GlassWindow control. You can create very customized ControlTemplates and replicate the look of a native window. You also have the flexibility of completely changing the look and feel of your application windows. This is a first step towards moving the Window content into the WPF sandbox.

    However you still have the same problem when it comes to animating the window. That is because the GlassWindow is still using the native window wrapper and animating its Width / Height would still cause the jerkiness. What we need to do is push the entire window contents into a WPF control and then animate that control.

    This is exactly what the AnimationWindow does. It derives from GlassWindow and overrides its OnApplyTemplate() method. Inside the override it caches a reference to the top level container that contains the complete window contents (client + non-client). When you trigger resizing animations on the Window, the actual animation happens on this container. Since you are completely inside the WPF sandbox, the animations are smooth and your customers happy ;-).

     

    So what's the Trick ?

    [1] Derive from GlassWindow and override OnApplyTemplate()

     

       [TemplatePart(Type = typeof(Grid), Name = "PART_WindowContent")]
        public class AnimationWindow : GlassWindow
        {
            private Grid _contentGrid;
            private Storyboard _resizeAnimator;
    
            public static readonly DependencyProperty NewSizeProperty = DependencyProperty.Register(
                "NewSize", typeof(Size), typeof(AnimationWindow), new PropertyMetadata(new Size()));
    
            public Size NewSize
            {
                get { return (Size)GetValue(NewSizeProperty); }
                set { SetValue(NewSizeProperty, value); }
            }
    
    
            static AnimationWindow()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimationWindow), 
    new FrameworkPropertyMetadata(typeof(AnimationWindow)));
            }
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
    
                // Get a reference to the ContentGrid
                _contentGrid = GetChildControl<Grid>("PART_WindowContent");
            }
    }

    Before starting the animation you need to set the NewSize DependencyProperty. The GetChildControl<T> is a protected method on GlassWindow. Inside OnApplyTemplate() I am caching the top-level container as _contentGrid.

    [2] Expose a method to animate window

     

            public void ApplyResizeAnimation()
            {
                Size currentSize = new Size(Width, Height);
    
                // Temporarily resize the ContentGrid to avoid automatic layout flashes
                _contentGrid.SetValue(WidthProperty, currentSize.Width);
                _contentGrid.SetValue(HeightProperty, currentSize.Height);
    
                if (NewSize.Width > currentSize.Width)
                {
                    Width = NewSize.Width;
                }
                if (NewSize.Height > currentSize.Height)
                {
                    Height = NewSize.Height;
                }
    
                PlayAnimation(currentSize);
            }
    
            private void PlayAnimation(Size currentSize)
            {
                // Create Storyboard
                _resizeAnimator = PrepareStoryboard(currentSize);
                _resizeAnimator.CurrentStateInvalidated += ResizeAnimator_CurrentStateInvalidated;
                _resizeAnimator.Begin(_contentGrid, true);
            }
    
            void ResizeAnimator_CurrentStateInvalidated(object sender, EventArgs e)
            {
                Clock clock = sender as Clock;
                if (clock.CurrentState != ClockState.Active)
                {
                    Width = NewSize.Width;
                    Height = NewSize.Height;
    
                    // Clear Width/Height settings and switch to automatic layout
                    _contentGrid.ClearValue(WidthProperty);
                    _contentGrid.ClearValue(HeightProperty);
    
                    _resizeAnimator.CurrentStateInvalidated -= ResizeAnimator_CurrentStateInvalidated;
                    _resizeAnimator.Remove(_contentGrid);
                }
            }
    
            private Storyboard PrepareStoryboard(Size size)
            {
                Storyboard board = new Storyboard();
                board.FillBehavior = FillBehavior.HoldEnd;
    
                // Width
                DoubleAnimation wAnim = new DoubleAnimation(size.Width, NewSize.Width, 
    new Duration(TimeSpan.FromMilliseconds(500)));
                Storyboard.SetTargetProperty(wAnim, new PropertyPath("(0)", WidthProperty));
    
                // Height
                DoubleAnimation hAnim = new DoubleAnimation(size.Height, NewSize.Height, 
    new Duration(TimeSpan.FromMilliseconds(500)));
                Storyboard.SetTargetProperty(hAnim, new PropertyPath("(0)", HeightProperty));
    
                board.Children.Add(wAnim);
                board.Children.Add(hAnim);
                return board;
            }
    

    In ApplyResizeAnimation() I first explicitly set the Width + Height of the _contentGrid. I do this to avoid some automatic layout flashes. These would happen if my NewSize is greater than the current size. I then clear the explicit Width + Height settings when the animation gets over. Note that I am exploiting the precedence nature of the DependencyProperty.

     

    [3] Create a ControlTemplate for the Window with transparency

        <Style x:Key="TestWindowStyle"
               TargetType="{x:Type Controls:AnimationWindow}">
            <Setter Property="AllowsTransparency"
                    Value="True"/>
            <Setter Property="WindowStyle"
                    Value="None"/>
            <Setter Property="ResizeMode"
                    Value="CanResize"/>
            <Setter Property="Template"
                    Value="{StaticResource TestWindow_Template}"/>
        </Style>
    

     

    With all of these, your animations should run super smooth!

     

    Getting Creative

    Note that you can pull off some really exotic tricks with the AnimationWindow. Right now the resize animations are hard-coded. You can change that to expose a property on AnimationWindow to set custom Storyboards. If you club it with the TransitionContainer, you can even have Mac OSX style genie effects, right on the Desktop !!

     

    Download EXE

     

    Technorati Tags: ,,
    12/15/2007

    Controlling Z-Index of children in Custom controls

    In WPF, the standard way to control Z-Index (programmatically) is to use Panel.SetZIndex(). However that works well only if you plan to use a list of children inside a Panel. What if you want to get the Z-Index functionality in your own custom controls ?

     

    Override GetVisualChild()

    The way to do that is to override the GetVisualChild() method of your custom control.

    The GetVisualChild() requests for a Visual at a particular index. Generally you would return the child in the same order as in your internal visual collection. This gives you the effect of the Z-order, with the 0th child at the bottom-most position and the (n-1)th child at the top-most position. If you want to change this default ordering, it is possible to do that by returning a different child at the requested index.

    Reversing Z-order

    Say you want to reverse the child order. In that case you could write your method like so:

            protected override Visual GetVisualChild(int index)
            {
                if (index < 0 || index >= Children.Count)
                {
                    throw new Exception("Invalid Child index: " + index);
                }
    
                int zIndex = GetZIndex(index);
                return Children[zIndex];
            }
    
            private int GetZIndex(int index)
            {
                return (Children.Count - 1) - index;
            }
    
    

    Thus the trick with Z-ordering lies in the particular implementation of GetVisualChild().

     

    Technorati Tags: , ,
    12/10/2007

    WPF In Finance Contest [Promo Post]

    Got some great ideas? Want to do it in WPF and also earn some big bucks?

    A "YES" means you should check out Lab49's WPF Innovation Contest.

    image

     

    Technorati Tags: , ,
    11/19/2007

    Improved DragDropManager - Source code

    The DragDropManger has been a very handy class for me in couple of my projects. Since my last post I made some changes to the interfaces (IDragSourceAdvisor, IDropTargetAdvisor) and also to DragDropManager. There are no major changes except for a few method additions in the interfaces. These methods make it even more flexible.

    Changes to IDragSourceAdvisor and IDropTargetAdvisor

    Added method: UIElement GetTopContainer();

    In my previous version I had assumed there would be only one MainWindow. Hence I was using Application.Current.MainWindow as my default top container. Of course this assumption worked well for some of my projects but broke when I had to do DnD across windows. This lead me to add the GetTopContainer() method, which is queried for the right container (AdornerDecorator).

    How is this useful? Say for example you have a DragSource in one window and a DropTarget in another window. When an element in dragged onto the target window, the location of the feedback-UI is no longer relative to the MainWindow, instead it is relative to the target window. By querying the IDropTargetAdvisor for the top-container we can see the feedback-UI at the right place.

    [Note] If you are using just the MainWindow, you can return Application.Current.MainWindow as your top container.

    Added read-only property ApplyMouseOffset to IDropTargetAdvisor

    When an element is moved from the source to the target, the initial click on the source-element may be at an offset inside its bounding-box. If the same offset needs to be applied to the feedback-UI on the target, the property ApplyMouseOffset can be set to True. In most cases you would set it as True. To accommodate a few outlier cases, this property had to be added.

     

    That is all for the changes. I hope you find the code useful.  If you do anything cool with it, I would love to know and shower praise on you ;)

     

    Download DragDrop.zip

     

    Technorati Tags: , ,
    11/6/2007

    SlideDeckPanel - panel that does Card Deck layout

    In one of my recent explorations in a project I had to implement a panel layout that displayed its items as cards that have been fanned out right->left.

     

    image

     

    Effectively the cards are laid out right-to-left, with the card on the right overlaying on the previous one. If the panel has enough space to accommodate all cards, there would be no overlap. As we keep increasing the number of cards, the amount of overlap increases appropriately.

     

    image

     

    Selections and Hovers

    image

    Selections and Hovers are an integral part of interacting with a list of items. There are some default selection and hover behaviors that are associated with this layout. However we wanted to make it customizable from the component point-of-view. Thus it is possible for an user of this panel to provide custom animations for selection and de-selection. On the same note, it is also possible to create custom adorners for hovers. The selection + hover logic has been abstracted enough to keep things very simple. The programmer only needs to provide Storyboards for animating the selection/deselections. As regards the adorners for hovers, the panel only needs a ControlTemplate (with standard names for placeholders).

    Using these ideas it is also possible to change the selection and hover animations on the fly. The following video demonstrates these concepts. I use two kinds of selections (None, JumpSelection) and two kinds of hovers.

     

     

     

    In a future post I'll go over some details about how you can implement such dynamic custom controls/panels.

     

    Technorati Tags: , ,
    10/10/2007

    File organization tip for Custom Control authors

    Custom controls can be fun to develop. Depending on the complexity of the control it would be a good practice to break it down into manageable pieces. While developing ElementFlow, I discovered such a technique which you may also find useful.

     

    ElementFlow is a fairly complex custom control and there are many interacting pieces in it. Here is a quick listing:

    • 3D setup
    • Handling meshes
    • Animations
    • Viewstates (Coverflow, Timeline, etc)
    • User input handling (Keyboard, Mouse)

    Putting all of this logic in the same file (ElementFlow.cs) made it a little cumbersome for me to navigate the file. Ofcourse I could use #region and #endregion to demarcate blocks of code but it was not that appealing. It works great to organize simpler elements like properties, fields, constructors, etc.

    A Better way

    What I was really looking for was a higher-level logical organization. The answer lied in using Partial Classes. It's a great language feature and should be exploited :) Most of us know that the Visual Designers in VS use partial classes to keep the code-behind separate from the "designer-vomit". It keeps our code-behind neat and readable. As control-authors we can leverage this feature to our benefit. To better understand, let me describe the way I used it.

     

    As described earlier, ElementFlow has many logical pieces. Using partial classes, these pieces can be pushed to separate files, keeping the original ElementFlow.cs file pretty short. I decided to organize it into four files:

    1. ElementFlow.cs : contains public-facing properties, constructors and methods. This serves more like a definition of the control.
    2. ElementFlow.3d.cs : contains most of the Viewport3D, mesh-handling logic
    3. ElementFlow.animation.cs : contains creation of Storyboards, manipulating animations, viewstates etc
    4. ElementFlow.input.cs : contains logic to handle user-input

     

    Keeping such a separation has given me a convenient way to see the different parts of my control. In future when I have to expand the functionality I could manipulate one of these files [or create a new partial class]. Adopting a convention for file-naming could be the next step when your organization is primarily into custom control development.

     

    As a side-thought, this post could be corollary to my earlier article on control development.

     

    Technorati Tags: , ,
    8/30/2007

    TransitionContainer: Easy transitions between views

    Transitions, which is another word for animating between views, is a great way of keeping the user engaged as he interacts with your application. Most applications would contain a wide variety of views, where each view aids in interacting with a specific functionality of the application. When switching to a different view, a gradual animated change is far superior than an instant switch. It gives a context to the user telling him where he was earlier and where he is going to next.

    TransitionContainer is my custom control that can do a wide variety of transitions, 2D as well as 3D. It contains a bunch of transitions, which come out of the box as well as a really simple framework for creating custom transitions. The transitions that come by default or also implemented using the same framework.

    Before I go into details about the framework, lets have a look at a video of the transitions in action. Below you can see my favorite transitions: Genie, Cube and Slide... you can tell where I get my inspiration from ;)

     

     

     

    TransitionContainer and the framework

    The TransitionContainer is a host element that takes care of all the nitty-gritties of applying transitions between views. A quick snippet of XAML shows its usage:

        <Controls:TransitionContainer x:Name="_transContainer">
          <Image x:Name="_image1"
                 Source="/Resources/img1.jpg"
                 Stretch="Fill"/>
          <Image x:Name="_image2"
                 Source="/Resources/img2.jpg"
                 Stretch="Fill"/>
        </Controls:TransitionContainer>
    

    Here the container contains a set of Images. In general it can contain any UIElement (aka views). TransitionContainer.Transition property points to an instance of a class that implements the transition. A transition between two views can be invoked by calling TransitionContainer.ApplyTransition(string, string). The two string parameters point to the names of the two children between which the transition is applied. In the example above, the transition between the images can be applied by calling:

                _transContainer.ApplyTransition("_image2", "_image1");

     

    You can see that it is really easy to use the control. Just dump all your views inside the TransitionContainer, set a Transition and call ApplyTransition(). Easy.

     

    Transitions

    Transitions are pluggable classes and you can switch the transition to use at runtime (and at will ;)). Each transition derives from the abstract class Transition:

        public abstract class Transition
        {
            public abstract void Setup(Grid container, VisualBrush prevBrush, VisualBrush nextBrush);
            public abstract void PlayTransition(Grid container, TransitionContainer transContainer);
        }
    

    ApplyTransition() internally calls the Setup() method first followed by PlayTransition(). In Setup() you can create all the elements that are needed during the transition. PlayTransition() is where the actual transition will be seen on screen. Here you can run Storyboards on the elements you created earlier. At the end of the transition, you are supposed to call TransitionContainer.FinishTransition(). This does some cleaning work, fires a TransitionCompleted event and then brings the new view for user interaction.

    [I am in the process of refactoring the APIs so that only the TransitionContainer would need to be passed into the Transition. The first parameter (Grid container) is unnecessarily exposing an implementation detail]

    I use the convention of suffixing all my transition classes with -Transition, as in GenieTransition. As a side note, the GenieTransition is actually very thin wrapper around my GenieAnimation class.

     

    Technorati Tags: , , , ,
    8/27/2007

    The iPhone (-like) interface in WPF

    Sometime back I had worked on an internal Instant Messaging client for our company. We were looking for some inspiration for our client interface and at about the same time the iPhone was introduced. After a lot of thought (which was like "few minutes"), we decided to mimic the iPhone interface for our client.

    The enabling UI technology was Windows Presentation Foundation (WPF). With WPF we could rapidly build something that looks a lot like iPhone in a very short time. I was playing the dual role of a Designer + Developer and tools like Blend and Design greatly helped in skinning the app. Below you can see a bunch of screenshots and a short video of accepting a call.

     

    Notification received. You can talk, IM or just ignore.

    image

     

    In an audio chat (call)

    image

     

    Contact list

    image

     

    Video of accepting a notification (via Talk)

     

    How was it built?

    • Blend and Design were used for creating the skin of the app. The XAML UI was created in Blend and the graphics were developed in Design. I had made a post earlier about how the icons for the app were created in Design.
    • The GlassWindow control was used for creating the window chrome.
    • Although you cannot see in the screenshots above, the Drag 'n' Drop library was also used
    • I missed out using the ElementFlow, but that can be easily added. I only need to find a business case for that ;)

     

    This example only goes to show how powerful WPF can be. With greater tool support from Blend + Design, lot can be achieved in a far lesser time. Now until I get my own iPhone, I will continue to use the WPF version ;)

     

    Technorati Tags: , , ,
    8/23/2007

    ElementFlow is now a Panel !!!

    The ElementFlow control was something I was working on a while back and I even posted about it here. The control originally derived from a FrameworkElement and had its own properties for enabling binding to a data-source, namely ElementsSource and ElementTemplate. These properties behave similar to the ItemsSource and ItemTemplate of ItemsControl.

    One could argue that instead of deriving from FrameworkElement, I should derive from ItemsControl. That way I would get the Databinding support free of cost. But making ElementFlow an ItemsControl means that I could use any Panel as my ItemsPanel. This ofcourse is NOT true. ElementFlow is primarily meant to visualize items in 3D. This means I would be using Viewport3D and GeometryModels to represent my items. Using any other kind of panel doesn't really work well and is simply not correct. Also I think it violates the Liskov Substitution Principle (LSP).

     

    However, ItemsControl does take care of data-binding by automatically generating the ContentPresenters for the items in the data-source. That idea led to a philosophical debate with myself, where I was arguing (with myself) that ElementFlow should really be handling layout of the items in 3D and should not worry about the data-source and deciphering DataTemplates. Its primary job is really to layout items in 3D.

    The component in WPF that does pure layout is a Panel. Making ElementFlow a Panel was "definitely" the way to go.

     

    Panel issues

    Deriving directly from Panel required me to make couple different changes to the control and I also ran into a few issues along the way:

    • Previously I was handling the part where I would generate the ContentPresenters from ElementsSource and ElementTemplate. That all had to go away, since none of it is needed any more.
    • Next I had to hook into the stage where the Panel makes a change to its visual-children collection. This can be done by overriding

            protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)

    I make a change to my Viewport3D (adding/removing models) within this method.

    • I also ran into an issue when setting ElementFlow as the ItemsPanel inside an ItemsControl. It had to do with the way ItemsPresenter uses the ItemsPanel property of ItemsControl. ItemsPresenter requires that the count of the visual children of the Panel should be 0 at the beginning (ie before populating the Panel). However inside my VisualChildrenCount override, I was always returning "1", indicating that I only have one visual child, which is my Viewport3D. Obviously I am not supposed to return "1" all the time. To circumvent this issue I call into the base-class to get me the VisualChildrenCount. If the count is zero, I return zero, else I return one.

     

    Once past those issues, I had ElementFlow working in collaboration with ItemsControl. I can now get all my UI elements from the ItemsControl and not have to worry about data-binding and its related issues (children add/remove/update). This also saves me lot of development and maintenance time, as I rely more on the framework for things its really good at, namely DataBinding!

     

    Note that ElementFlow also works independently of ItemsControl, just like any other Panel :)

     

    Technorati Tags: , , , ,
    8/17/2007

    ItemSkimmingPanel - a panel that does more than just layout

    Recently Apple released their next version of iLife, which has some cool enhancements to iPhoto and iMovie. One specific enhancement called "skimming" was interesting from my project's perspective. The idea is that when you scrub your mouse over an album, it skims through all the photos in that album. The same interaction applies when you scrub your mouse over a movie clip: it skims through all of its frames.

    We had a similar requirement for viewing all the items inside a container. Skimming looked like a neat way of doing that. We had a couple of different ways of implementing this interaction but finally we decided on making this interaction as a Panel. The justification can be summarized in the bullets below:

    • Panel has a SetZIndex() API that is used to pop items to the front. This is useful to bring an item to the top of the stack.
    • Panels can be used in conjunction with ItemsControl to leverage data-binding. This can be done by setting the ItemsPanel property of ItemsControl

     

    We added an extra feature to skimming, which we call "Item Context". When you are skimming through a bunch of items, you would also want to know where the item is positioned in relation to others. In other words you would want to know the context of the item relative to others. It is also very handy to quickly visualize your stack (aka container). Thus from a top level we have two different concepts in our ItemSkimmingPanel:

    1. It skims through all the items in the Panel when you scrub your mouse on it. Each "skimming" action brings the item to the top of the stack.
    2. When you start the skimming process, a context is also displayed to see the relative location of the item.

     

    Both of these ideas can be seen in the video below. In the first half of the video you can see the skimming action without the context. The second half shows the context as you skim through the items. It will be clear how effective the skimming can be when the context is displayed.

     

     

     A quick note on internals

    The ItemSkimmingPanel leverages the fact that a Panel can automatically handle the ZIndex of an item. We listen to standard mouse events (MouseDown, MouseUp, MouseMove) to achieve the effect of scrubbing. To prevent very rapid scrubs we make sure that the mouse moves a specific distance between each scrub. The context for skimming is shown using Adorners. The Adorner is positioned relative to the panel that is being scrubbed. Skinning of the context is supported via ControlTemplates. There are two Dependency properties on ItemSkimmingPanel used specifically for this purpose: ContextContainerTemplate and ContextItemTemplate. The names should be self-explanatory.

    All of the skimming and context display logic is nicely encapsulated inside the ItemSkimmingPanel.

     

    Technorati Tags: , , , , , ,
    8/3/2007

    Creating WPF based addins with System.AddIn

    With Framework 3.5 we have a new namespace called System.AddIn which is useful for creating AddIn based application architectures. For an introduction to this namespace and its usage refer to:

    CLR AddIn blog

    Jason He's Paint.Net adventure with System.AddIn

     

    WPF AddIns

    When creating WPF AddIns one of the first things you would want is the ability to exchange some UI related classes across AppDomains. Doing so requires us to marshall these classes using a Contract (as per System.AddIn parlance). The WPF team has created a new namespace System.Windows.Presentation that provides this contract for exchanging WPF elements. Since the AddIn does not directly depend on the Contract, the exchange over the wire happens through an Adapter class. The Adapter will adapt the Contract to the View or the View to the Contract based on whether it is translating on Host side or the AddIn side.

    System.Windows.Presentation contains a class called VisualAdapters that does exactly this job. When transmitting the Visual (System.Windows.Media.Visual) from the AddIn side, one needs to convert it to a Contract. The Contract interface defined for a Visual is called System.AddIn.Contract.INativeHandleContract defined in the System.AddIn.Contract assembly. VisualAdapters.ViewToContractAdapter is the method that does this conversion for us.

     

    public static System.AddIn.Contract.INativeHandleContract ViewToContractAdapter(System.Windows.Media.Visual root)
        Member of System.AddIn.Pipeline.VisualAdapters

    Similarly to translate from a Contract to a Visual we have the complementary method on VisualAdapters: ContractToViewAdapter. Note that this method is not exactly symmetrical with the previous one. It does not return a Visual, instead a FrameworkElement.

     

    public static System.Windows.FrameworkElement ContractToViewAdapter(System.AddIn.Contract.INativeHandleContract nativeHandleContract)
        Member of System.AddIn.Pipeline.VisualAdapters

     

    Passing Collections across AppDomains

    Once you start building functionality into your addins you would soon hit the requirement to pass collections. Fortunately we already have contracts defined for them. System.AddIn.Contract defines the IListContract<T> which is a contract for passing IList. The adapter for this contract is defined in System.AddIn: CollectionAdapters. It contains a bunch of generic methods for translating between IList<T> and IListContract<T>.

    I need more Contracts!

    One of the AddIns I am trying to develop requires me to pass things like VisualBrush, Storyboard, etc. However there is no contract defined for these classes. Hopefully the WPF team will add it in the future or make some available via a CodePlex project ! I hope someone from the WPF team is reading this :)

    Technorati tags: ,
    7/2/2007

    Making the Scrollbar work (with DragDropManager)

    This post is a corollary to a set of posts I did on the DragDropManager component.

     

    If you have been using the DragDropManager with a ListBox, you may have encountered an issue where you are not able to scroll using the ScrollBar. There are couple of reasons for this:

    1. I have setup Preview events on the UI control on which you attached the DragDropManager. This traps all the mouse events happening on the control.
    2. When you try to use the ScrollBar to scroll, the DnD manager comes in and checks if a Drag-gesture is being made. This traps the mouse events for the ScrollBar and hence it never sees it. This is the default behavior since the ScrollViewer is part of the ControlTemplate for the ListBox and hence considered part of the control.
    3. When the PreviewMouseLeftButtonDown event occurs, the dragged-element that is returned turns out to be the ListBox itself and not the ListBoxItem or ContentPresenter (in case of a Databound ListBox). Thus it is not clear from the dragged-element, whether the MouseDown event happened on the ScrollBar or the List item.

     

    So what's the workaround?

    The main reason the Scrollbar doesn't work is because of it being part of the ControlTemplate. Hence we need to take it out. We can then put the ListBox inside a ScrollViewer separately and get the desired scrolling behavior. Now the mouse preview events do not include the ScrollBar anymore.

     

    A side note

    I have improved the DragDropManager and made some changes to the IDragSourceAdvisor and IDropTargetAdvisor interfaces. Will make a post on that soon :)

    Technorati tags: , , ,
    6/26/2007

    BalloonDecorator source code

    image image

     

    Sometime back I had posted about a simple Decorator that draws Balloon Text, similar to the speech balloons you see in comic strips. I called it the BalloonDecorator. At the time of the post I found some minor bugs and delayed posting the source. Although I fixed the bugs I was a little lazy to upload the source. Thanks to few of my readers for nagging me :) I finally got around to do just that.

     

    Download Source

    Original Blog entry

     

    Technorati tags: , , ,
    6/14/2007

    Nice article on extending WPF animation classes - MSDN

    Charles Petzold has published an interesting article on creating custom animation classes. Read it here.

    Sometime back I had used these concepts in making the GenieAnimation. It would have saved me some time if Charles had published this article earlier :)

     

    Technorati tags: , ,
    6/5/2007

    The ElementFlow custom control - can do CoverFlow too!

    I am sure many of you have heard and even interacted with the CoverFlow view of iTunes. In the WPF world we have the good folks from Thirteen23, who have created an app called Harmony that shows off the use of CoverFlow. They make good use of the Viewport3D to pull the desired effect.

    Recently I had the opportunity to develop a custom control similar to CoverFlow. I call this control the ElementFlow, because besides CoverFlow, the control can do lots of other views. In fact the whole view part of the control has been abstracted out so you can plug in your own view (if you wish). This is possible by creating a new ViewState and providing the necessary animations to flow the items.

    Since the control is supposed to layout a list of items, it behaves very similar to an ItemsControl. However for various reasons I have not derived from ItemsControl (Why? - that's a topic of a different blog post).

    To demonstrate this control I have captured two videos, one which contains reflections and one without. In the first one I show only the CoverFlow control (with reflections ON). In the second video (reflections OFF) I start with the CoverFlow view and then switch my ViewStates. You can see that the view transition is very fluid and the selected-item is retained on different ViewStates. [Note that not all layout-related dependency properties have mappings for different ViewStates.]

     
    YouTube - ElementFlow control (showing CoverFlow with reflection)

     

     
    YouTube - ElementFlow control - showing other ViewStates

     

    Some of the inner workings

    • There are two ways in which you can add items to the ElementFlow control: creating the UIElement directly or by specifying a ElementsSource and ElementTemplate. Whenever an item is added, it is kept hidden separately and a VisualBrush of that item is texture-mapped onto the 3D Mesh. The mesh is then positioned according to its item-index in the list.
    • I derive from a FrameworkElement instead of ItemsControl.
    • To change the visual tree I override the following methods: GetVisualChild(), ArrangeOverride(), MeasureOverride() and the property: VisualChildrenCount
    • Viewport3D is my only visual-child

     

    What are the features of this control?

    • Can do multiple forms of 3D list visualizations using ViewStates. Some of the out-of-box ViewStates include: CoverFlow, RollerCoaster, TimeMachine, Rolodex
    • Pluggable ViewStates
    • Fluid animations when switching between ViewStates
    • Items can be added directly via XAML or in code using the Children property
    • Supports data-binding via ElementsSource + ElementTemplate
    • Detects changes to collection - Add/Remove/Update
    • Various properties to control layout of items - ItemGap, FrontItemGap, PopoutDistance, TiltAngle. Changing these properties causes animated changes on the view.
    • Mouse, Keyboard and Mousewheel interaction

     

    Future updates

    • Virtualization of the view when dealing with a huge collection. Got to dig up those posts by Dan Crevier and Ben Constable :), think VirtualizingStackPanel and IScrollInfo !!
    • I can't release the Source code yet but keep visiting back :)

     

    5/29/2007

    Structuring a WPF User Interface

    When creating a typical user interface, say a Window, Page or UserControl, we start with the top-level container and nest other containers or controls inside it. This is a good approach for a first attempt. But just like we refactor our code to make it more maintainable, we should also be refactoring the User Interface by applying similar principles. Such a refactoring leads to a more maintainable view. Also since we are only refactoring, the final view looks the same but the internal construction is different. In this blog post I'll present some such refactorings in the context of the WPF User Interface. Note that the concepts I present here are already popular when developing with Adobe Flex.

     

    The IM Client example

    Lets say for example we want to create a Contact-list window for an IM client.

    This view could be created with the following nesting of components:

    In this simple structuring we have just used a Grid and laid out all the controls inside it. Since we will also be data-binding we can set up all the bindings directly in this view. This works for now but is not very amenable when your designer comes up with a better layout for the view.

    The next logical step is to group related controls together inside panels. So I could put the top 3 Label controls inside a vertically-oriented StackPanel. I could then group the Icon and the StackPanel into a DockPanel and so on. Grouping these controls helps in making the view more organized. However we are still dealing with a pretty big tree of UI components. It may seem very manageable right now and you will also find it easy to set up the bindings. However if you try navigating this view after a week or more you will see that it takes some time to gear up to how things are nested.

     

    Making the "UserControl" work for you

    UserControls come in very handy when structuring views and work very well as organizing containers. Also since a UserControls has a DataContext property we can also set up data-bindings easily. The primary purpose of the UserControl is to contain related controls and present them as one single abstracted control. Lets look at our IM client example. If we take a step back we can imagine a more high-level organization of controls like so:

    Now the Window looks more readable and we can easily identify its core UI components. Each of the views in the above figure represent a UserControl. By organizing the controls inside such UserControls we can even reuse peices like the UserInfoView, ContactInfoView in other Windows/Pages/UserControls.

     

    Communicating between UserControls

    The UserControls by themselves have no clue about the presence of other UserControls, so it is left to a top-level container to orchestrate the communication. We can create a top-level UserControl, say the DashboardView which would internally host all the other views. All the logic of communicating between views could now be contained inside DashboardView. The UserControls themselves inform to the outside world about interesting things by firing events. This is done by setting up RoutedEvents. For example in our SearchView, we could have a SearchChanged event being fired whenever the user presses the Enter key on the TextBox. This abstracts the view and delivers the required information in the form of a RoutedEvent. Here is the sample code from the SearchView UserControl:

     

    public partial class SearchView : UserControl { public static RoutedEvent SearchChangedEvent = EventManager.RegisterRoutedEvent("SearchChanged", RoutingStrategy.Bubble, typeof(SearchChangedEventHandler), typeof(SearchView)); public event SearchChangedEventHandler SearchChanged { add { AddHandler(SearchChangedEvent, value); } remove { RemoveHandler(SearchChangedEvent, value); } } public SearchView() { this.InitializeComponent(); // Insert code required on object creation below this point. } public string SearchText { get { return _text.Text; } } private void Textbox_KeyDown(object sender, KeyEventArgs args) { if (args.Key == Key.Enter) { SearchChangedEventArgs evtArgs = new SearchChangedEventArgs(); evtArgs.RoutedEvent = SearchChangedEvent; evtArgs.SearchText = _text.Text; RaiseEvent(evtArgs); } } }

    Similarly such RoutedEvents could be set up for other views.

     

    Setting up Databindings

    Typically you would have a model object-tree for your top-level view. For setting up data-bindings, you could pass the model-object as the DataContext for the UserControl. In the above example, I could have a root model-object as IMClientModel, which has a property CurrentUser of type Person. The Person object has properties such as FirstName, LastName, Availability, Email, Address, etc. In my DashboardView I could then pass the IMClientModel.CurrentUser as DataContext to the UserInfoView.

    <views:UserInfoView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{StaticResource DarkBlueGradient}" Grid.Row="0" DataContext="{Binding CurrentUser}"/>

    The DataContext of the DashboardView is set to the instance of IMClientModel, which is why I can simply use {Binding CurrentUser} on UserInfoView. When inside the UserInfoView, you would have Bindings to FirstName, LastName, Availability etc. Notice how the UserInfoView is self-contained and only concerned with the Person object.

     

    Summary

    The basic idea behind such view-refactoring is to make things easy for Developers and Designers. It makes the view more digestible and keeps it simple. By assembling views, instead of assembling primitive controls, we have greater flexibility in modifying the view. By adding RoutedEvents to UserControls, we can establish communication with rest of the views/outside-world. Databinding can set up using the DataContext property of the UserControl.

     

     

    5/10/2007

    XamlXporter - Formatting numbers for German Locale

    Alexander Ullrich from Germany has been kind enough to share a patch of the XamlXporter library (Exporter.Core), which fixes the formatting of the floating point numbers for German locale.

    If you are facing such an issue you can download the patch here.

     

    Thanks Alex!

     

     

    Technorati tags: ,