none
Innerhalb async/await auf ContextIdle warten RRS feed

  • Frage

  • Hallo!

    Ich "fülle" ein TreeView mit Daten die ich mit einem asynchronen (async/await) Datenthread abrufe.

    Optisch stelle ich die Zeit des Datenabrufes durch "abdunkeln" (Opacity = 0.5) des Fensters dar:

    private async void cobODB_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        wndSQLtoTabelle.Opacity = 0.5;                                              // Hauptfenster "abdunkeln"
    
        // DB-Struktur der Oracle-DB einlesen (Mit der akt. Oracle-Verbindung)
       ...
       tvODBTabellen.DataContext = await System.Threading.Tasks.Task<TABELLEN_SPALTEN>.Run(() => getODBStruktur(Selektierte_ODB_Verbindung));
    
       wndSQLtoTabelle.Opacity = 1.0;                                              // Hauptfenster wieder voll sichtbar anzeigen.
             
    }

    Da der Datenabruf sehr viel kürzer, als die Zeit der Aktualisierung des TreeViews (tvODBTabellen) ist, wird das Abdunkeln des Fensters zu zeitig wieder aufgehoben. Deshalb habe ich bis jetzt immer auf das ContextIdle des Fensters gewartet und die optische Freigabe des Fensters in einen separaten Thread () ausgelagert:

    private async void cobODB_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        wndSQLtoTabelle.Opacity = 0.5;
        tvODBTabellen.DataContext = await System.Threading.Tasks.Task<TABELLEN_SPALTEN>.Run(() => getODBStruktur(Selektierte_ODB_Verbindung));            
        await Dispatcher.BeginInvoke(new Action(RendernFertig), System.Windows.Threading.DispatcherPriority.ContextIdle, null);
    }
    
    private void RendernFertig()            // GUI aktualisieren
    {
        wndSQLtoTabelle.Opacity = 1.0;                                              // Hauptfenster wieder voll sichtbar anzeigen.
    }

    Innerhalb von async/await wird das ContextIdle des Hauptfensters aber sofort gesetzt.

    Wie kann ich auf das Idle denn jetzt warten?

    Fred.



    • Bearbeitet perlfred Montag, 28. Januar 2019 12:44
    Montag, 28. Januar 2019 11:03

Antworten

  • Hi Fred,
    ich kann Dein Problem mit der folgenden Demo nicht nachvollziehen. Ich erzeuge nach der Auswahl in einer ListBox 10000 Knoten und bringe sie im TreeView zur Anzeige. Das dauert in meiner Umgebung ca. 15 Sekunden. In dieser Zeit ist die ListBox halbtransparent, solange die Daten im TreeView nicht sichtbar sind.

    XAML:

    <Window x:Class="WpfApp1.Window89"
            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"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window89" Height="450" Width="800">
      <Window.Resources>
        <local:Window89VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" ItemsSource="{Binding ViewLB}" Opacity="{Binding Opacity}"/>
        <TreeView Grid.Column="1" ItemsSource="{Binding ViewTV}"/>
      </Grid>
    </Window>

    Dazu der ViewModel:

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
    
      public class Window89VM : INotifyPropertyChanged
      {
        public Window89VM()
        {
          List<string> listeLB = new List<string>();
          for (int i = 0; i < 10; i++) listeLB.Add($"Zeile {i}");
          cvsLB.Source = listeLB;
          cvsLB.View.CurrentChanged += async (sender, e) =>
          {
            Opacity = .5;
            cvsTV.Source = await System.Threading.Tasks.Task<string>.Run(() =>
            {
              List<string> listeTV = new List<string>();
              for (int i = 0; i < 10000; i++) listeTV.Add($"Knoten {i}");
              return listeTV;
            });
            OnPropertyChanged(nameof(ViewTV));
            Opacity = 1;
          };
        }
        CollectionViewSource cvsLB = new CollectionViewSource();
        CollectionViewSource cvsTV = new CollectionViewSource();
        public ICollectionView ViewLB { get { return cvsLB.View; } }
        public ICollectionView ViewTV { get { return cvsTV.View; } }
    
        private double _opacity = 1;
        public double Opacity
        {
          get { return _opacity; }
          set { _opacity = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort vorgeschlagen Peter Fleischer Montag, 28. Januar 2019 13:25
    • Als Antwort markiert perlfred Dienstag, 29. Januar 2019 14:35
    Montag, 28. Januar 2019 13:22

Alle Antworten

  • Hi Fred,
    ich kann Dein Problem mit der folgenden Demo nicht nachvollziehen. Ich erzeuge nach der Auswahl in einer ListBox 10000 Knoten und bringe sie im TreeView zur Anzeige. Das dauert in meiner Umgebung ca. 15 Sekunden. In dieser Zeit ist die ListBox halbtransparent, solange die Daten im TreeView nicht sichtbar sind.

    XAML:

    <Window x:Class="WpfApp1.Window89"
            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"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window89" Height="450" Width="800">
      <Window.Resources>
        <local:Window89VM x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" ItemsSource="{Binding ViewLB}" Opacity="{Binding Opacity}"/>
        <TreeView Grid.Column="1" ItemsSource="{Binding ViewTV}"/>
      </Grid>
    </Window>

    Dazu der ViewModel:

    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
    
      public class Window89VM : INotifyPropertyChanged
      {
        public Window89VM()
        {
          List<string> listeLB = new List<string>();
          for (int i = 0; i < 10; i++) listeLB.Add($"Zeile {i}");
          cvsLB.Source = listeLB;
          cvsLB.View.CurrentChanged += async (sender, e) =>
          {
            Opacity = .5;
            cvsTV.Source = await System.Threading.Tasks.Task<string>.Run(() =>
            {
              List<string> listeTV = new List<string>();
              for (int i = 0; i < 10000; i++) listeTV.Add($"Knoten {i}");
              return listeTV;
            });
            OnPropertyChanged(nameof(ViewTV));
            Opacity = 1;
          };
        }
        CollectionViewSource cvsLB = new CollectionViewSource();
        CollectionViewSource cvsTV = new CollectionViewSource();
        public ICollectionView ViewLB { get { return cvsLB.View; } }
        public ICollectionView ViewTV { get { return cvsTV.View; } }
    
        private double _opacity = 1;
        public double Opacity
        {
          get { return _opacity; }
          set { _opacity = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks

    • Als Antwort vorgeschlagen Peter Fleischer Montag, 28. Januar 2019 13:25
    • Als Antwort markiert perlfred Dienstag, 29. Januar 2019 14:35
    Montag, 28. Januar 2019 13:22
  • Hallo Peter!

    Erst einmal Danke für dein ausführliches Beispiel!

    Mit MVVM und einer CollectionViewSource klappt es perfekt!!!

    Warum die Zuordnung der CollectionView.Source.Source erst wirklich mit Beendigung des Renderns der TreeViewItems abgeschlossen wird, weis ich zwar nicht, aber es funktioniert tadellos!

    Oder wird durch das OnPropertyChanged(nameof(ViewTV)) noch auf den Abschluss des Renderns gewartet?

    Auf jeden Fall noch einmal Danke!!! für deine Hilfe.

    Fred.

    Ich habe dein Beispiel mal mit einem Hierarchical-TreeView umgesetz:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApplication1
    {
        /// <summary>
        /// Interaktionslogik für Window1.xaml
        /// </summary>
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
    
            private void btnStart_Click(object sender, RoutedEventArgs e)
            {
                ((clDATEN)wnd.Resources["vm"]).aktuTV(sender, null);   //  TV aktualisieren
            }
        }
    
        public class clDATEN : INotifyPropertyChanged
        {
            public clDATEN() { }
    
            public async void aktuTV(object sender, EventArgs e)
            {
                Opacity = .5;                           // Eigenschaft Opacity (der Datenklasse) setzen
                cvsTV.Source = await System.Threading.Tasks.Task<List<TABELLEN>>.Run(() => getDaten());     // Daten asynchron abrufen
                OnPropertyChanged(nameof(ViewTV));      // Eigenschaftsänderung dem TreeView "mitteilen"
                Opacity = 1;                            // Eigenschaft Opacity (der Datenklasse) zurücksetzen
            }
    
            private List<TABELLEN> getDaten()
            {
                List<TABELLEN> liTabellen = new List<TABELLEN>();
                List<SPALTEN> liSpalten = new List<SPALTEN>() { new SPALTEN("Spalte A", "String"), new SPALTEN("Spalte B", "Numerisch") };
                for (int i = 0; i < 10000; i++) liTabellen.Add(new TABELLEN($"Tabelle {i}", liSpalten));
                return liTabellen;
            }
    
            CollectionViewSource cvsTV = new CollectionViewSource();
            public ICollectionView ViewTV { get { return cvsTV.View; } }
            private double _opacity = 1;
            public double Opacity { get { return _opacity; } set { _opacity = value; OnPropertyChanged(); } }
            public event PropertyChangedEventHandler PropertyChanged;
            internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    
        #region DB-Struktur
        public class TABELLEN
        {
            public TABELLEN() { }
    
            public TABELLEN(string Name, List<SPALTEN> liSpalten)
            {
                zNAME = Name; liSPALTEN = liSpalten;
            }
    
            public string zNAME { get; set; }
            public List<SPALTEN> liSPALTEN { get; set; }
        }
    
        public class SPALTEN
        {
            public SPALTEN() { }
    
            public SPALTEN(string zName, string zTyp)
            {
                zNAME = zName; zTYP = zTyp;
            }
    
            public string zNAME { get; set; }
            public string zTYP { get; set; }
        }
        #endregion
    
    }

    <Window x:Name="wnd" x:Class="WpfApplication1.Window1"
            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"
            xmlns:local="clr-namespace:WpfApplication1"
            mc:Ignorable="d"
            Title="Window1" Height="300" Width="300" >
        <Window.Resources>
            <local:clDATEN x:Key="vm"/>
        </Window.Resources>
        <Grid DataContext="{StaticResource vm}" Opacity="{Binding Opacity}">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TreeView ItemsSource="{Binding ViewTV}" Margin="10,5,10,0" >
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:TABELLEN}" ItemsSource="{Binding liSPALTEN}">
                        <DockPanel>
                            <Image DockPanel.Dock="Left" Source="table16.png" />
                            <TextBlock x:Name="tblTV_TABELLEN" Text="{Binding zNAME}" Foreground="Blue" FontWeight="Bold" Margin="3,0,0,0" />
                        </DockPanel>
                    </HierarchicalDataTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type local:SPALTEN}" ItemsSource="{Binding}">
                        <DockPanel>
                            <Image DockPanel.Dock="Left" Source="Column_Di16.png" Height="16" />
                            <StackPanel Orientation="Horizontal" Margin="5,0,0,0">
                                <TextBlock x:Name="tblTV_SPALTEN" Text="{Binding zNAME}" FontWeight="Bold" />
                                <TextBlock Text="{Binding zTYP}" Foreground="Gray" Margin="5,0,0,0"/>
                            </StackPanel>
                        </DockPanel>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
            <Button Grid.Row="1" Content="TV aktualisieren" Name="btnStart" Click="btnStart_Click" Margin="10" />
        </Grid>
    </Window>


    • Bearbeitet perlfred Dienstag, 29. Januar 2019 14:45
    Dienstag, 29. Januar 2019 14:34