Search code examples
c#wpfwcfdatagridiasyncresult

Sending DataGrid (DataGridRowDetailsEventArgs e) AsyncResult


Good day. Have a DataGrid (operationGrid) RowDetailsTemplate, which contains the DataGrid (sumOperationGrid)

            <DataGrid AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" IsReadOnly="True" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=OperCountUnits}" Name="operationGrid" RowDetailsVisibilityMode="VisibleWhenSelected" SelectionMode="Single" VerticalContentAlignment="Bottom" Grid.ColumnSpan="2" Margin="6,60,7,6" LoadingRowDetails="operationGrid_LoadingRowDetails" SelectionChanged="operationGrid_SelectionChanged">
                <DataGrid.Columns>
                    <DataGridTextColumn x:Name="Id" Binding="{Binding Path=Id}" Header="{Binding NumberOfOperation, Source={StaticResource resources}}" IsReadOnly="True" />
                    <DataGridTextColumn x:Name="StartedAt" Binding="{Binding Path=StartedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding StartedAt, Source={StaticResource resources}}" IsReadOnly="True" />
                    <DataGridTextColumn x:Name="FinisedAt" Binding="{Binding Path=FinishedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding FinishedAt, Source={StaticResource resources}}" IsReadOnly="True" />
                    <DataGridTextColumn x:Name="CountMode" Binding="{Binding Path=Description}" Header="{Binding CountMode, Source={StaticResource resources}}" IsReadOnly="True" />
                    <DataGridTextColumn x:Name="CurrencyCode" Header="{Binding CurrcencyCode, Source={StaticResource resources}}" IsReadOnly="True" />
                    <DataGridTextColumn x:Name="UnitId" Binding="{Binding Path=UnitId}" Header="UnitId" IsReadOnly="True" Visibility="Collapsed" />
                    <DataGridTextColumn x:Name="CountModeId" Binding="{Binding Path=CountModeId}" Header="CountModeId" IsReadOnly="True" Visibility="Collapsed" />
                    <DataGridTextColumn x:Name="HotListCount" Binding="{Binding Path=HotListCount}" Header="Фальш" IsReadOnly="True" Visibility="Collapsed" />
             </DataGrid.Columns>
                <DataGrid.RowDetailsTemplate>
                    <DataTemplate>
                        <DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding CurrencySum}" Name="sumOperationGrid" SelectionChanged="sumOperationGrid_SelectionChanged" SelectionMode="Single">
                            <DataGrid.Columns>
                                <DataGridTextColumn Binding="{Binding csId}" Visibility="Collapsed" />
                                <DataGridTextColumn Binding="{Binding CurrId}" Visibility="Collapsed" />
                                <DataGridTextColumn Binding="{Binding Curr}" Header="{Binding Currency, Source={StaticResource resources}}" />
                                <DataGridTextColumn Binding="{Binding TotalSum, Converter={StaticResource replaceNull}}" Header="{Binding Sum, Source={StaticResource resources}}" />
                          </DataGrid.Columns>
                        </DataGrid>
                    </DataTemplate>
                </DataGrid.RowDetailsTemplate>
            </DataGrid> 

The request is for the WCF service and it turns out the answer is everything is good If you submit data through a delegate, then the answer is in the datagrid (sumOperationGrid) come and displays.

  private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
   private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
    {
        OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
        DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
        client.BeginGetCurrencySumByCurrentItem((int)_con.Id,
            new AsyncCallback(
            delegate(IAsyncResult result)
            {
                List<CurrencySum> sum = client.EndGetCurrencySumByCurrentItem(result).ToList<CurrencySum>();
                if (sum != null)
                {
                    s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[2] {s2, sum});
                }
            }
           ), null);
      }

    void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
    {
        sumGrid.ItemsSource = sum;
    } 

Tried so.

private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
 DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback        (GetCurrencySumByCurrentItemCallBack), null);
}

private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGridRow opGrRow = (DataGridRow)(this.operationGrid.ItemContainergenerator.containerfromitem        (this.operationGrid.Items.Currentitem));
 ContentPresenter cpOpGrRow = FindVisualChild<ContentPresenter>(opGrRow);
DataTemplate dtOpGrRow = cpOpGrRow.ContentTemplate;
DataGrid s2 = (DataGrid)dtOpGrRow.FindName("sumOperationGrid", cpOpGrRow);

}

void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}

private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
} 

and so

private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback    (GetCurrencySumByCurrentItemCallBack), s2);
}

private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGrid s2 = result.AsyncState as DataGrid
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
if (sum != null)
{
s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke),     new object[2] {s2, sum});
}


}

void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}

In both cases, it all boils down to the error "The calling thread cannot access this object because the owner of this object is another thread."

The question of how to remove a delegate from client.BeginGetCurrencySumByCurrentitem and forward data table s2 in a separate function? How do I get back into your flow and load data into a table sumOperationGrid ?


Solution

  • If I understood messy code I cant understand why you trying to pass through data grid everywhere. Only try to access grid and set ItemsSource at the end of operation only by passing sum. Here is pseudocode of callback function:

    private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
    {
       if(sum != null)
       {
          this.Invoke(new Action(()=>
           {
              //get data grid 
              //set ItemSource
           })
       }
    }
    

    Or if you do need to have data grid to Invoke try to do something like this:

    DataGrid myDataGrid;
    private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
    {
        OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
        myDataGrid = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
        //do stuff without passing grid
    }
    
    private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
    {
        List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
        if (sum != null)
        {
            myDataGrid.Dispatcher.BeginInvoke(new Action(()=>
             {
                 myDataGrid.ItemSource = sum;
             });
    }