Blogs

The One With

March 2009 - Posts

  • Twitter Account

         

    So I have resisted the twitter for some time now. Not because it isn’t cool. Because it is. Or as someone at MIX’09 last week said to me.

    “Like, it’s like totally cool man, like dude why are you not on it?!?” Twitter__What_are_you_doing_-3.jpg

    Well, I had an account, but I have no idea what password I selected and or what email address I registered it to. Lame I know.

    Julian posted that some of us from DevExpress are already there. You can read it here.

    and I have just created an account, so join us.

    Azret Botash @dxlight “Did you know there is a DevExpress.Utils.Compress.Compressor?”

     

    See you on twitter

    Azret

  • 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

More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.