2 problems with Grid + RadDomainDataSource

10 posts, 0 answers
  1. Serhiy Volhushyn
    Serhiy Volhushyn avatar
    21 posts
    Member since:
    Dec 2009

    Posted 31 Mar 2011 Link to this post

    We are using Telerik Q1 2011 build 0322 (tested with 0328 too).
    There 2 problems when using grid + RadDomainDataSource.

    1. Need an ability to insert prepared object into domain data source and submit changes.
    When trying to RadDomainDataSource.DataView.Add(new MyObject());
    getting an exception:
    System.InvalidOperationException was unhandled by user code
      Message=Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
      StackTrace:
           at Telerik.Windows.Data.DataItemCollection.EnsureIsUsingInternalView()
           at Telerik.Windows.Data.DataItemCollection.Add(Object value)
           at Accellos.DataServices.WUI.FileMaintenance.Controls.Test1.MyWorkspaceControl1.<.ctor>b__2(Object , RoutedEventArgs )
           at System.Windows.Controls.Primitives.ButtonBase.OnClick()
           at System.Windows.Controls.Button.OnClick()
           at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
           at System.Windows.Controls.Control.OnMouseLeftButtonUp(Control ctrl, EventArgs e)
           at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName)
      InnerException:

    Is there any other way to insert object into RadDomainDataSource and submit changes ?

    2. After inserting a new row (hit "Insert" key) an submitting changes grid selection is totally screwed when selecting with mouse.

    http://img600.imageshack.us/i/gridv.png

    Here's the source code:

    <UserControl
        x:Class="Accellos.DataServices.WUI.FileMaintenance.Controls.Test1.MyWorkspaceControl1"
        xmlns:mt="clr-namespace:Accellos.DataServices.MT.Client.SL.DomainDataSource.Context;assembly=Accellos.DataServices.MT.Client.DataServicesAsync.SL"
        xmlns:radData="clr-namespace:Telerik.Windows.Data;assembly=Telerik.Windows.Data"
        xmlns:radDomain="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.DomainServices"
        xmlns:radGrid="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
        xmlns:Controls="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Data"
        >
      
        <UserControl.Resources>
            <radDomain:RadDomainDataSource x:FieldModifier="public" x:Name="BuildingTypeDomainDataSource" QueryName="FindAllBuildingType" AutoLoad="True" DataContext="{Binding}">
                <radDomain:RadDomainDataSource.DomainContext>
                    <mt:BuildingTypeDomainContext/>
                </radDomain:RadDomainDataSource.DomainContext>
                <radDomain:RadDomainDataSource.QueryParameters>
                    <radDomain:QueryParameter ParameterName="BuildingTypeHydration" Value="None"/>
                </radDomain:RadDomainDataSource.QueryParameters>
                <radDomain:RadDomainDataSource.SortDescriptors>
                    <radData:SortDescriptor Member="Type" SortDirection="Ascending"/>
                </radDomain:RadDomainDataSource.SortDescriptors>
            </radDomain:RadDomainDataSource>
        </UserControl.Resources>
      
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
              
            <Button x:Name="InsertButton" Content="Insert new row" />
      
            <radGrid:RadGridView Grid.Row="1" x:Name="gridView1" AutoGenerateColumns="False" SelectionMode="Extended" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding DataView, Source={StaticResource BuildingTypeDomainDataSource}}">
                <radGrid:RadGridView.Columns>
                    <radGrid:GridViewDataColumn UniqueName="Id" DataMemberBinding="{Binding Path=Id, Mode=TwoWay}" IsReadOnly="True" IsGroupable="True" Width="Auto" IsVisible="True">
                        <radGrid:GridViewDataColumn.Header>
                            <TextBlock Text="Id" ToolTipService.ToolTip="Id" />
                        </radGrid:GridViewDataColumn.Header>
                    </radGrid:GridViewDataColumn>
      
                    <radGrid:GridViewDataColumn UniqueName="Type" DataMemberBinding="{Binding Path=Type, Mode=TwoWay}" IsReadOnly="False" IsGroupable="True" Width="Auto" IsVisible="True">
                        <radGrid:GridViewDataColumn.Header>
                            <TextBlock Text="Type" ToolTipService.ToolTip="Type" />
                        </radGrid:GridViewDataColumn.Header>
                    </radGrid:GridViewDataColumn>
                </radGrid:RadGridView.Columns>
            </radGrid:RadGridView>
      
            <Controls:RadDataPager Grid.Row="2" Name="dataPager1" PageSize="20" Source="{Binding DataView, Source={StaticResource BuildingTypeDomainDataSource}}" />
        </Grid>
    </UserControl>

    public MyWorkspaceControl1()
    {
        InitializeComponent();
        gridView1.RowEditEnded +=
            delegate(object o, GridViewRowEditEndedEventArgs e)
            {
                if (e.EditAction != GridViewEditAction.Commit)
                {
                    BuildingTypeDomainDataSource.RejectChanges();
                    return;
                }
                if (BuildingTypeDomainDataSource.HasChanges && !BuildingTypeDomainDataSource.IsSubmittingChanges)
                    BuildingTypeDomainDataSource.SubmitChanges();
            };
        gridView1.Deleted +=
            delegate
                {
                    if (BuildingTypeDomainDataSource.HasChanges && !BuildingTypeDomainDataSource.IsSubmittingChanges)
                        BuildingTypeDomainDataSource.SubmitChanges();
                };
        InsertButton.Click +=
            delegate
                {
                    BuildingTypeDomainDataSource.DataView.Add(new BuildingType());
                };
    }
  2. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 01 Apr 2011 Link to this post

    Hi Serhiy Volhushyn,

    1. For a CRUD implementation, please take a look at this blog post. Basically, do all the CRUD on RadGridView and don't work with RDDS.DataView directly. Since, the RDDS.DataView is an IEditableCollectionView it should be able to "pick-up" all the CRUD automatically.

    You should only need to call RDDS.SubmitChanges after you are ready to submit the changes to the server. I believe that the blog post will help you.

    2. For the second issue we will need a runnable sample project that reproduces this behavior, since we cannot understand what is going wrong from the screen-shot and the code-snippets.

    To explore the new RadDomainDataSource control in greater depth, please check its online examples and take a look at the following blog posts:

    We are looking forward to hearing from you.

    Greetings,
    Ross
    the Telerik team
  3. DevCraft banner
  4. Jonx
    Jonx avatar
    258 posts
    Member since:
    Jul 2012

    Posted 10 Jul 2011 Link to this post

    Hello Ross,
    I have the same problem.
    Would you mind giving me a sample on how to add a new item by code only?
    My scenario is that I have a grid bound to a RadDomainDatasource showing a list of clients.
    I have a new button that shows a dialog letting me fill the values for my new Client.
    When I click OK, my client should be added to the grid items...
    What I try to do is this:
    void dlgClient_Closed(object sender, WindowClosedEventArgs e)
           {
               if (e.DialogResult.HasValue && e.DialogResult.Value == true)
               {
                   customersDataSource.DataView.Add(lastAddedCustomer);
                   customersDataSource.SubmitChanges();
               }
         }

    How is this done using the way you recommand?
    Thanks a lot for your help,
    John.
  5. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 11 Jul 2011 Link to this post

    Hello John,

    If you want to add a record programmatically, you create it, initialize its properties, add it to the DataView collection and then call the SubmitChanges method. In other words, you should do this in absolutely the same way as you would do it with the stock DomainDataSource control. In fact, guess what, adding the record to the DataView will in fact add it to the underlying EntityContainer and then calling the SubmitChanges method will in fact call the SubmitChanges method of the DomainContext. In other words, it is the WCF RIA DomainContext class that does all of the magic, we are simply calling its methods, nothing more. 

    So the code you have pasted seems fine.

    I hope this helps.

    Regards,
    Ross
    the Telerik team

    Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!

  6. Jonx
    Jonx avatar
    258 posts
    Member since:
    Jul 2012

    Posted 11 Jul 2011 Link to this post

    Hi ross,
    Thank you for the supra fast feedback and the detailed help...
    The thing is that when I do exactly that I get the following error : "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."

    I'm unable to track the thing that is under way... 

    The workaround that I'm using rigth now is to add the item to the context then force a reload of the datasource:
    void dlgClient_Closed(object sender, WindowClosedEventArgs e)
            {
                if (e.DialogResult.HasValue && e.DialogResult.Value == true)
                {
                    OgContext ctx = new OgContext();
                    ctx.Clients.Add(lastAddedCustomer);
     
                    busyIndicator.IsBusy = true;
                    busyIndicator.BusyContent = "Ajout du client en cours...";
     
                    ctx.SubmitChanges(CreatedCmdOp =>
                    {
                        busyIndicator.IsBusy = false;
     
                        if (CreatedCmdOp.HasError)
                        {
                            RadWindow.Alert("Impossible d'ajouter ce client.");
                            CreatedCmdOp.MarkErrorAsHandled();
                        }
                        else
                        {
                            customersDataSource.QueryParameters[0].Value = string.Format("{0}", lastAddedCustomer.CliID);
                            customersDataSource.Load();
                        }
                    }, null);
                }
                else
                {
                    lastAddedCustomer = null;
                }
            }

    By the way, is there no way to use name parameters instead of just an index?
    Like customersDataSource.QueryParameters["clientName"].Value

    Any hint of how I could track what is going on?

    Thank you for your help,
    John.
  7. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 11 Jul 2011 Link to this post

    Hello John,

    My bad, I made a mistake. I totally forgot that when adding an item we have to work through the IEditableCollectionView interface like this:

    var category = (Category)this.dds.DataView.AddNew();
    category.CategoryName = "New Category Name";
    category.Description = "New Category Description";
    this.dds.DataView.CommitNew();
     
    this.dds.SubmitChanges();

    I have prepared a sample project which demonstrates this.

    If you use RadGridView's UI to insert, edit or delete records, this IEditableCollectionView stuff will be automatically performed for you.

    As for finding the query parameter that you want -- you could always use LINQ and search for the the parameter with the name that you need. It's pretty easy. There is some code after InitializeComponent in my sample project.

    I hope this helps.

    Kind regards, Ross
    the Telerik team

    Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!

  8. Jonx
    Jonx avatar
    258 posts
    Member since:
    Jul 2012

    Posted 11 Jul 2011 Link to this post

    Indeed, it's working...
    Thanks a lot for the fast response and the sample...

    Nice tricks for the LINQ query:
    // This is how you can look for anything using LINQ to Objects
    var myParam = this.dds.QueryParameters.FirstOrDefault(qp => qp.ParameterName == "your param name here");

    And the way to compute the key:
    category.CategoryID = this.dds.DataView.OfType<Category>().Max(c => c.CategoryID) + 1;

    But... Yes, there needs to be a but ;)

    The thing is that with that I don't know how to fit it in my workflow...

    First there is the fact that my Client entity initialisation is pretty complicated and for that I use a static constructor taking parameters like that:
    Client newCustomer = Client.New(true);
      
    DetailPersonnePanelAdd detail = new DetailPersonnePanelAdd();
    detail.DataContext = newCustomer;
      
    //open the dialog bound to that entity to let the user fill the fields
    AddNewEntity(detail, "New customer", 490, 800, dlgClient_Closed);

    And then when the dialog closes, I add newCustomer to my context and submitchanges.

    Is there no other way to add my newCustomer entity to the collection?

    So that I can use my constructors and avoid having extra code after the DataView.AddNew(); were I wold set the values that I currently set in my constructor?

    Also it would be very nice if the RadDomainDatasource add also a SubmitChanges taking a callback like the DomainContext.SubmitChanges does. It is very usefull to me to be able to use anonymous methods as a callback for SubmitChanges instead of the current solution involving the SubmittedChanges event...

    Thanks again for the quality of your responses. They are very helpfull,
    John.
  9. Rossen Hristov
    Admin
    Rossen Hristov avatar
    2478 posts

    Posted 13 Jul 2011 Link to this post

    Hi John,

    When working with views, the only way to add a new item is through the AddNew method. That is because they are views and not collections.

    I will give you a workaround (kind of a hack really) that will give you a reference to the underlying source collection in which you can add items manually, i.e. with the Add method:

    private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        // Create and initalize your object with your factory methods.
        var newItem = new Category();
        newItem.CategoryID = this.dds.DataView.OfType<Category>().Max(c => c.CategoryID) + 1;
        newItem.CategoryName = "Category " + newItem.CategoryID;
        newItem.Description = "Category Description " + newItem.CategoryID;
     
        // And once you have it
        QueryableDomainServiceCollectionView<Category> qdscv = (QueryableDomainServiceCollectionView<Category>)this.dds.DataView.SourceCollection;
         
        // internalCollection is actually of type DomainServiceCollection<Category> but it is internal
        // Anyway, you only need to add an item, so you can do it through the ICollection interface.
        ICollection<Category> internalCollection = (ICollection<Category>)qdscv.SourceCollection;
         
        // This will make sure it is added to the respective EntitySet.
        internalCollection.Add(newItem);
         
        this.dds.SubmitChanges();
    }

    As for the SubmitChanges callback, we simply can not replace all events with callbacks in all of our controls. Currently, events are the standard way of doing things. I suppose that you need to use anonymous callback in order to see some variables that are in the current scope. That could be the only reason that I can think of.

    If you really need to use a callback, you can easily write an extension method that will accept one and then do the trick inside:

    public static class RadDomainDataSourceExtensions
    {
        public static void SubmitChangesWithCallback(this RadDomainDataSource rdds
            , Action<RadDomainDataSource, DomainServiceSubmittedChangesEventArgs> submittedChangesCallback)
        {
            EventHandler<DomainServiceSubmittedChangesEventArgs> submittedChangesEventHandler = null;
            submittedChangesEventHandler = (sender, args) =>
            {
                rdds.SubmittedChanges -= submittedChangesEventHandler;
                submittedChangesCallback((RadDomainDataSource)sender, args);
            };
     
            rdds.SubmittedChanges += submittedChangesEventHandler;
            rdds.SubmitChanges();
        }
    }

    Note how I detach from the event handler to avoid memory leaks.

    Then you can call this new extension method like this and pass in your anonymous callback:

    this.dds.SubmitChangesWithCallback((rdds, args) =>
    {
        MessageBox.Show("SubmittedChanges!");
    });

    Isn't .NET is simply beautiful :)

    All of these changes are in the sample project that I have attached.

    I hope this helps.

    Best wishes,
    Ross
    the Telerik team

    Register for the Q2 2011 What's New Webinar Week. Mark your calendar for the week starting July 18th and book your seat for a walk through of all the exciting stuff we will ship with the new release!

  10. Jonx
    Jonx avatar
    258 posts
    Member since:
    Jul 2012

    Posted 13 Jul 2011 Link to this post

    Hello Ross,
    Yes, .NET is beautiful and you are a genius ;)

    Honnestly, thank you for your help.
    It all works like you say and the extension method will be really useful for me in many other cases...

    Case closed. Excellent support, like always...
    Best regards,
    John.
  11. Tingting
    Tingting avatar
    14 posts
    Member since:
    May 2012

    Posted 20 Nov 2012 Link to this post

    Hello, 

       I have a problem with  RadDomainDataSource.DomainContext. My DomainContext works well with 
    DomainDataSource, but when i use it in the RadDomainDataSource i will retrive a error like this: 
              "Can not create an instance of RadDomainDataSource"
    
    Do you have any idea for this ? 
Back to Top
DevCraft banner