none
DataTemplateの描画終了を検知する方法はありませんか? RRS feed

  • 質問

  • WPFを使用したグラフの画面を作成しています。

    DataTemplateを使用して、折れ線グラフを作成したのですが

    データ量が大量な場合、表示されるまでに結構な時間がかかってしまいます。

    そのため、描画処理が終わるまで処理中の画面を表示するようにしたいのですが

    DataTemplateの描画終了を検知する方法がわからなくて困っています。

    ソース内で描画する場合は、ContentRenderedやOnRender等で取得できるようなのですが

    DataTemplateによる描画終了はどのように検知すればよいのでしょうか

    2014年6月10日 2:14

回答

  • DataTemplateはあくまでもテンプレートであり、実体をもってはいません。
    実体が無いのだから、実体から発せられるイベントは当然存在しないので、、DataTemplateのイベントなどで検出するということはできません。

    DataTemplateをItemTemplateプロパティやContentTemplateプロパティなどに設定することで、DataTemplateを雛型にした実体が作られるのです。
    そのTemplateが適用された実体が描画されるときならばOnRenderでとらえることは一応は可能でしょう。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:app="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <StackPanel>
                <Button Click="Button_Click" Content="Test"/>
                <app:ContentPresenterEx Content="{Binding}">
                    <ContentPresenter.ContentTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FallbackValue=Test}" />
                        </DataTemplate>
                    </ContentPresenter.ContentTemplate>
                </app:ContentPresenterEx>
    
                <app:ListBoxEx ItemsSource="{Binding}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FallbackValue=Test2}" />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </app:ListBoxEx> 
            </StackPanel>
        </Grid>
    </Window>
    
    Class MainWindow 
    
        Private Sub UpdateDataContext()
            Dim list As New List(Of Integer)
            For i As Integer = 0 To 100
                list.Add(i)
            Next
            Me.DataContext = list
        End Sub
    
        Private Sub Button_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim act As Action = AddressOf Me.DispatcherEndRender
            Me.Dispatcher.BeginInvoke(act, Windows.Threading.DispatcherPriority.Loaded, Nothing)
    
            UpdateDataContext()
        End Sub
    
        Private Sub DispatcherEndRender()
            System.Diagnostics.Debug.Print("Rendered Dispatcher")
        End Sub
    End Class
    
    Class ContentPresenterEx
        Inherits ContentPresenter
    
    
        Protected Overrides Sub OnRender(drawingContext As System.Windows.Media.DrawingContext)
            MyBase.OnRender(drawingContext)
            System.Diagnostics.Debug.Print("Rendered ContentPresenter")
        End Sub
    End Class
    
    Class ListBoxEx
        Inherits ListBox
    
        Protected Overrides Function GetContainerForItemOverride() As System.Windows.DependencyObject
            Return New ListBoxItemEx()
        End Function
    End Class
    
    Class ListBoxItemEx
        Inherits ListBoxItem
    
        Protected Overrides Sub OnRender(drawingContext As System.Windows.Media.DrawingContext)
            MyBase.OnRender(drawingContext)
            System.Diagnostics.Debug.Print("ListBoxItem ContentPresenter")
        End Sub
    End Class

    Templateを実体化されるのはだいたい○○Presenterというクラス名がついてます。
    ListBoxItemにはPresenterが無いように見えますが、ListBoxItemのTemplateにContentPresenterが入っており、そこでDataTemplateが実体化されます。
    ListBoxItemのテンプレートを書き換えればContentPresenterに触れますが、面倒なので、ListBoxItemのRenderですませてます。

    グラフでどのようにDataTemplateを使うのか知りませんが、DataTemplateをどこかで実体化させているはずなので、その実体化させているものでイベントを捕まえることになるでしょう。

    まぁ、いろいろ面倒なことをやって個別に描画処理を検出しても面倒なだけだと思います。処理中の画面を消すのを同じDispatcherで行うのなら、他の描画が終わるのを待つことになるでしょうし。
    Dispatcher.Invoke/BeginInvokeで描画処理よりも優先順位が低いDispatcherPriority.Loadedを待つことで、すべての描画が終わるのを待った方が楽だと思います。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク WPF_Visiter 2014年6月11日 2:51
    2014年6月10日 12:54

すべての返信

  • DataTemplateはあくまでもテンプレートであり、実体をもってはいません。
    実体が無いのだから、実体から発せられるイベントは当然存在しないので、、DataTemplateのイベントなどで検出するということはできません。

    DataTemplateをItemTemplateプロパティやContentTemplateプロパティなどに設定することで、DataTemplateを雛型にした実体が作られるのです。
    そのTemplateが適用された実体が描画されるときならばOnRenderでとらえることは一応は可能でしょう。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:app="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <StackPanel>
                <Button Click="Button_Click" Content="Test"/>
                <app:ContentPresenterEx Content="{Binding}">
                    <ContentPresenter.ContentTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FallbackValue=Test}" />
                        </DataTemplate>
                    </ContentPresenter.ContentTemplate>
                </app:ContentPresenterEx>
    
                <app:ListBoxEx ItemsSource="{Binding}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FallbackValue=Test2}" />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </app:ListBoxEx> 
            </StackPanel>
        </Grid>
    </Window>
    
    Class MainWindow 
    
        Private Sub UpdateDataContext()
            Dim list As New List(Of Integer)
            For i As Integer = 0 To 100
                list.Add(i)
            Next
            Me.DataContext = list
        End Sub
    
        Private Sub Button_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim act As Action = AddressOf Me.DispatcherEndRender
            Me.Dispatcher.BeginInvoke(act, Windows.Threading.DispatcherPriority.Loaded, Nothing)
    
            UpdateDataContext()
        End Sub
    
        Private Sub DispatcherEndRender()
            System.Diagnostics.Debug.Print("Rendered Dispatcher")
        End Sub
    End Class
    
    Class ContentPresenterEx
        Inherits ContentPresenter
    
    
        Protected Overrides Sub OnRender(drawingContext As System.Windows.Media.DrawingContext)
            MyBase.OnRender(drawingContext)
            System.Diagnostics.Debug.Print("Rendered ContentPresenter")
        End Sub
    End Class
    
    Class ListBoxEx
        Inherits ListBox
    
        Protected Overrides Function GetContainerForItemOverride() As System.Windows.DependencyObject
            Return New ListBoxItemEx()
        End Function
    End Class
    
    Class ListBoxItemEx
        Inherits ListBoxItem
    
        Protected Overrides Sub OnRender(drawingContext As System.Windows.Media.DrawingContext)
            MyBase.OnRender(drawingContext)
            System.Diagnostics.Debug.Print("ListBoxItem ContentPresenter")
        End Sub
    End Class

    Templateを実体化されるのはだいたい○○Presenterというクラス名がついてます。
    ListBoxItemにはPresenterが無いように見えますが、ListBoxItemのTemplateにContentPresenterが入っており、そこでDataTemplateが実体化されます。
    ListBoxItemのテンプレートを書き換えればContentPresenterに触れますが、面倒なので、ListBoxItemのRenderですませてます。

    グラフでどのようにDataTemplateを使うのか知りませんが、DataTemplateをどこかで実体化させているはずなので、その実体化させているものでイベントを捕まえることになるでしょう。

    まぁ、いろいろ面倒なことをやって個別に描画処理を検出しても面倒なだけだと思います。処理中の画面を消すのを同じDispatcherで行うのなら、他の描画が終わるのを待つことになるでしょうし。
    Dispatcher.Invoke/BeginInvokeで描画処理よりも優先順位が低いDispatcherPriority.Loadedを待つことで、すべての描画が終わるのを待った方が楽だと思います。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク WPF_Visiter 2014年6月11日 2:51
    2014年6月10日 12:54
  • 確かに 複数のDataTemplateを使用しているので

    各描画をすべて待つようにするのは大変ですよね

    ありがとうございます。記載いただいたサンプルを元に処理中画面を実装します


    2014年6月11日 2:51