in
Forums
Blogs
Files
Devexpress.Com
ClientCenter
Support Center
DevExpress Channel

The One With

July 2008 - Posts

  • Silverlight : More than one default template in generic.xaml?

    I was working on something recently that required more than one default template. In other words the default template to be used by a control was determined by another property.

    For example, suppose you have a Control called Shape

    public enum ShapeType {
    Red,
    Black,
    }

    public class Shape : Control {
    public Shape() {
    this.DefaultStyleKey = typeof(Shape);
    }

    public ShapeType Type {
    get { return (ShapeType)GetValue(TypeProperty); }
    set { SetValue(TypeProperty, value); }
    }
    }

    with a default style:

    <Style TargetType="local:Shape">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:Shape">
    <Rectangle Fill="Red"></Rectangle>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    and you want the template to change based on the ShapeType. This is simple to do just by looking up the template from the resource dictionary.

    First thing to do is to refactor the generic.xaml a little bit so that different control templates are available as resource items.

    <ControlTemplate TargetType="local:Shape" x:Key="Shape:Black">
    <Rectangle Fill="Black"></Rectangle>
    </ControlTemplate>

    <ControlTemplate TargetType="local:Shape" x:Key="Shape:Red">
    <Rectangle Fill="Red"></Rectangle>
    </ControlTemplate>

    <Style TargetType="local:Shape">
    <Setter Property="Template" Value="{StaticResource Shape:Red}"></Setter>
    </Style>

    The initial value for the Template is the same as the default value for the ShapeType enum.

    Then, when the value of the Type changes, we will look up the control template:

    public static readonly DependencyProperty TypeProperty =
    DependencyProperty.Register("Type", typeof(ShapeType), typeof(Shape), new PropertyMetadata(new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    Shape s = d as Shape;
    if (s != null) {
    s.OnShapeTypeChanged(true);
    }

    })));

     

    private string GetTemplateName(ShapeType type) {
    switch (type) {
    case ShapeType.Red:
    return "Shape:Red";
    case ShapeType.Black:
    return "Shape:Black";
    default:
    return null;
    }
    }
    protected void OnShapeTypeChanged(bool applyTemplate) {
    ResourceDictionary genericXaml = ResourceManager.GetResourceDictionary(null);
    if (genericXaml == null) {
    this.Template = null;
    return;
    }
    string templateName = GetTemplateName(this.Type);
    if (string.IsNullOrEmpty(templateName)) {
    this.Template = null;
    return;
    }
    this.Template = genericXaml[templateName] as ControlTemplate;
    if (applyTemplate) {
    this.ApplyTemplate();
    }
    }

    An additional helper class ResourceManager:

    internal static class ResourceManager {
    private static Dictionary<string, ResourceDictionary> _resourceDictionaryCache =
    new Dictionary<string, ResourceDictionary>();

    /// <summary>
    /// GetResourceDictionary
    /// </summary>
    public static ResourceDictionary GetResourceDictionary(string name) {
    var resourceName = name;
    if (string.IsNullOrEmpty(resourceName)) {
    resourceName = "generic.xaml";
    }
    ResourceDictionary retVal = null;
    if (_resourceDictionaryCache.TryGetValue(resourceName, out retVal)) {
    return retVal;
    }
    System.Reflection.Assembly assembly = typeof(ResourceManager).Assembly;
    string baseName = string.Format("{0}.g",
    assembly.FullName.Split(new char[] { ',' })[0]);
    string fullName = string.Format("{0}.resources", baseName);
    foreach (string s in assembly.GetManifestResourceNames()) {
    if (s == fullName) {
    System.Resources.ResourceManager manager =
    new System.Resources.ResourceManager(baseName, assembly);
    using (System.IO.Stream stream = manager.GetStream(resourceName)) {
    using (System.IO.StreamReader reader = new System.IO.StreamReader(stream)) {
    retVal = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as ResourceDictionary;
    if (retVal != null) {
    _resourceDictionaryCache.Add(resourceName, retVal);
    return retVal;
    }
    }
    }
    break;
    }
    }
    return null;
    }
    }

    And that's it.

     

    Cheers,

    Azret

  • Silverlight Data Grid: Master Detail View by using the RowPreview template.

    The cool thing about the Preview area of the data row, is that you can place anything there. For example you can place a simple text or an image and data bind it to the current DataContext, which is in fact your data row object. So if you have another AgDataGrid inside your preview template, you can bind it's DataSource to the current DataContext.

    So if, our master grid is bound to a collection of customer objects and each customer has a list of orders.
     
    public class Order {
        public int OrderId { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
    }
    
    public class Customer {
        public string Name { get; set; }
        public string Country { get; set; }
        private List<Order> _orders = new List<Order>();
        public List<Order> Orders { get { return this._orders; } }
    }
     
    List<Customer> GetCustomers() {
        Customer c;
        List<Customer> list = new List<Customer>();
    
        c = new Customer() { Name = "Joe", Country = "USA" };
        c.Orders.Add(new Order() { OrderId = 0, Description = "Sterio", Price=2000 });
        c.Orders.Add(new Order() { OrderId = 1, Description = "XP Radio", Price=1000 });
        c.Orders.Add(new Order() { OrderId = 2, Description = "Cell Phone", Price=200 });
        list.Add(c);
    
        c = new Customer() { Name = "Bill", Country = "USA" };
        c.Orders.Add(new Order() { OrderId = 3, Description = "Sterio", Price=2000 });
        c.Orders.Add(new Order() { OrderId = 4, Description = "Head Set", Price=100 });
        c.Orders.Add(new Order() { OrderId = 5, Description = "Keyboard", Price=100 });
        list.Add(c);
        return list;
    }
     
    then the Customer.Orders could be bound to a DataSource of the grid that is inside the preview row like so:
     
    <DevExpress:AgDataGrid  
        x:Name="dataGrid" PreviewVisibility="ForAllRows" 
        ColumnsAutoWidth="True" ShowGroupPanel="Visible">
                
        <DevExpress:AgDataGrid.Columns>
                    <DevExpress:AgDataGridColumn FieldName="Name"></DevExpress:AgDataGridColumn>
                    <DevExpress:AgDataGridColumn FieldName="Country"></DevExpress:AgDataGridColumn>
        </DevExpress:AgDataGrid.Columns>
        <DevExpress:AgDataGrid.PreviewTemplate>
                    <DataTemplate>
                        <Grid  Height="180">
                            <DevExpress:AgDataGrid AutoGenerateColumns="True"
                                    DataSource="{Binding Orders}" ColumnsAutoWidth="True" Margin="5">
                            </DevExpress:AgDataGrid>
                        </Grid>
                    </DataTemplate>
        </DevExpress:AgDataGrid.PreviewTemplate>
     </DevExpress:AgDataGrid>
     
     
    Cheers,
    Azret
     
  • Silverlight DataGrid: Unbound Mode

    Antoine Habert has written a very cool blog post on how to simulate the Unbound mode with AgDataGrid with a help of a Dictionary.

    http://devinfra-us.blogspot.com/2008/07/agdatagrid-and-silverlight-datagrid-how.html

     

    --

    Azret

  • Silverlight Data Grid - Creating an IM/Chat Server and Client using Sockets

    One of the things I love about Silverlight 2 Beta 2+, is that cross domain networking is now possible and all you have to do is provide a policy file on your target server that will dictate the security permissions. For your HTTP traffic it is just a matter of dropping a clientaccesspolicy.xml file at the root of your domain and for raw sockets you provide this file via a special port 943. Having support for cross-domain networking means that real time clients are now easy to develop. In this article we will use Sockets to build a simple chat application.

    The Server

    Before any Socket connection could be made by a Silverlight client, the runtime will try to connect to a special port 943 to get the cross domain policy information. It will send a <policy-file-request/> string for the request so we will need to reply back with a valid cross-domain policy xml.

    In our case we will send back a simple "allow all" configuration.

    <?xml version="1.0" encoding ="utf-8"?>
    <access-policy>
        <cross-domain-access>
            <policy>
                <allow-from>
                    <domain uri="*" />
                </allow-from>
                <grant-to>
                    <socket-resource port="4510" protocol="tcp" />
                </grant-to>
            </policy>
        </cross-domain-access>
    </access-policy>
     
    Policy Server implementation:
     
    public class PolicyServer : SocketServer {
        public static readonly byte[] PolicyBuffer = System.IO.File.ReadAllBytes("clientaccesspolicy.xml");
    
        public PolicyServer()
            : base(943) {
        }
    
        protected override void OnAccept(Socket client) {
            PostReceive(client);
        }
    
        protected override void OnReceive(Socket client, byte[] receivedBytes, int count) {
            string request = Encoding.UTF8.GetString(receivedBytes);
            if (!string.IsNullOrEmpty(request) && request.StartsWith("<policy-file-request/>")) {
                client.Send(PolicyBuffer);
            }
            client.Close();
        }
    }

    At a minimum, there are 3 commands that our server will need to handle.

    • Login
    • RefreshUsers
    • Message

    I will use XML to send the commands back and forth since it is easy to serialize them using the XML Serialization facilities provided by the .NET framework.

    public enum Operation {
        Unknown,
        Login,
        Message,
        RefreshUsers,
    }
    
    [XmlRoot("Packet")]
    public class Packet {
        public Packet() {
            this.Children = new List<User>();
        }
        public Guid UserId { get; set; }
        public string UserName { get; set; }
        public Operation Operation { get; set; }
        public string Payload { get; set; }
        public List<User> Children { get; set; }
    }
    • UserId - A unique ID for a specific user.
    • Children - List of User objects for the RefreshUsers command.
    • Payload - Text for the Message command.

    A helper class to convert our Packet object to and from XML:

    public static class Protocol {
    
        public static byte[] GetPacket(Packet packet) {
            XmlSerializer serializer = new XmlSerializer(typeof(Packet));
            using (MemoryStream stream = new MemoryStream()) {
                serializer.Serialize(stream, packet);
                return stream.GetBuffer();
            }
        }
    
        public static Packet GetPacket(byte[] buffer, int count) {
            XmlSerializer serializer = new XmlSerializer(typeof(Packet));
            using (MemoryStream stream = new MemoryStream()) {
                stream.Write(buffer, 0, count);
                stream.Position = 0;
                return serializer.Deserialize(stream) as Packet;
            }
        }
    }

    Handling the requests:

    protected override void OnReceive(Socket client, byte[] receivedBytes, int count) {
        try {
            DXTalk.Packet packet = DXTalk.Protocol.GetPacket(receivedBytes, count);
            switch (packet.Operation) {
                case Operation.Login:
                    UpdateUserInfo(client, packet);
                    BroadcaseUserList();
                    break;
                case Operation.RefreshUsers:
                    BroadcaseUserList();
                    break;
                case Operation.Message:
                    ForwardMessage(packet);
                    break;
            }
        } finally {
            PostReceive(client);
        }
    }
    

    Now we just start up the 2 listening sockets: one for our policy xml and another for handling the protocol requests and we are done with the server side. There is one restriction on the port usage however, it must in 4502-4534 range. We will use 4510 for our talk server.

     

    Establishing the connection

    The Socket API is a little bit different in Silverlight then in the full .NET framework. It only supports asynchronous calls and all the calls use a special SocketAsyncEventArgs class for completion events and state.

    Connection to our chat server:

    void Login(string userName) {
        if (_socket == null) {
            _socket = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream, ProtocolType.Tcp);
    
            SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
            sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnected);
            sendArgs.RemoteEndPoint = new DnsEndPoint("localhost", 4510);
    
            _loginId = Guid.NewGuid();
            _loginName = userName;
            _socket.ConnectAsync(sendArgs);
        }
    }
    
    void OnConnected(object sender, SocketAsyncEventArgs e) {
        SocketError error = e.SocketError;
        if (error == SocketError.Success) {
            _socket.BeginReceive(new EventHandler<SocketAsyncEventArgs>(OnReceiveComplete));
            _socket.BeginSend(new Packet() {
                UserId = this._loginId,
                UserName = this._loginName,
                Operation = Operation.Login,
            }, null);
        }
    }

    BeginReceive and BeginSend?! Don' worry, those are just extension methods on the Socket class that call ReceiveAsync and SendAsync :)

    public static class SocketExtentions {
        
        #if SILVERLIGHT
    
        public static void BeginSend(this Socket socket, 
            Packet packet,
            EventHandler<SocketAsyncEventArgs> completionEvent) {            
            SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
            sendArgs.Completed += completionEvent;
            byte[] buffer = Protocol.GetPacket(packet);
            sendArgs.SetBuffer(buffer, 0, buffer.Length);
            socket.SendAsync(sendArgs);
        }
        
        public static void BeginReceive(this Socket socket,
            EventHandler<SocketAsyncEventArgs> completionEvent) {
            SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
            byte[] buffer = new byte[1024];
            sendArgs.SetBuffer(buffer, 0, buffer.Length);
            sendArgs.Completed += completionEvent;
            socket.ReceiveAsync(sendArgs);
        }
        
        #endif
    
    }

    When we receive the reply from the server, OnReceiveComplete event is invoked:

    void OnReceiveComplete(object sender, SocketAsyncEventArgs e) {
        SocketError error = e.SocketError;
        if (error == SocketError.Success) {
            try {
                Packet packet = Protocol.GetPacket(e.Buffer, e.BytesTransferred);
                if (packet.Operation == Operation.RefreshUsers) {
                    this.Dispatcher.BeginInvoke(delegate() {
                        Users.DataSource = packet.Children;
                        Users.ExpandAll();
                    });
                } else if (packet.Operation == Operation.Message) {
                    this.Dispatcher.BeginInvoke(delegate() {
                        ShowChatWindow(packet.Children[0], packet.Payload);
                    });
                }
            } catch {
            }
        }
        _socket.BeginReceive(new EventHandler<SocketAsyncEventArgs>(OnReceiveComplete));
    }
    

    Two things to note here. 1: "Users" is an AgDataGrid control:

    <TalkControl:AgDataGrid Grid.Row="1" x:Name="Users"
                           ShowGroupPanel="Visible"
                           ColumnsAutoWidth="True"
                           AllowEditing="False">
        <TalkControl:AgDataGrid.Columns>
            <DevExpress:AgDataGridTextColumn HeaderContent="User Name" FieldName="UserName"/>
            <DevExpress:AgDataGridTextColumn HeaderContent="User ID" FieldName="UserId"/>
        </TalkControl:AgDataGrid.Columns>
    </TalkControl:AgDataGrid>        

    And 2, we must synchronize access to the main thread via the Dispatcher object.

     

    Preparing the UI

    Once the client is connected, a conversation could be started either by receiving a message or by double clicking the a record in the data grid. In both cases, the same ShowChatWindow function is called:

    private void ShowChatWindow(User peer, string message) {
        ChatWindow window = ChatWindow.FindWindow(peer.UserId);
        if (window == null) {
            Popup popup = new Popup();
            window = new ChatWindow(popup,
                new User() { UserId = _loginId, UserName = _loginName },
                peer, _socket);
            window.Title.Text = peer.UserName;
            popup.VerticalOffset = 100;
            popup.HorizontalOffset = 100;
            popup.Child = window;
            popup.IsOpen = true;
        }
        if (!string.IsNullOrEmpty(message)) {
            window.AddLine(message, Colors.Red, peer);
        }
        window.parent.IsOpen = true;
        window.Focus();
    }

    There is no support for double-click in Silverlight so we will have to fake it using a special MouseHelper class that I copied from the AgDataGrid source code.

    public delegate void DblClickEvent(object sender, MouseEventArgs e);
    
    public class AgDataGrid : DevExpress.Windows.Controls.AgDataGrid {
        public void Refresh() {
            base.DataController.DoRefresh();
        }
    
        private static class MouseHelper {
            static int Timeout = 500;
            static bool clicked = false;
            static Point position;
            public static bool IsDoubleClick(MouseButtonEventArgs e) {
                if (clicked) {
                    clicked = false;
                    return position.Equals(e.GetPosition(null));
                }
                clicked = true;
                position = e.GetPosition(null);
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread);
                Thread thread = new Thread(threadStart);
                thread.Start();
                return false;
            }
            private static void ResetThread(object state) {
                Thread.Sleep(Timeout);
                clicked = false;
            }
        }
    
        public event DblClickEvent DblClick;
    
        public override void OnApplyTemplate() {
            base.OnApplyTemplate();
            this.Surface.MouseLeftButtonUp += new MouseButtonEventHandler(Surface_MouseLeftButtonUp);
        }
    
        void Surface_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
            if (MouseHelper.IsDoubleClick(e)) {
                if (DblClick != null) {
                    DblClick(sender, e);
                }
                e.Handled = true;
            }
        }
    }

    And there no build-in windows or dialogs in Silverlight 2. But there is a System.Windows.Controls.Primitives.Popup container and it's all we need to create a conversation window. A ChatWindow is a simple UserControl and in the ChatWindow.xaml I have one special element TitleBar.

    <Grid Background="Black" Grid.Row="0" x:Name="TitleBar">
        <Border BorderBrush="White" BorderThickness="0.3"></Border>
        <TextBlock  IsHitTestVisible="False" x:Name="Title" Text="Title" VerticalAlignment="Center" HorizontalAlignment="Left"
                   FontFamily="Tahoma" Foreground="White" FontSize="14" 
                    FontWeight="Bold"
                    Margin="8,0,0,0"></TextBlock>
        <Button Width="32" Margin="4" HorizontalAlignment="Right" Content="X"
            Click="CloseButtonClick"></Button>
    </Grid>

    It is used to display the NC (non-client) area of the window with the close button and it is also used to control the dragging of the window. We must have cool UI :). Implementing the window dragging is simple:

    public ChatWindow()
        : base() {
        InitializeComponent();
        this.MouseLeftButtonDown += new MouseButtonEventHandler(ChatWindow_MouseLeftButtonDown);
        this.MouseLeftButtonUp += new MouseButtonEventHandler(ChatWindow_MouseLeftButtonUp);
        this.MouseMove += new MouseEventHandler(ChatWindow_MouseMove);
    }
    
    bool _hasNCHitTest = false;
    Point _initalNCHitTest = new Point();
    
    void ChatWindow_MouseMove(object sender, MouseEventArgs e) {
        if (e.Handled == true)
            return; 
        if (_hasNCHitTest && parent != null) {
            Point hitTest;
            hitTest = e.GetPosition(Title);
    
            Point delta = new Point(
                hitTest.X - _initalNCHitTest.X,
                hitTest.Y - _initalNCHitTest.Y);
    
            this.parent.HorizontalOffset += delta.X;
            this.parent.VerticalOffset += delta.Y;
            
            e.Handled = true;
        }
    }
    
    void ChatWindow_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
        if (e.Handled == true)
            return; 
        if (_hasNCHitTest) {
            _hasNCHitTest = false;
            TitleBar.ReleaseMouseCapture();
            e.Handled = true;
        }
    }
    
    void ChatWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
        if (e.Handled == true)
            return; 
        if (e.Source == TitleBar && parent != null) {
            _hasNCHitTest = true;                
            _initalNCHitTest = e.GetPosition(TitleBar);
            TitleBar.CaptureMouse();
            e.Handled = true;
        }
    }
    

    For conversation area, I use the AgDataGrid as well with a custom preview template. Where the grid is called ChatBox and is bound to a list of Message objects.

    <TalkConrol:AgDataGrid Grid.Row="1" x:Name="ChatBox" AllowEditing="False" ShowColumnHeaders="Collapsed" ShowHorizontalLines="False" ShowVerticalLines="False" PreviewVisibility="ForAllRows" ColumnsAutoWidth="True"> <TalkConrol:AgDataGrid.DataRowTemplate> <ControlTemplate TargetType="DevExpress:AgDataGridRow"> <Grid Name="RootElement"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <DevExpressInternal:AgLineStackPanel x:Name="CellsPresenterElement" Opacity="0" Height="1" /> <ContentControl x:Name="PreviewPresenterElement" Height="0"/> </Grid> </ControlTemplate> </TalkConrol:AgDataGrid.DataRowTemplate> <TalkConrol:AgDataGrid.PreviewTemplate> <DataTemplate> <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="6,0,0,0"> <TextBlock Text="{Binding Timestamp}" TextWrapping="NoWrap" Margin="2,0,0,0" Width="300" Foreground="Gray" FontFamily="lucida grande,tahoma,verdana,arial,sans-serif" FontSize="11"></TextBlock> <TextBlock Text="{Binding Line}" TextWrapping="Wrap" Margin="16,1,0,0" Width="300" Foreground="{Binding Color}" FontFamily="lucida grande,tahoma,verdana,arial,sans-serif" FontSize="11"></TextBlock> </StackPanel> </DataTemplate> </TalkConrol:AgDataGrid.PreviewTemplate> </TalkConrol:AgDataGrid>

    public class Message {
        public string Timestamp { get; set; }
        public string Line { get; set; }
        public Brush Color { get; set; }
    }
    
    internal void AddLine(string message, Color color, User peer) {
        var list = this.ChatBox.DataSource as List<Message>;
        list.Add(new Message() {
            Line = string.Format("{0}: {1}", peer.UserName, message),
            Timestamp = DateTime.Now.ToString(), Color = new SolidColorBrush(color)
        });
        ChatBox.Refresh();
    }

    One last thing, sending the actual message and we are done:

    private void Send_Click(object sender, RoutedEventArgs e) {
        if (!string.IsNullOrEmpty(MessageBox.Text)) {
            Packet packet = new Packet() {
                UserId = user.UserId,
                UserName = user.UserName,
                Operation = Operation.Message,
                Payload = MessageBox.Text,
                Children = new List<User>() { new User() { UserId = peer.UserId, UserName = peer.UserName } },
            };
            AddLine(MessageBox.Text, Colors.Black, this.user);
            socket.BeginSend(packet, null);
        }
    }

     

    image 

    Download Source Code

     

    Happy chatting :)

    Azret

  • Silverlight DataGrid - How to perform custom summary calculations within the grid control

    There are 5 build-in aggregate functions that our Silverlight DataGrid supports (Max, Min, Avg, Sum and Count), and an ability to define custom ones.

     

     

    Using build-in Summary Items

    <DevExpress:AgDataGrid>

       <DevExpress:AgDataGrid.Totals>

           <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Average"/>

           <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Max"/>

           <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Min"/>

           <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Count"/>

           <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Sum"/>  

       </DevExpress:AgDataGrid.Totals>

    </DevExpress>

     

    Calculating Custom Summary

    To calculate custom summary, we need to add an AgDataGridSummaryItem with SummaryType of "Custom" and use the AgDataGrid.CustomSummary event.

    <DevExpress:AgDataGrid CustomSummary="myGrid_CustomSummary">

       <DevExpress:AgDataGrid.Totals>

             <DevExpress:AgDataGridSummaryItem FieldName="Points" SummaryType="Custom"

                                              Title="Sum Of Even Rows"/>

       </DevExpress:AgDataGrid.Totals>

    </DevExpress>

    private void myGrid_CustomSummary(object sender, DevExpress.Windows.Data.CustomSummaryEventArgs e) {              

                      if (e.SummaryProcess == CustomSummaryProcess.Start) {

                            int rowHandle = 0;                       

                            int sumOfEvenRows = 0;

                            int i = 1;

                            while (myGrid.IsValidRowHandle(rowHandle)) {

                                  Item item = (Item)myGrid.GetDataRow(rowHandle);

                                  if ((i % 2) == 0){

                                sumOfEvenRows += item.Points;

                                  }

                                  rowHandle++;

                                  i++;

                            }

                            e.TotalValue = sumOfEvenRows;

                            e.TotalValueReady = true;

                   }

    }

    public class MyRowItem {

          public int Points { get; set; }

    }

    All we are doing here is going through every row in our data source and summing up the Points for even rows.

     

    Previous: Silverlight DataGrid - Where are my Facebook friends?

     

    Cheers,

    Azret

  • Silverlight Control Builder Contest

    If you have not entered the Silverlight Control Builder Contest yet you should hurry up, only 31 Days remaining, and Developer Express will be offering some cool prizes to the winners:

     

    1st Place

    And a $500 gift certificate to NewEgg.com

    2nd Place