Blogs

The One With

iPhone like Touch Interface with Silverlight Grid

In the recent year or two, touch interfaces (with the help of cool devices like iPhone) become more popular then ever. Even some of the new desktop machines are now touch enabled. If you have been to PDC2008, you might have seen those cool all in one HP TouchSmarts. They were showing them off during the keynote and at the DevExpress booth, had them for our own demos. In fact, ever since PDC, I have been using one exclusively instead of a laptop. (And yes, I carry it with me everywhere :)).

For Windows 7, Microsoft is introducing a new set of APIs to deal with multi-touch and so does Silverlight 3. And as developers, we should start exploring this relatively new UI concepts and think about whether or not it is applicable to our own apps.

In this article, we are going to explore the AgDataGrid from DXperience 2009 vol1 and how to customize it to look and feel just like an iPhone. In fact, we are going to do much more then that…we are going to build a complete “Soft-Phone” and IM experience using Silverlight 3, WinForms and the controls from DXperience 2009 vol1.

 

image

Pre-requisites

 

First Steps

Let’s start with the AgTouchGrid (our customized AgDataGrid) first and deal with the backend and architecture later.

We will need reference the following assemblies:

  • DevExpress.AgCore.v9.1: This is where we keep the most common classes and utils and extensions methods.
  • DevExpress.AgData.v9.1: The place for all data related helpers. Most notably, this is where our DataController lives.
  • DevExpress.AgEditors.v9.1: A new assembly, and as the name implies, contains all the basic data editors and the plumbing to reuse them as standalone or inside the AgDataGrid+.
  • DevExpress.AgDataGrid.v9.1: The AgDataGrid and family.

Inheriting from AgDataGrid:

public class AgTouchGrid : DevExpress.AgDataGrid.AgDataGrid {
    public AgTouchGrid() {
        DefaultStyleKey = typeof(AgTouchGrid);            
    }
}

we’ll create a custom template for our new AgTouchGrid.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DevExpress.AgDataGrid;assembly=DevExpress.AgDataGrid.v9.1"
    xmlns:touch="clr-namespace:DevExpress.Exchange.Communicator.Silverlight.Controls"
    xmlns:internal="clr-namespace:DevExpress.AgDataGrid.Internal;assembly=DevExpress.AgDataGrid.v9.1"
    xmlns:utils="clr-namespace:DevExpress.AgDataGrid.Utils;assembly=DevExpress.AgDataGrid.v9.1"
    xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
    
    <!--AgTouchGrid-->
    <Style TargetType="touch:AgTouchGrid">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="touch:AgTouchGrid">
                    <Grid x:Name="Surface">
                        <Grid.Resources>
                            <Storyboard x:Name="TouchStory">
                                <DoubleAnimation x:Name="Touch Animation" Duration="0:0:0.3000" 
                                                 Storyboard.TargetName="VerticalScrollbarElement" Storyboard.TargetProperty="Value"  />
                            </Storyboard>
                        </Grid.Resources>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"  />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid Name="RootElement" Background="White" Cursor="Hand">
                            <Grid.RowDefinitions>
                                ....
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                ....
                            </Grid.ColumnDefinitions>
                            
                            .....
                            
                            <Grid Grid.Column="2" Grid.Row="6" Grid.RowSpan="1" Width="17" Visibility="Collapsed">
                                <ScrollBar x:Name="VerticalScrollbarElement" Orientation="Vertical" />
                            </Grid>
                            
                            <ContentControl x:Name="CellsClipperElement" Background="White" Grid.Column="1" Grid.Row="6">
                                <internal:AgLineStackPanel 
                                        x:Name="CellsPresenterElement" 
                                        Orientation="Vertical" ArrangeAccordingToVisibleIndex="True" 
                                        LinesWidth="{TemplateBinding HorizontalLinesThickness}"
                                        ShowLines="{TemplateBinding ShowHorizontalLines}" 
                                        StretchLines="ByPreviousAndNextElements" 
                                        VerticalAlignment="Top" />
                            </ContentControl>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>       
    </Style>  
</ResourceDictionary>

The easiest way to start is to copy generix.xaml from the AgDataGrid sources into your own themes\generic.xaml. We will keep everything as in the original template except for a view things:

1: We’ll wrap the VerticalScrollbarElement with a Collapsed Grid panel. This will ensure that we still have the default scrolling logic and the mouse wheel working.

2:

....
<Setter Property="SelectedCellStyle">
           <Setter.Value>
               <local:AgStyle>
                   <local:AgSetter Property="Foreground">
                       <local:AgSetter.Value>
                           <SolidColorBrush Color="White"  />
                       </local:AgSetter.Value>
                   </local:AgSetter>
                   <local:AgSetter Property="Background">
                       <local:AgSetter.Value>
                           <SolidColorBrush Color="White" />
                       </local:AgSetter.Value>
                   </local:AgSetter>
               </local:AgStyle>
           </Setter.Value>
</Setter>
....

We’ll remove all the highlighting of cells and rows by setting them all to White or whatever you choose your default background to be. This will make the Grid look like it is “not selectable”.

We can now use it like so:

....
xmlns:Touch="clr-namespace:DevExpress.Exchange.Communicator.Silverlight.Controls"
xmlns:AgDataGrid="clr-namespace:DevExpress.AgDataGrid;assembly=DevExpress.AgDataGrid.v9.1" >
....
<Grid x:Name="LayoutRoot" Background="White">
        <Touch:AgTouchGrid Margin="1,0,1,1" x:Name="contactList" ColumnsAutoWidth="true" Visibility="Visible" ShowColumnHeaders="Collapsed">
            <AgDataGrid:AgDataGrid.Columns>
                <AgDataGrid:AgDataGridColumn FieldName="Name" AllowEditing="false">
                    <AgDataGrid:AgDataGridColumn.DisplayTemplate>
                        <ControlTemplate>
                            <Grid Height="40">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"  />
                                    <ColumnDefinition Width="*"  />
                                </Grid.ColumnDefinitions>
                                <Grid VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0">
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="32"></ColumnDefinition>
                                            <ColumnDefinition Width="*"></ColumnDefinition>
                                            <ColumnDefinition Width="16"></ColumnDefinition>
                                        </Grid.ColumnDefinitions>
                                        <Image Grid.Column="0"  VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="Icon" Source="{Binding Edit.DataContext.RowValue.Icon}" Width="16" Height="16"  />
                                        <Grid Grid.Column="1" VerticalAlignment="Center">
                                            <Grid.RowDefinitions>
                                                <RowDefinition  />
                                                <RowDefinition  />
                                            </Grid.RowDefinitions>
                                            <TextBlock Grid.Row="0"  x:Name="Name" Text="{Binding Edit.DataContext.RowValue.Name}" FontFamily="Verdana" FontSize="12" />
                                            <TextBlock Grid.Row="1"  x:Name="Status" Text="{Binding Edit.DataContext.RowValue.Status}" Foreground="Gray" />
                                        </Grid>
                                    </Grid>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </AgDataGrid:AgDataGridColumn.DisplayTemplate>
                </AgDataGrid:AgDataGridColumn>
            </AgDataGrid:AgDataGrid.Columns>
        </Touch:AgTouchGrid>
</Grid>

Note: Because of the architecture change, the DataContext of the Column.DisplayTemplate is different then in the Free AgDataGrid. We must use the above notation to access data object properties from within the DisplayTemplate:

Here, I am binding to an IList<Contact>

public class Contact {        
        public Guid Id {
            get;
            set;
        }
        public string[] ExtensionList {
            get;
            set;
        }
        public int SortOrder {
            get;
        }
        public string Status {
            get;
        }
        public string Icon {
            get;
        }
        public string Name {
            get;
            set;
        }
 }

so I must use {Binding Edit.DataContext.RowValue.MyPropertyName} to get to MyPropertyName. 

Mouse Events and Touch Scrolling

Idea is simple,

  • Override OnMouseLeftButtonDown, OnMouseLeftButtonUp and OnMouseMove events and calculate the distance traveled.
  • Convert the distance traveled into scroll steps and send this delta to the VerticalScrollbarElement.
  • Optionally, update VerticalScrollbarElement using a storyboard for that slick slide effect.

private bool _touchScrolling = false; private double _touchStart = 0; protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if(this.VerticalScrollController != null && !_touchScrolling && e.OriginalSource != _pageFooter) { OnTouchStart(e); } else { base.OnMouseLeftButtonDown(e); } } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); OnTouchFinish(e); } private void OnTouchStart(MouseButtonEventArgs e) { _touchScrolling = true; _touchStart = e.GetPosition(this).Y; this.CaptureMouse(); this.DataController.SelectionController.Clear(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if(this.VerticalScrollController != null && _touchScrolling) { double delta = Math.Ceiling((e.GetPosition(this).Y - _touchStart) / 20); if(Math.Abs(delta) > 2) { this.Cursor = Cursors.Hand; if(Math.Sign(delta) > 0) { AnimatedScroll(+Math.Abs(delta)); } else if(Math.Sign(delta) < 0) { AnimatedScroll(-Math.Abs(delta)); } _touchStart = e.GetPosition(this).Y; } } } private void OnTouchFinish(MouseButtonEventArgs e) { if(this.VerticalScrollController != null && _touchScrolling) { _touchScrolling = false; ReleaseMouseCapture(); double delta = Math.Ceiling((e.GetPosition(this).Y - _touchStart) / (this.ActualHeight / this.VisibleRowCount)); if(Math.Abs(delta) > 0) { if(Math.Sign(delta) > 0) { AnimatedScroll(+Math.Abs(delta)); } else if(Math.Sign(delta) < 0) { AnimatedScroll(-Math.Abs(delta)); } } else { base.OnMouseLeftButtonDown(e); base.OnMouseLeftButtonUp(e);

// Raise the mouse click event here } } }

And the AnimatedScroll:

public void AnimatedScroll(double delta) {
    Storyboard storyBoard = GetTemplateChild("TouchStory") as Storyboard;
    if(storyBoard != null) {
        (storyBoard.Children[0] as DoubleAnimation).From = VerticalScrollController.Value;
        (storyBoard.Children[0] as DoubleAnimation).To = VerticalScrollController.Value + delta;
        (storyBoard.Children[0] as DoubleAnimation).By = 1;
        storyBoard.Begin();
    }
}

  

That’s it, press and move your mouse up and down on the Grid’s surface to see the effect. Or if you have a touch screen (wink wink) use your fingers :)

Download the Complete Source Code:

Includes:

  • Lightweight connection-oriented request-response library
  • WinForms Client for presence exchange
  • Presence Server
  • TAPI Server (If you have TAPI drivers on your machine and a TAPI enabled device, you will have more fun with this then anyone else :))
  • Simple Policy Server for Silverlight

 

Next Steps

The included source code is too big to cover in one post. So please follow alone as I continue to build this cool little app and as result, I will show you lots and lots* of goodies from DXperience 2009 vol1.

 

Cheers

Azret

Published Mar 22 2009, 11:07 PM by
Bookmark and Share

Comments

Chris Walsh [DX-Squad]

Love the post Azret!!!  Good to see you back!

March 23, 2009 6:48 AM

Ferraro

Nice !!! Definitely!

In fact, I posted a similar request a few times ago, but for the XtraGrid component.

Take a look at the Q180833 question (www.devexpress.com/.../Q180833.aspx) and take 30 seconds to watch the video of what we could have with a devExpress grid (or a caroussel, ...)

As you said, more and more desktop computers have touch devices. Silverlight is of course a great place to start touch applications, but the desktop is also here.

It may be very difficult to implement in WinForms and I could understand that DevEx doesn't want to invest much in this technology anymore ... but, IMHO, you should definitely do this in WPF.

Ray or Julian, I would like to know you ropinion about this ...

Julien

March 23, 2009 7:11 AM

Peter Thorpe

I really liked this post, I think the biggest challenge with touch is screen real estate for things that have to be able to be touched, especially with most devices using it have small screens anyway.

March 23, 2009 8:08 AM

Azret Botash (DevExpress)

Julien,

> It may be very difficult to implement in WinForms and

Difficult or not, it is definitely something we will investigate on all fronts (R&D time, Value Proposition etc...). Personally, I like the idea and will definitely fight for it. :)

> I could understand that DevEx doesn't want to invest much

> in this technology anymore

This of course is entirely wrong assumption I am afraid. Our WinForms line is the best on the market and we will continue to keep it that way...

This is not something we will give up!!! :)

--

Azret

March 23, 2009 2:11 PM

Azret Botash (DevExpress)

Thanks Chris...

March 23, 2009 2:12 PM

Azret Botash (DevExpress)

Peter:

> I think the biggest challenge with touch is screen real estate > for things that have to be able to be touched, especially with > most devices using it have small screens anyway.

Exactly, this where touch-scrolling and touch-zoom-typing (i just made that on up) help a lot..

--

Azret

March 23, 2009 2:33 PM

elpipo

For winforms development, the only things devexpress is missing are a zip component and a sweet excel library.

March 23, 2009 2:45 PM

Ferraro

Hi all,

Azret, I know that you won't let WinForms down. What I was trying to say is that it may be easier to achieve this in the WPF version of the grid.

The answer to my question (Q180833) made me think that it won't be implemented (the idea was kind of rejected at that time). And I will have to find a solution by myself in the application I'm currently creating. I don't think we'll get this in 9.1 or 9.2 ...

So I can wait and use it in the next version of my application that will be in WPF (in 2 years I think)

Anyway, is there a issue I can track about this ?

Julien

March 24, 2009 1:51 AM

Joie Tagum

Good stuff, Azret..

March 25, 2009 4:08 PM

Ajay Patel

Interesting article.

Shame Silverlight lacks the multi-touch gestures of the real iPhone.

March 26, 2009 12:55 PM
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners