none
UI freeze in for loop Need Async and await

    Pertanyaan

  •     private void CopyData()
            {
                if (Collection.SelectedCells == null) return;
                var sb = new StringBuilder();
                var i = 0;
                var j = 0;
                var controlList = new List<int>();
                foreach (var variable in Collection.SelectedCells)
                {
                    controlList.Add(variable.Column.DisplayIndex);
                    if (controlList.Count == controlList.Distinct().Count()) continue;
                    i = controlList.Count - 1;
                    controlList.Clear();
                    break;
                }
    
                foreach (var variable in Collection.SelectedCells)
                {
                    if (j == i)
                    {
                        sb.AppendLine(null);
                        j = 0;
                    }
                    switch (variable.Column.Header.ToString())
                    {
                        case "Drug":
                            sb.Append(((Model) variable.Item).Drug).Append("\t");
                            break;
                        case "Dosage":
                            sb.Append(((Model) variable.Item).Dosage).Append("\t");
                            break;
                        case "Patient":
                            sb.Append(((Model) variable.Item).Patient).Append("\t");
                            break;
                        case "Date":
                            sb.Append(((Model) variable.Item).Date).Append("\t");
                            break;
                        default:
                            break;
                    }
                    j++;
                }
                Clipboard.SetText(sb.ToString(), TextDataFormat.Text);
                sb.Clear();
            }

    Datagrid Freeze while Running the above Code...

     Collection.SelectedCells is  IList<DataGridCellInfo> 

    Need async and await

    18 Agustus 2018 19:01

Semua Balasan

  • Tried Below code as well...Not working

      private async Task CopyData()
            {
                if (Collection.SelectedCells == null) return;
                var sb = new StringBuilder();
                var i = 0;
                var j = 0;
                var controlList = new List<int>();
                foreach (var variable in Collection.SelectedCells)
                {
                    controlList.Add(variable.Column.DisplayIndex);
                    if (controlList.Count == controlList.Distinct().Count()) continue;
                    i = controlList.Count - 1;
                    controlList.Clear();
                    break;
                }
    
                await Task.Factory.StartNew(
                    () =>
                    {
    
                     foreach (var variable in Collection.SelectedCells)
                {
                    if (j == i)
                    {
                        sb.AppendLine(null);
                        j = 0;
                    }
    
                    switch (variable.Column.Header.ToString())
                    {
                        case "Drug":
                            sb.Append(((Model) variable.Item).Drug).Append("\t");
                            break;
                        case "Dosage":
                            sb.Append(((Model) variable.Item).Dosage).Append("\t");
                            break;
                        case "Patient":
                            sb.Append(((Model) variable.Item).Patient).Append("\t");
                            break;
                        case "Date":
                            sb.Append(((Model) variable.Item).Date).Append("\t");
                            break;
                        default:
                            break;
                    }
                    j++;
                }
                    }
                    , CancellationToken.None
                    , TaskCreationOptions.RunContinuationsAsynchronously
                    , TaskScheduler.Default
                );
    
                await Task.Factory.StartNew(
                    () =>
                    {
                       Clipboard.SetText(sb.ToString(), TextDataFormat.Text);
                sb.Clear();
                    }
                    , CancellationToken.None
                    , TaskCreationOptions.RunContinuationsAsynchronously
                    , TaskScheduler.Default
                );
            }

    18 Agustus 2018 19:05
  • See the following docs for InvokeRequired.

    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    18 Agustus 2018 19:52
  • A loop freezing doesn't indicate that you need async/await. You'd use async/await if your UI is not responding while you do work. Is the above method called from an async method? If so then post the logic for that as it is the key. This method has to be called on the UI thread. Depending upon how your async method is set up determines whether that is happening. Specifically if you are calling this method from the UI thread (e.g. an event handler) before your first await then it is being called correctly. However if you are doing that after the await then it may or may not be called on the correct thread depending upon whether you're using ConfigureAwait or not.

    If you are not on the UI thread then this method needs to be invoked on the UI thread. The easiest way is to use Control.Invoke. However you don't want to mix that with async/await if you don't need it. Also consider that BackgroundWorker may be a better fit for a Winforms app that needs to do some work in the background and other work in the UI.


    Michael Taylor http://www.michaeltaylorp3.net

    19 Agustus 2018 6:11
  • A loop freezing doesn't indicate that you need async/await. You'd use async/await if your UI is not responding while you do work. Is the above method called from an async method? If so then post the logic for that as it is the key. This method has to be called on the UI thread. Depending upon how your async method is set up determines whether that is happening. Specifically if you are calling this method from the UI thread (e.g. an event handler) before your first await then it is being called correctly. However if you are doing that after the await then it may or may not be called on the correct thread depending upon whether you're using ConfigureAwait or not.

    If you are not on the UI thread then this method needs to be invoked on the UI thread. The easiest way is to use Control.Invoke. However you don't want to mix that with async/await if you don't need it. Also consider that BackgroundWorker may be a better fit for a Winforms app that needs to do some work in the background and other work in the UI.


    Michael Taylor http://www.michaeltaylorp3.net

         await Task.Factory.StartNew(
                    () =>
                    {
                      
                    }
                    , CancellationToken.None
                    , TaskCreationOptions.RunContinuationsAsynchronously
                    , TaskScheduler.Current
                );


    InvokeRequired.

    Dispatched

    Thread

    backgroundworker

    All failed in variable.Column.Header as above error msg shown in the Screen Shot..

    System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'

    Collection.SelectedCells is   

    public IList<DataGridCellInfo> SelectedCells
            {
                get => _selectedCells;
                set
                {
                    _selectedCells = value;
                    OnPropertyChanged(nameof(SelectedCells));
                }
            }


      private static void Selectedcells(object sender, SelectedCellsChangedEventArgs e)
            {
                
                updateVm.Collection.SelectedCells = datagrid.SelectedCells;
            }

    Which COmes from Datagrid WPF

    variable.Column.Header is datagrid Header Name. ...

    My Problem Statment UI freeze without async or Paralleism or Dispatcher or backgroundworker..what should i do..
    19 Agustus 2018 8:54
  •      await Task.Factory.StartNew(
                    () =>
                    {

                    }
                    , CancellationToken.None
                    , TaskCreationOptions.RunContinuationsAsynchronously
                    , TaskScheduler.Current
                );


    InvokeRequired.

    Dispatched

    Thread

    backgroundworker

    All failed in variable.Column.Header as above error msg shown in the Screen Shot..

    System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'

    Collection.SelectedCells is 

    	public IList<DataGridCellInfo> SelectedCells
            {
                get => _selectedCells;
                set
                {
                    _selectedCells = value;
                    OnPropertyChanged(nameof(SelectedCells));
                }
            }
    		
    		
    		  private static void Selectedcells(object sender, SelectedCellsChangedEventArgs e)
            {
                
                updateVm.Collection.SelectedCells = datagrid.SelectedCells;
            }

    Which COmes from Datagrid WPF

    variable.Column.Header is datagrid Header Name. ...

    My Problem Statment UI freeze without async or Paralleism or Dispatcher or backgroundworker..what should i do..

                      
    19 Agustus 2018 8:55
  • Again, the method you posted is fine. Provided it is called on the UI thread it will work correctly. What we need to see is the code you're using to call this method. Please provide the exact code you're using right now. The variants you specified won't work, in general, unless you configure things properly. How you're calling this method is the problem, not the method itself.

    Michael Taylor http://www.michaeltaylorp3.net

    19 Agustus 2018 19:06
  • Again, the method you posted is fine. Provided it is called on the UI thread it will work correctly. What we need to see is the code you're using to call this method. Please provide the exact code you're using right now. The variants you specified won't work, in general, unless you configure things properly. How you're calling this method is the problem, not the method itself.

    Michael Taylor http://www.michaeltaylorp3.net

    I am Calling that Method from Context Menu,


    <ContextMenu ItemsSource="{Binding Path=ContextMenu.MenuItems}">
        <ContextMenu.ItemContainerStyle>
            <Style TargetType="{x:Type MenuItem}">
                <Setter Property="Command" Value="{Binding CopyData}" />
                <Setter Property="CommandParameter"
                        Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
                                        Path=PlacementTarget}" />
            </Style>
        </ContextMenu.ItemContainerStyle>
    </ContextMenu>

    I am Sending the datagrid to Viewmodel Via Relycommand....and Running CopyData Method.

    28 Agustus 2018 8:01
  • So the method is running on the UI thread. Since it is touching UI elements you cannot use async/await with it. If you try then you'll get a cross threading error. You also cannot use threading, BackgroundWorker or anything else that moves it off the UI thread. They will all generate an error. All interactions with a UI element must occur on the thread that created it (e.g. the UI thread). 

    This is a WPF app so short of breaking up your logic between UI-touching code and non-UI code the only thing I can think of to do is periodically ask WPF to dispatch messages. The code itself has to run on the UI thread though so while it is running you cannot do anything in the UI. Unless your collection is large though it should be pretty quick. The string builder logic is going to be the slowest part I'd wager. 

    One approach you might take is to (on the UI thread) get the items associated with the selected cells. Then pass that list of items to a background thread (async/await or BWC) to actually build the string buffer. This would be about as much as you can move in your current function.

    Can a moderator in the Winforms forum please move this question over to WPF forums?



    Michael Taylor http://www.michaeltaylorp3.net

    28 Agustus 2018 14:00
  • I don't mind showing mouse busy and work also let it stop... I just need context menu to disappear once clicked..
    30 Agustus 2018 6:02
  • The post got move to off topic and unfortunately I can't move it to WPF for you. Please post your question in the WPF forums on how to get the context menu to disappear before your code does its lengthy work. They'll be able to provide you that information.

    Michael Taylor http://www.michaeltaylorp3.net

    30 Agustus 2018 14:37
  • Sure...

    Please use the Link UI Freeze Link WPF

    30 Agustus 2018 14:46
  • The post got move to off topic and unfortunately I can't move it to WPF for you. Please post your question in the WPF forums on how to get the context menu to disappear before your code does its lengthy work. They'll be able to provide you that information.

    Michael Taylor http://www.michaeltaylorp3.net

    Sure...

    Please use the Link UI Freeze Link WPF

    30 Agustus 2018 14:46