![]() |
|
Spaces home the WPF way...ProfileFriendsBlogMore ![]() | ![]() |
|
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.
Please update your feeds by visiting the blog !!
Thanks! Pavan Podila 1/9/2008 Community input on Write - Speak - CollaborateOver 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
I will definitely be publishing more source code!
Speaking
Collaboration
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...
1/7/2008 Degrafa goes liveAlthough 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.
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 ;-)
1/3/2008 Smooth animations, at the Window levelIf 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 !!
12/15/2007 Controlling Z-Index of children in Custom controlsIn 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().
12/10/2007 WPF In Finance Contest [Promo Post]11/19/2007 Improved DragDropManager - Source codeThe 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
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 ;)
11/6/2007 SlideDeckPanel - panel that does Card Deck layoutIn 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.
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.
Selections and Hovers
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.
10/10/2007 File organization tip for Custom Control authorsCustom 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:
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:
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.
8/30/2007 TransitionContainer: Easy transitions between viewsTransitions, 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.
8/27/2007 The iPhone (-like) interface in WPFSometime 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.
In an audio chat (call)
Contact list
Video of accepting a notification (via Talk) How was it built?
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 ;)
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
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 :)
8/17/2007 ItemSkimmingPanel - a panel that does more than just layoutRecently 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:
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:
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.
8/3/2007 Creating WPF based addins with System.AddInWith 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: Jason He's Paint.Net adventure with System.AddIn
WPF AddIns
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) 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)
Passing Collections across AppDomains
I need more Contracts!
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:
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 :) 6/26/2007 BalloonDecorator source code
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.
6/14/2007 Nice article on extending WPF animation classes - MSDNCharles 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 :)
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 |