Like pancakes...

the random ramblings begin...
in

April 2009 - Posts

Random Visual Studio 2008 Tips #2
Select columns of text with Shift + Alt

If you ever find yourself copying and pasting a line of code to change one value in each line, this tip is for you. Let’s say you want to create 10 pieces of test data – to be aptly named test1, test2, and so on. So you write the first one, then copy and paste the line 9 times.

image

Now, of course, you have 9 lines to update. You can speed up the process by selecting the columns of “1”s. Place your cursor next to the character. Press Shift + Alt and then use your Left or Right Arrow keys to select the column to delete on your current line. Use the Down or Up Arrow keys and suddenly you’re selecting the column! It’s amazing!

image 

This is just a little time saver, but I happen to use it often, and really enjoy it’s simplicity.

Just one little note, make sure you use a left or right arrow before you use the down or up arrow – otherwise VS assumes you want to select the entire line of code.

image

Random Visual Studio 2008 Tips #1
IntelliSense Help Keyboard Shortcut: Shift+Alt+F10

You know, you’re writing code with a Type you haven’t property included and you see that little bar pop up on the right.

image

Instead of reaching for the mouse, try hitting Shift+Alt+F10 and suddenly:

image

Wow! VS wants to help! Hit Enter to accept the suggestion and have VS add the appropriate using statement.

That’s it. I like this one, hope you do too.

 

Oh, um, let’s not talk about the fact that VS tells you this in the tool tip…

image

WPF Drag and Drop Between ItemsControls

Last week I blogged about a generic Drag and Drop framework. That post focused on the framework and gave some simple implementations of it, but didn’t get into more practical examples. So, in honor of Opening Day, here’s a quick example of how to use the framework to create the Cincinnati Reds batting order by dragging from a list of players to another list.

Setup

The first thing we need to do is get all of our resources set up. I included the existing DragDropAdornerBase and DragDropHelper classes (discussed in the framework post) into my new project. Next, I set up the images of the Reds starting roster as application level resources.

Model

Now that our resources are in place, let’s make a quick object to represent a player. I named this class “Player” (surprise!). Player.cs has simple public properties to hold the data we’ll show on the screen: Picture, Name and Position.

namespace Clarity.Demo.ListDragDrop
{
    public class Player
    {
        public string Name { get; set; }
        public Position Position { get; set; }
        public BitmapImage Img { get; set; }

        public Player() { }

        public Player(string name, Position position, BitmapImage img)
        {
            Name = name;
            Position = position;
            Img = img;
        }
    }

    public enum Position
    {
        Pitcher = 1,
        Catcher = 2,
        First = 3,
        Second = 4,
        ShortStop = 5,
        Third = 6,
        Outfield = 7
    }
}

View

We’ll also need a UserControl to show the player information. PlayerControl inherits from UserControl and has no code behind. In order to represent the player data visually without code behind PlayerControl takes advantage of DataBinding to the Player model object.

<Canvas Background="Red">
   <Image x:Name="playerImage" Source="{Binding Img}" Margin="5 5 0 5" MaxHeight="90" MaxWidth="90"/>
   <TextBlock x:Name="name" Text="{Binding Name}" Background="Transparent" Margin="100 15 5 0"/>
   <TextBlock x:Name="position" Text="{Binding Position}" Background="Transparent" Margin="100 60 5 0"/>
</Canvas>

And now we have to build the window. Let’s make this as simple as possible, so we’ll just put 2 scrollable ItemsControls that use the PlayerControl as a DataTemplate.

<Window x:Class="Clarity.Demo.ListDragDrop.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.ListDragDrop"
    Title="Window1" Height="600" Width="800" Loaded="Window_Loaded">
    <Canvas>
        <UniformGrid Rows="1" Columns="2" Height="550">
            <ScrollViewer Margin="15 15 30 15" >
                <ItemsControl x:Name="playerList">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <local:PlayerControl Margin="5"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
            <ScrollViewer Grid.Column="1" Margin="30 15 15 15">
                <ItemsControl x:Name="lineup">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <local:PlayerControl Margin="5"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
        </UniformGrid>
    </Canvas>
</Window>

And now we’ll populate the playerList ItemsControl in the Window codebehind:

public partial class Window1 : Window
{
    ObservableCollection<Player> _players = new ObservableCollection<Player>();
    ObservableCollection<Player> _lineup = new ObservableCollection<Player>();

    public Window1()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        LoadPlayers();
        playerList.ItemsSource = _players;
        lineup.ItemsSource = _lineup;
    }

    private void LoadPlayers()
    {
        _players.Add(new Player("Aaron Harang", Position.Pitcher, 
            (BitmapImage)App.Current.TryFindResource("harang")));
        _players.Add(new Player("Alex Gonzalez", Position.ShortStop,
            (BitmapImage)App.Current.TryFindResource("gonzalez")));
        _players.Add(new Player("Brandon Phillips", Position.Second,
            (BitmapImage)App.Current.TryFindResource("phillips")));
        _players.Add(new Player("Chris Dickerson", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("dickerson")));
        _players.Add(new Player("Edwin Encarnacion", Position.Third,
            (BitmapImage)App.Current.TryFindResource("encarnacion")));
        _players.Add(new Player("Jay Bruce", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("bruce")));
        _players.Add(new Player("Joey Votto", Position.First,
            (BitmapImage)App.Current.TryFindResource("votto")));
        _players.Add(new Player("Ramon Hernandez", Position.Catcher,
            (BitmapImage)App.Current.TryFindResource("hernandez")));
        _players.Add(new Player("Willy Taveras", Position.Outfield,
            (BitmapImage)App.Current.TryFindResource("taveras")));
    }
}

Now, running the app gives us the following window:

image 

Implementation

OK, so at this point we’ve got the player model and view in place. Now, let’s add the fun stuff. First, let’s create an adorner for our drag and drop. Inheriting from the DragDropAdornerBase I’ve created a simple adorner that just shows the player picture (hiding the RenderTransforms, Animations and Triggers for simplicity).

<local:DragDropAdornerBase x:Class="Clarity.Demo.ListDragDrop.PlayerAdorner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.ListDragDrop"
    Height="75" Width="100">
    <UserControl.RenderTransform...>
    <UserControl.Resources...>
    <UserControl.Triggers...>
    <Grid x:Name="grid">
        <Grid.RenderTransform...>
        <Image x:Name="playerImage" Source="{Binding Img}" Margin="2"/>
    </Grid>
</local:DragDropAdornerBase>

Now that adorner control is created, that means we’re ready to add it as a resource to Window1.

<Window.Resources>
    <local:PlayerAdorner x:Key="adorner"/>
</Window.Resources>

Of course, there are three more pieces necessary to implement the drag and drop. 1) Window1 needs to contain an adorner layer, 2) the PlayerControl DataTemplates need to define the DragDropHelper attached properties to be recognized as Drag Sources, and 3) Window1 needs to subscribe to the DragDropHelper.ItemDropped event.

First the adorner layer gets added at the end of the Window1 xaml:

    </UniformGrid>
    <Canvas x:Name="adornLayer" Visibility="Collapsed"/>
</Canvas>

Next the playerList and lineup ItemsControls have their DataTemplates redefined:

<local:PlayerControl Margin="5"
                 local:DragDropHelper.AdornerLayer="adornLayer"
                 local:DragDropHelper.DragDropControl="{StaticResource adorner}"
                 local:DragDropHelper.DropTarget="lineup"
                 local:DragDropHelper.IsDragSource="true"/>
<local:PlayerControl Margin="5"
                 local:DragDropHelper.AdornerLayer="adornLayer"
                 local:DragDropHelper.DragDropControl="{StaticResource adorner}"
                 local:DragDropHelper.DropTarget="playerList"
                 local:DragDropHelper.IsDragSource="true"/>

And finally, Window1 subscribes to the ItemDropped event to add and remove the players accordingly.

void DragDropHelper_ItemDropped(object sender, DragDropEventArgs e)
{
    Player p = e.Content as Player;
    if (p == null) return;

    if (_players.Contains(p))
    {
        _players.Remove(p);
        _lineup.Add(p);
    }
    else if (_lineup.Contains(p))
    {
        _lineup.Remove(p);
        _players.Add(p);
    }
}

And that’s all it takes. Now we can drag and drop players from the left list to the right to set the batting order:

 image

And back right to left if you change your mind

image image

All source is available, “as-is” and without express or implicit warantee or support. Happy drag and dropping, and go Reds!

Next Steps

Stay tuned for Part 2, including some “hot-ification” of the dropping functionality.

WPF Image Sequencer (for png sequences)

Working with the illustrious design team at here at Clarity has opened my eyes about the user experience limitations of coding. Specifically, some of their concepts for a particular animation are great but just can’t be done (or at least not easily) with the built-in WPF animations.

And that is where the Image Sequencer comes in.

Originally used by a co-worker of mine, Erik Klimczak, to run the splash animation for the Clarity Endless Whiteboard, this simple WPF user control displays a sequence of images (.pngs here, but could be anything) and plays through them at just over 30 fps.

The xaml is extremely simple, just an Image to host the currently displaying frame:

<UserControl x:Class="Clarity.Demo.ImageSequencer.ImageSequencerControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Background="Transparent">
    <Grid>
        <Image x:Name="image" Stretch="Fill"/>
    </Grid>
</UserControl>

The real logic is the in the control’s code-behind. A simple timer is initialized to tick every 30 milliseconds.

public ImageSequencerControl()
{
    InitializeComponent();

    this.updateImageTimer = new DispatcherTimer(DispatcherPriority.Render);
    this.updateImageTimer.Interval = TimeSpan.FromMilliseconds(30.0);
    this.updateImageTimer.Tick += new EventHandler(this.updateImageTimer_Tick);
}

On every tick, the Image source is switched to the next image in the sequence.

private void updateImageTimer_Tick(object sender, EventArgs e)
{
    if (this.currentIndex == this.images.Count)
    {
        this.currentIndex = 0;
    }
    if (this.images != null)
    {
        if (((this.images != null) && (this.currentIndex < this.images.Count)) && (this.currentIndex >= 0))
        {
            this.image.Source = this.images[this.currentIndex];
        }
    }
    this.currentIndex++;
}

Basic Play() and Stop() methods start and stop the timer, and therefore the animation.

public void Play()
{
    this.currentIndex = 0;
    this.updateImageTimer.Start();
}

public void Stop()
{
    this.updateImageTimer.Stop();
}

Typically animations less than about 12 fps start looking choppy and anything too much over 30 fps is overkill. I suggest playing around with the timer interval to see this for yourself.

The only thing left, now, is to add the sequencer to Window1 and load the images for the sequencer to flip through. The control is added in the window xaml:

<local:ImageSequencerControl x:Name="sequencer" Width="576" Height="384" />

Note that I’ve used a width and height here based on the sizing of the .pngs I’m animating. Those values should be set to scale your images appropriately.

I’m loading the images and starting the animation in the Window code-behind at when the Window_Loaded event fires. To use this yourself you’ll have to modify the “filename” string to match your image file naming conventions.

public void LoadImages()
{
    _animationSeq = new List<BitmapSource>();
    string str = ".Images.whiteboard";
    string str2 = ".png";

    Assembly executingAssembly = Assembly.GetExecutingAssembly();

    for (int i = 0; i <= 300; i++)
    {
        string filename = string.Format("{0}{1}{2}{3}", this.GetType().Namespace, str, i.ToString("000"), str2);
        BitmapImage item = new BitmapImage();
        item.BeginInit();
        item.StreamSource = executingAssembly.GetManifestResourceStream(filename);
        item.CacheOption = BitmapCacheOption.OnLoad;
        item.CreateOptions = BitmapCreateOptions.None;
        item.EndInit();
        item.Freeze();
        _animationSeq.Add(item);
    }

    sequencer.Load(_animationSeq);
    sequencer.Play();
}

And that’s all there is to it! Simple, clean, and looks great. Download the source to try this out and see all the code. And don’t forget to check out the link above!

As always, this is provided “as-is” without any express or implied warrantee or support.