none
Need some help with custom control that can handle datagrid with undefined number of columns.

    Question

  • I’m close to a custom control that can mimic a datagrid with an undefined amount of columns. I’m struggling with some databinding stuff, for which I hope you can help.

    I can illustrate my intention with following simple example: I have a table with Rooms (which have some properties like Name and location) and a table Reservation, which has a foreign key towards Room, and has 2 fields: State and Slot. Slot can be seen a date if you want.

    Now, I want a visualization of these tables where the rows are the room names and the columns provide the state information of the reservations, as in following screenshot.

    So, not a regular table, but rather kind of “tilt” structure.

    With some help from google, I succeeded to implement the above in plain Silverlight and I’m trying to implement it now as custom control in LightSwitch. This is the screen:

    T

    I’m doing this based on the following xaml

    <UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  x:Class="ReservationControl.RoomWithView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"
        xmlns:local="clr-namespace:ReservationControl"   
        d:DesignHeight="300" d:DesignWidth="400">
        
        <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Screen}" >
           
        <sdk:DataGrid AutoGenerateColumns="False" 
                          ItemsSource="{Binding Rooms}">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn 
                                 Binding="{Binding Name}"  
                                 Header="Name"/>
    
                <sdk:DataGridTemplateColumn Width="*">
                    <sdk:DataGridTemplateColumn.HeaderStyle>
                        <Style 
                              TargetType="primitives:DataGridColumnHeader">
                            <Setter 
                                  Property="HorizontalContentAlignment" 
                                  Value="Stretch" />
                            <Setter 
                                  Property="VerticalContentAlignment"  
                                  Value="Stretch" />
                            <Setter Property="Margin" 
                                             Value="0" />
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <ItemsControl  
                              ItemsSource="{Binding ElementName=LayoutRoot,Path= DataContext.DateSlots}">
                                            <ItemsControl.ItemsPanel>
                                                <ItemsPanelTemplate>
                                                    <StackPanel 
                                             Orientation="Horizontal">
                                                    </StackPanel>
                                                </ItemsPanelTemplate>
                                            </ItemsControl.ItemsPanel>
                                            <ItemsControl.ItemTemplate>
                                                <DataTemplate>
                                                    <Border  Width="70" >
                                                        <TextBlock Text="{Binding Slot}" 
                                                  TextAlignment="Center"/>
                                                    </Border>
                                                </DataTemplate>
                                            </ItemsControl.ItemTemplate>
                                        </ItemsControl>
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </sdk:DataGridTemplateColumn.HeaderStyle>
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl  
                                    ItemsSource="{Binding Path=Reservations}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate >
                                        <StackPanel Orientation="Horizontal"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Border Width="70">
                                            <TextBlock Text="{Binding Path=State}" 
                                                TextAlignment="Center"/>
                                        </Border>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
        </Grid>
    </UserControl>

    In case you want to give it a try, I prepared a zip with the solution and the custom control. (it contains also a button to provide some sample data.  Thanks !!!

    http://blog.pragmaswitch.com/wp-content/uploads/2012/03/RoomReservation.zip


    paul van bladel




    Monday, March 05, 2012 9:27 PM

Answers

  • You picked up a complicated solution, so you might spend some extra time to dig things out.

    Your binding to Rooms collection is fine.  Just like you noticed, the trouble is to make the binding inside stackpanel to work.  You cannot make it to bind to the Reservations collection defined inside the screen.

    The reason is straight, there is only one Reservations collection defined in the screen, but you are showing a stack panel for each room, which means each room has its own collection to bind to the screen.  On that level, it is not possible that you can bind to a collection property you defined in the light switch designer.

    Fortunately, you don't need this top level Reservations collection, which is only useful in the standard master-detail UI, where it is always refreshed, when the current selected room is changed.  If you check each room entity, you should find a Reservations navitation property too.  You can use this navigation property to access the reservations for each room directly in your code.  That collection is independent to the screen level collection.

    Now, if you try to bind to that collection to your UI, it will fail.  Because the navigation property can only be accessed in the thread running user code, it cannot be accessed by the UI code (which runs in the main UI thread) directly.  With the intellisense, you can fine a room object also exposes ReservationsQuery property, which is a query object.

    You can write a converter to convert this query object to an collection, which can be bound to your stack panel. With the converter, you then can bind your control to the query object directly.

    Inside the converter, you can find the query object supports IExecutable<IEnumerable<Reservation>> interface. This interface has a method ExecuteAsync, which allows you to execute the query asynchoronously. This can be called in the UI thread. (Don't try to call Execute).  Before that, you need create an empty observable collection, and also hook up ExecuteCompleted event of the query object.  The converter will return the empty collection.

    When the execution is done, you can get the result from it (from Result property), and then fill the observable collection you created earlier.  Your UI will get notification, and fill the stack panel.

    Hope that gives you some idea.

    Good Luck

    Lifeng


    LLF

    Saturday, March 10, 2012 6:44 PM
  • Sorry for the delay. I completed today the sample project.

    So, it's a custom control for visualizing room reservation in a kind of "plan board" way rather than a classic datagrid.

    You can find the details + a sample project here:

    http://blog.pragmaswitch.com/?p=318


    paul van bladel

    • Marked as answer by Otomii Lu Friday, June 01, 2012 7:57 AM
    Thursday, May 31, 2012 1:27 PM
  • No, the navigation query you get from the entity should implement IExecutable as well.  That means the IDataServiceQueryable<Reservation> object you get from the entity is an IExecutable object, and you should be able to execute it in async way, and hook up events to get the result.   I am not sure why your type convertion doesn't work.  Can you try it again to convert it to IExecutableWithResult? 

    The IDataServiceQueryable doesn't inherit from the IExecutable, but the implementation should support both.


    -L

    Wednesday, March 14, 2012 6:06 PM
  • Lifeng,

    I think we found a final solution. If you know a way to get rid of the intermediate list IEnumerable, drop me a line :)

    Many thanks for your help !

     public class ReservationValueConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                ObservableCollection<Reservation> reservationObservableCollection = new ObservableCollection<Reservation>();
                List<Reservation> list = new List<Reservation>();
                Room currentRoom = value as Room;
                currentRoom.Details.Dispatcher.BeginInvoke(() =>
                    {
                        var query = currentRoom.ReservationsQuery as IExecutableWithResult;
                        query.ExecuteCompleted += new EventHandler<ExecuteCompletedEventArgs>((s, e) =>
                        {
                            foreach (Reservation item in query.Result as IEnumerable<Reservation>)
                            {
                                list.Add(item);
                            }
                            Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() =>
                                {
                                    foreach (Reservation item in list)
                                    {
                                        reservationObservableCollection.Add(item);
                                    }
                                });
                        });
                        query.ExecuteAsync();
                    });
                return reservationObservableCollection;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return value;
            }
        }


    paul van bladel

    Thursday, March 15, 2012 7:00 AM

All replies

  • Hey Paul... I dont understand what you want exactly sorry.? can you explain more what you want to achieve? a UI design representing tables or what? sorry again.

    Like Yann Said : "If you found this post helpful, please "Vote as Helpful". If it actually answered your question, remember to "Mark as Answer". This will help people find the answers that they're looking for more quickly."

    Monday, March 05, 2012 10:39 PM
  • Sorry, I'll try to provide better info.

    Let's start with the simple data model:

    So, a simple parent child table structure: A Reservation must have a room, and a room can have many reservations.

    I want to visualize this in a way that in the rows we have the rooms and the columns are organised per slot. So we get a kind of TIME LINE representation. The point is that we get then of course an undefined amount of 'columns'.

    So, how am I doing this in Xaml. Basically, I only have 2 columns. The first column, which is a simple DataGridTextColumn, provides the label for the room and the second column is a DataGridTemplateColumn. the DataGridTemplateColumn has a header which is an horizontal stackpanel of Slot labels. Next, the DataGridTemplateColumn has as well a CellTemplate which is basically also an horizontal stackpanel with the Slots from the Room.Reservations.

    The binding of the first column is easy. the binding of the header of the DataGridTemplateColumn is working as well, I only have trouble with the binding of the inner cells inside the stackpanel.

    Feel free to download the solution I set up (  http://blog.pragmaswitch.com/wp-content/uploads/2012/03/RoomReservation.zip ), so you can easily try it out.  I think it would be great to get this working because it's a kind of pattern that has many use cases. 


    paul van bladel

    Tuesday, March 06, 2012 7:14 AM
  • The way to go is to use a ListBox (or ListView) control (not a DataGrid). In the ListView you can make any UI you want.

    Here's a tutorial http://weblogs.asp.net/scottgu/pages/silverlight-tutorial-part-5-using-the-listbox-and-databinding-to-display-list-data.aspx now you can format your UI as you like using ItemTemplate (you can even has another ListBox inside an ItemTemplate of another ListBox, like that you could achieve what you exactly want)

    Hope it's clear.


    Like Yann Said : "If you found this post helpful, please "Vote as Helpful". If it actually answered your question, remember to "Mark as Answer". This will help people find the answers that they're looking for more quickly."

    • Proposed as answer by Nadjib Bait Tuesday, March 06, 2012 11:30 AM
    Tuesday, March 06, 2012 11:30 AM
  • Hi,

    Thanks for the update.

    As a wrote in the initial post, I got this working in a plain silverlight project completely in the MVVM way of working. 

    Is it correct to presume that if the data binding is working in a plain silverlight project, that it is possible to get it working as well in a LightSwitch project?

    I did this based on the following article: 

    http://msmvps.com/blogs/deborahk/archive/2011/01/23/populating-a-datagrid-with-dynamic-columns-in-a-silverlight-application-using-mvvm.aspx


    paul van bladel

    Tuesday, March 06, 2012 12:19 PM
  • Oh right I see sorry for my last post.  I think it should be a path binding problem...

    Like Yann Said : "If you found this post helpful, please "Vote as Helpful". If it actually answered your question, remember to "Mark as Answer". This will help people find the answers that they're looking for more quickly."

    Tuesday, March 06, 2012 3:37 PM
  • No problem, I'm very happy you want to take a look. Feel free to download the sample project.


    paul van bladel

    Tuesday, March 06, 2012 3:42 PM
  • Hi, Paul,

      You certainly have an interesting project to work with.  I believe that creating a such custom control in the lightSwitch form is doable.  Depend on what you want to do, it can be simple or complicated.

     To make your DataGrid to work, you expect to have a DataObject in this shape:

      Root - DataSlots*

                Rooms*  -- Name

                                 Resevations*

    The LightSwitch collection does not looks like that, you have to create data objects like that by yourself, and bind the DataGrid to your objects.  I didn't read your sample application, but I am pretty sure that you have already created something like that, so it will work.

    To make that to work inside the LS screen, you have different choices.

    A simple way is to add your object to the screen directly.   When you write code to the screen, it is easily noticable that a screen is defined as a partial class in the project. You can add any SilverLight property to it, just don't name it to conflict with any screen property.  (I do not recommend to add all the properties this way, you don't get things like change tracking for a property added like that.)  So, you can add your object to your screen, and execute query and fill data to it by handing screen created event.  That is essentially a way to plug any silverLight stuff into a LS screen.  Your custom control can bind to the screen, and you can bind to any of your properties directly inside the control.

    If your application UI is more dynamic, for example, there is some searching UI to select rooms, and some date range to control to show only a specific range of data, you still can handle some data events to refresh your grid.  If pulling data is slow, you may notice it will block the other function of the screen for a while.

    A more dynamic way is to create a value converter to map lightSwitch collection to the shape of the structure you need in your custom control.  This can be very complicated, so unless it is absolutely necessary, we should not get into this for now.

    Hope that is helpful

    Lifeng


    LLF

    Thursday, March 08, 2012 2:02 AM
  • Hi Lifeng,

    thanks for the info. 

    My Viewmodel goes as follows:

    So I  basically want to bind 3 things (i skip for a while the header of the 2nd column, because that's working):

    1. My parent collection is Rooms which I bound directly to the outer grid (see the xaml above)

     <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Screen}" >
           
        <sdk:DataGrid AutoGenerateColumns="False" 
                          ItemsSource="{Binding Rooms}">

    2. Directly from the rooms collection I want the name field, which is used in the first column.

     <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn 
                                 Binding="{Binding Name}"  
                                 Header="Name"/>

    --> working perfectly.

    3. The Reservations navigation property inside Rooms: basically this involves 2 things

    a) binding the itemssource

    b) binding the state property inside the stackpanel.

    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ItemsControl  
                                    ItemsSource="{Binding Path=Reservations}">    --> ??????
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate >
                                        <StackPanel Orientation="Horizontal"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Border Width="70">
                                            <TextBlock Text="{Binding Path=State}" 
                                                TextAlignment="Center"/>
                                        </Border>

    so, If I would use a converter, on- which binding do I have to specify this converter. On the Room Collection binding, On the Reservations binding or on the State binding?

    I understand that if I just bind to a property in the partial class I lose all comfort that a visualCollection is bringing in, so that's not an option. So, I believe that implementing a converter is the only option. Can you please give some additional guidance on this?

    thanks

    paul.


    paul van bladel

    Thursday, March 08, 2012 10:35 AM
  • You picked up a complicated solution, so you might spend some extra time to dig things out.

    Your binding to Rooms collection is fine.  Just like you noticed, the trouble is to make the binding inside stackpanel to work.  You cannot make it to bind to the Reservations collection defined inside the screen.

    The reason is straight, there is only one Reservations collection defined in the screen, but you are showing a stack panel for each room, which means each room has its own collection to bind to the screen.  On that level, it is not possible that you can bind to a collection property you defined in the light switch designer.

    Fortunately, you don't need this top level Reservations collection, which is only useful in the standard master-detail UI, where it is always refreshed, when the current selected room is changed.  If you check each room entity, you should find a Reservations navitation property too.  You can use this navigation property to access the reservations for each room directly in your code.  That collection is independent to the screen level collection.

    Now, if you try to bind to that collection to your UI, it will fail.  Because the navigation property can only be accessed in the thread running user code, it cannot be accessed by the UI code (which runs in the main UI thread) directly.  With the intellisense, you can fine a room object also exposes ReservationsQuery property, which is a query object.

    You can write a converter to convert this query object to an collection, which can be bound to your stack panel. With the converter, you then can bind your control to the query object directly.

    Inside the converter, you can find the query object supports IExecutable<IEnumerable<Reservation>> interface. This interface has a method ExecuteAsync, which allows you to execute the query asynchoronously. This can be called in the UI thread. (Don't try to call Execute).  Before that, you need create an empty observable collection, and also hook up ExecuteCompleted event of the query object.  The converter will return the empty collection.

    When the execution is done, you can get the result from it (from Result property), and then fill the observable collection you created earlier.  Your UI will get notification, and fill the stack panel.

    Hope that gives you some idea.

    Good Luck

    Lifeng


    LLF

    Saturday, March 10, 2012 6:44 PM
  • Hi Lifeng,

    Thanks a lot for you detailed recommendations.

    I worked today on the custom control, and I think I'm doing more or less what you are proposing over here.

    Feel feel to suggest any enhancements !

    thx

    paul.

    This is the result:

    Indeed, a valueconverter is the clue !

    The xaml goes as follows:

     <Grid x:Name="LayoutRoot" Background="White"  DataContext="{Binding Screen}" >
            <sdk:DataGrid  AutoGenerateColumns="False" x:Name="grid"  ItemsSource="{Binding Rooms}">
                <sdk:DataGrid.Columns>
                    <sdk:DataGridTextColumn 
                                 Binding="{Binding Name}" 
                                 Header="Name"/>
                    <sdk:DataGridTemplateColumn   Width="*">
                        <sdk:DataGridTemplateColumn.HeaderStyle>
                            <Style 
                              TargetType="primitives:DataGridColumnHeader">
                                <Setter 
                                  Property="HorizontalContentAlignment" 
                                  Value="Stretch" />
                                <Setter 
                                  Property="VerticalContentAlignment"  
                                  Value="Stretch" />
                                <Setter Property="Margin" 
                                             Value="0" />
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <ItemsControl  
                              ItemsSource="{Binding DataContext.DateSlots,ElementName=LayoutRoot}">
                                                <ItemsControl.ItemsPanel>
                                                    <ItemsPanelTemplate>
                                                        <StackPanel 
                                             Orientation="Horizontal">
                                                        </StackPanel>
                                                    </ItemsPanelTemplate>
                                                </ItemsControl.ItemsPanel>
                                                <ItemsControl.ItemTemplate>
                                                    <DataTemplate>
                                                        <Border  Width="25" >
                                                            <TextBlock Text="{Binding Slot}" 
                                                  TextAlignment="Center"/>
                                                        </Border>
                                                    </DataTemplate>
                                                </ItemsControl.ItemTemplate>
                                            </ItemsControl>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </sdk:DataGridTemplateColumn.HeaderStyle>
                        <sdk:DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ItemsControl ItemsSource="{Binding Converter={StaticResource ReservationValueConverter}, ConverterParameter=Reservations}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate  >
                                            <StackPanel   Orientation="Horizontal"/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <StackPanel>
                                                <Border Width="25">
                                                    <TextBlock Height="25" Width="25" TextAlignment="Center" 
                                                               MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"  
                                                               Tag="{Binding Id}"  Text="{Binding State}">
                                                        
                                                        
    
                                                    </TextBlock>
                                                </Border>
                                            </StackPanel>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                            </DataTemplate>
                        </sdk:DataGridTemplateColumn.CellTemplate>
                    </sdk:DataGridTemplateColumn>
                </sdk:DataGrid.Columns>
            </sdk:DataGrid>
        </Grid>

    The valueconverter: in fact I'm doing more or less the same that Sheel is doing in his TreeControl.

     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                ObservableCollection<Reservation> reservationOC = new ObservableCollection<Reservation>();
                List<Reservation> list = new List<Reservation>();
                IEntityObject hostEntity = value as IEntityObject;
    
    
                if (hostEntity == null)
                {
                    return value;
                }
    
                hostEntity.Details.Dispatcher.BeginInvoke(() =>
                {
                    IEntityCollection reservations = hostEntity.Details.Properties[parameter.ToString()].Value as IEntityCollection;
    
                    foreach (Reservation item in reservations)
                    {
                        list.Add(item);
                    }
    
                    Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(delegate
                   {
                       foreach (Reservation item2 in list)
                       {
                           reservationOC.Add(item2);
                       }
                   });
    
                });
                return reservationOC; 
    
            }


    paul van bladel

    Saturday, March 10, 2012 7:17 PM
  • Hi Lifeng,

    I'm trying to understand your approach.

    What is the way to go from the IDataServiceQueryable to IExecutable ?

     IDataServiceQueryable<Reservation> query = currentRoom.ReservationsQuery;

     IExecutable<IEnumerable<Reservation>> exec = query as IExecutable<IEnumerable<Reservation>>;

    By doing so, exec is always null.

    thx

    paul.


    paul van bladel

    Saturday, March 10, 2012 8:45 PM
  • That doesn't sound right.  I am not sure what went wrong.  BTW, your code looks right to me, and I tried something similar and it worked.

    If you have trouble to get this to work.  I think a simplier solution is to make a sync call in the logic thread. We will see some performance consquence, but I think it will get you to start at least.

    My suggestion is that you can delegate the call to the logic thread, and fill the data back in the main thread.  So your converter will look like:

      ObservableCollection<Reservation> reservations = new ObservableCollection<Reservation>();

      currentRoom.Details.Dispatcher.BeginInvoke(() => {

         List<Reservations> list = new List<Reservations>();

          foreach (Reservation r in currentRoom.Reservations)

          {

                 list.Add(r);

          }

          Dispatchers.Main.BeginInvoke( () => {

              foreach (Reservation r in list) {

                 reservations.Add(r);

              });

       });

        return reservations;

    Lifeng

    Monday, March 12, 2012 5:12 PM
  • Hi Lifing,

    Indeed, that's how I'm doing it (maybe you missed the previous ?).

    Would be great to learn how I can invoke an async call.

    As far as I can see, only an EntitySet implements IExecutable? 

    Thanks for your continuous support !

    paul.


    paul van bladel

    Monday, March 12, 2012 6:19 PM
  • No, the navigation query you get from the entity should implement IExecutable as well.  That means the IDataServiceQueryable<Reservation> object you get from the entity is an IExecutable object, and you should be able to execute it in async way, and hook up events to get the result.   I am not sure why your type convertion doesn't work.  Can you try it again to convert it to IExecutableWithResult? 

    The IDataServiceQueryable doesn't inherit from the IExecutable, but the implementation should support both.


    -L

    Wednesday, March 14, 2012 6:06 PM
  • Hi Lifeng,

    I would be great if I could incorporate the asyn improvement you suggested.

    So, I'm trying now to focus now solely on the IExecutable problem and therefor I'm trying to execute following code just in a button click code behind on a regular ListDetails screen (in my case the Room List detail screen) , rather than in the valueConverter.

    Is it correct to presume that the behaviour should be the same?

    Can you please advice what I should change to the following code block to get this working?

    thx a lot !!

     partial void DummyButton_Execute()
            {
    
                IEnumerable<Reservation> mylist = new List<Reservation>();
                Room currentRoom = this.Rooms.SelectedItem;
                var currentReservations = this.Rooms.SelectedItem.ReservationsQuery;
    
                var exec = currentReservations as IExecutable<IEnumerable<Reservation>>;
    
                exec.ExecuteAsync();
    
                exec.ExecuteCompleted += new EventHandler<ExecuteCompletedEventArgs>((s, e) =>
                {
                    mylist = exec.Result;
                });
         }


    paul van bladel

    Wednesday, March 14, 2012 7:32 PM
  • Lifeng,

    Good new, in the isolated scenario, as above, it works when I change the IExecutable into a IExecutableWithResult !!

    Now, i'm trying to get this working in the ValueConverter, which is not working yet.

    Can you give a (last) hint on this code block ?

    ObservableCollection<Reservation> reservationOC = new ObservableCollection<Reservation>(); List<Reservation> list = new List<Reservation>(); Room currentRoom = value as Room; currentRoom.Details.Dispatcher.BeginInvoke(() => { IDataServiceQueryable<Reservation> query = currentRoom.ReservationsQuery; var exec = query as IExecutableWithResult; exec.ExecuteCompleted += new EventHandler<ExecuteCompletedEventArgs>((s, e) => { Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() => { foreach (var item in exec.Result as IEnumerable<Reservation>) { reservationOC.Add(item); } }); }); exec.ExecuteAsync();

     });
                return reservationOC;



    paul van bladel


    Wednesday, March 14, 2012 8:21 PM
  • Lifeng,

    I think we found a final solution. If you know a way to get rid of the intermediate list IEnumerable, drop me a line :)

    Many thanks for your help !

     public class ReservationValueConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                ObservableCollection<Reservation> reservationObservableCollection = new ObservableCollection<Reservation>();
                List<Reservation> list = new List<Reservation>();
                Room currentRoom = value as Room;
                currentRoom.Details.Dispatcher.BeginInvoke(() =>
                    {
                        var query = currentRoom.ReservationsQuery as IExecutableWithResult;
                        query.ExecuteCompleted += new EventHandler<ExecuteCompletedEventArgs>((s, e) =>
                        {
                            foreach (Reservation item in query.Result as IEnumerable<Reservation>)
                            {
                                list.Add(item);
                            }
                            Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(() =>
                                {
                                    foreach (Reservation item in list)
                                    {
                                        reservationObservableCollection.Add(item);
                                    }
                                });
                        });
                        query.ExecuteAsync();
                    });
                return reservationObservableCollection;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return value;
            }
        }


    paul van bladel

    Thursday, March 15, 2012 7:00 AM
  • Your code looks good to me.  I guess you can get rid of both BeginInvoke, and in that case, you don't need use the list in the middle.  Unlike Execute, the ExecuteAsync function can be called in the main thread.  You also need hook up the event, and call ExecuteAsync in the same thread, and the event will be fired in that thread as well.

    Thanks


    -L

    Wednesday, March 21, 2012 1:28 AM
  • Hi Lifeng,

    Many thanks, the code code indeed be simplyfied accordingly:

     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                ObservableCollection<Reservation> reservationObservableCollection = new ObservableCollection<Reservation>();
                Room currentRoom = value as Room;
                var query = currentRoom.ReservationsQuery as IExecutableWithResult;
                query.ExecuteCompleted += new EventHandler<ExecuteCompletedEventArgs>((s, e) =>
                {
                    foreach (Reservation item in query.Result as IEnumerable<Reservation>)
                    {
                        reservationObservableCollection.Add(item);
                    }
                });
                query.ExecuteAsync();
                return reservationObservableCollection;
            }

    There is still one factor on which performance could be improved: the value converter is now doing a row by row conversion meaning that there is for each room a call to the database. Is there a way to fetch all data in one iteration, rather than the current "chatty" communication ? 

    Thanks

    paul.


    paul van bladel

    Wednesday, March 21, 2012 7:24 AM
  • Hi Paul,

    Your solution have many implementations in our case. Is it possible for you to upload us a sample application preferable in vb.net in order to work with?

    We are not experts in lightswitch and that would help us a lot in order to implement that logic to our project.thank you!

    Sunday, May 20, 2012 8:44 PM
  • Hi Mike,

    No problem, I will try to help you. I'm quite busy beginning of this week, but I will make a small blog post on the solution I could work out with the help of Lifeng, at the end of this week..  I only know c#, but there are "translators" online.  Anyhow, the most important part is in the xaml and that's language independent. 

    I'll drop a line over here when I have something :)


    paul van bladel

    Monday, May 21, 2012 6:23 PM
  • thank you !
    Tuesday, May 22, 2012 5:08 PM
  • Sorry for the delay. I completed today the sample project.

    So, it's a custom control for visualizing room reservation in a kind of "plan board" way rather than a classic datagrid.

    You can find the details + a sample project here:

    http://blog.pragmaswitch.com/?p=318


    paul van bladel

    • Marked as answer by Otomii Lu Friday, June 01, 2012 7:57 AM
    Thursday, May 31, 2012 1:27 PM