none
Désactiver le raffaichissement d'un control en WPF RRS feed

  • Question

  • Bonjour,
    Je souhaite désactiver le rafraichissement d'un control en WPF. Cela peut sembler assez louche mais je vais expliquer pourquoi. Si d'autres solutions sont possibles, n'hésitez bien sûr pas à éclairer ma lanterne :P.

    Je travaille actuellement sur un projet WPF qui utilise quelques notions de DirectX. Pour des raisons professionnelles (travaille fait en amont), je ne peux pas utiliser un D3DImage, comme il aurait été plus simple. Je dois afficher une image DirectX sur une partie de l'interface en récupérant son Handle. Ceci fonctionne très bien et l'image s'affiche sans soucis. Aucun problème d'interaction avec les élément d'entrées.
    L'élément affiché doit se modifier avec le temps. En WPF, n'existant pas de OnIdle(), je suis passé par plusieurs solutions pour effectuer une boucle fluide. Ces solutions sont : Dipatcher.BeginInvok, BackGroundWorker, DispatcherTimer. Ces solutions fonctionnent plus ou moins bien mais fonctionnent. Mais le problème revient toujours, d'où, en désépoir de cause, cette question.
    Voici pour le contexte, voilà le problème.
    L'image est affiché par dessus une zone de la fenetre. Ce qu'il y a derrière importe peu (c'est juste un canvas avec un background coloré pour dire qu'il y a quelque chose). En temps normal la boucle est bonne et l'image est fluide. Par contre, il arrive que la fenetre ai besoin de se rafraichir et l'image clignote, et on voit le canvas avec son background. Elle clignote car le rendue est prioritaire sur l'affichage de l'image. Si j'utilise le Dispatcher (Le delegate rappelle la ligne du dispatcher.begininvoke) avec une priorité supérieur à input, c'est alors l'image qui est prioritaire mais le rendu est freezé, c'est à dire que je ne peux plus intéragir avec lui. J'ai tenté le backgroundworker pour avoir la même priorité que le rendu de la fenêtre mais je l'ai peut-être raté. Ou alors, puis-je récupérer un evenement fin de rendu ?

    Merci d'avance, et désolé si cela peut sembler flou, j'ai essayé d'être le plus clair possible avec le peu que je peux en dire :P
    vendredi 27 novembre 2009 09:22

Réponses

  • Je pense à une solution, mais je ne suis pas sûr qu'elle fonctionne avec une surface de rendu directX. A voir.

    Le problème avec WPF, c'est que quand on n'arrive pas à faire quelque chose, c'est souvent qu'on s'y prend de la mauvaise façon. Je parle d'expérience ;)

    Quelque chose qui peut aider c'est d'utiliser InteropBitmap http://msdn.microsoft.com/en-us/library/system.windows.interop.interopbitmap.aspx

    La doc MSDN n'est pas très locace sur le sujet, mais il s'agit d'un bitmap dont on alloue la mémoire soi-même. On associe la source d'un control Image à un InteropBitmap. On peut modifier ce bitmap autant qu'on veut en écrivant dans la mémoire du bitmap. Et pour que WPF mette à jour l'image à l'écran, il suffit d'appeler une méthode magique de InteropBitmap : Invalidate() !

    Facile à priori.

    On peut par exemple écrire un player vidéo en WPF de cette façon, en recopiant chaque frame de la vidéo dans la zone mémoire du bitmap InteropBitmap, puis en appelant Invalidate() 25 fois par seconde pour que l'image soit affichée à l'écran, dans l'image control. Ca marche !

    Ainsi il n'y a pas besoin d'afficher quelque chose "par dessus" la fenêtre WPF, avec tous les problèmes que cela peut comporter.

    J'avais lu un article à propos d'InteropBitmap : http://blogs.microsoft.co.il/blogs/tamir/archive/2008/03/02/how-to-high-performance-graphics-in-wpf.aspx

    Voici comment ça se passe :
    - on alloue de la mémoire pour l'image (4 octets RGBA par pixel de l'image)
    - on appelle System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection pour créer l'objet InteropBitmap à partir de l'espace mémoire contenant les pixels du bitmap
    - On associe l'objet InteropBitmap à la propriété Source d'un controle System.Windows.Control.Image.
    - On écrit dans la mémoire pour modifier les pixels du bitmap, puis on appelle InteropBitmap.Invalidate() pour que WPF mette à jour le bitmap à l'écran. On peut faire ça plusieurs fois par seconde.

    Concernant plus spécifiquement la surface DirectX, il faudrait voir si l'on peut créer un InteropBitmap directement à partir d'un pointeur sur la zone mémoire de la surface DirectX. Ou alors, recopier la surface DirectX dans la zone mémoire de l'InteropBitmap chaque fois que l'animation DirectX change. Pour la recopie, il faut bien sûr que la copie soit rapide (C++, ou Marshal.Copy).

    Pierre
    mardi 1 décembre 2009 12:36

Toutes les réponses

  • Il me semble que j'ai déjà rencontré ce genre de problème en WPF, mais je ne suis pas bien sûr d'avoir tout compris.

    Est-ce que le problème est le suivant :
    - Afficher un bitmap dans un contrôle quelconque
    - ce bitmap est modifié en temp réel et doit être rafraichi à l'écran
    - le rafraichissement du bitmap ne doit pas entrer en conflit avec le système de rafraissiment des contrôles et fenêtres de WPF, par exemple lors de changement de taille de la fenêtre.

    Le cas où j'ai rencontré ce problème c'était pour l'écriture d'une appli qui applique des filtres sur une image, avec un changement progressif des paramètres du filtre, d'où une modification en temps réel de l'image affichée.

    Est-ce que le problème est similaire ? Si oui je peux détailler la solution que j'ai utilisée.

    Pierre
    vendredi 27 novembre 2009 14:33
  • Bonjour et merci de ta aide.
    Oui, le problème est similaire, sauf que je n'affiche pas une bitmap mais une zone de rendu directX. Et il y a bien conflit entre cette zone de rendue par-dessus un control (l'affichage est forcé) et le rendu de la fenêtre. Le problème arrive quand le fenêtre se redessine entièrement (j'utilise WPF Performance Suite pour suivre le rafraichissement).
    Le problème ne se situe aps seulement au changement de taille. Ici, c'est un simple mouseover sur un onglet (avec un template) qui rafraichi la fenêtre.

    Je peux plus détailler au besoin la partie affichage forcé par dessus le control.
    Fabrice
    vendredi 27 novembre 2009 17:13
  • Je pense à une solution, mais je ne suis pas sûr qu'elle fonctionne avec une surface de rendu directX. A voir.

    Le problème avec WPF, c'est que quand on n'arrive pas à faire quelque chose, c'est souvent qu'on s'y prend de la mauvaise façon. Je parle d'expérience ;)

    Quelque chose qui peut aider c'est d'utiliser InteropBitmap http://msdn.microsoft.com/en-us/library/system.windows.interop.interopbitmap.aspx

    La doc MSDN n'est pas très locace sur le sujet, mais il s'agit d'un bitmap dont on alloue la mémoire soi-même. On associe la source d'un control Image à un InteropBitmap. On peut modifier ce bitmap autant qu'on veut en écrivant dans la mémoire du bitmap. Et pour que WPF mette à jour l'image à l'écran, il suffit d'appeler une méthode magique de InteropBitmap : Invalidate() !

    Facile à priori.

    On peut par exemple écrire un player vidéo en WPF de cette façon, en recopiant chaque frame de la vidéo dans la zone mémoire du bitmap InteropBitmap, puis en appelant Invalidate() 25 fois par seconde pour que l'image soit affichée à l'écran, dans l'image control. Ca marche !

    Ainsi il n'y a pas besoin d'afficher quelque chose "par dessus" la fenêtre WPF, avec tous les problèmes que cela peut comporter.

    J'avais lu un article à propos d'InteropBitmap : http://blogs.microsoft.co.il/blogs/tamir/archive/2008/03/02/how-to-high-performance-graphics-in-wpf.aspx

    Voici comment ça se passe :
    - on alloue de la mémoire pour l'image (4 octets RGBA par pixel de l'image)
    - on appelle System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection pour créer l'objet InteropBitmap à partir de l'espace mémoire contenant les pixels du bitmap
    - On associe l'objet InteropBitmap à la propriété Source d'un controle System.Windows.Control.Image.
    - On écrit dans la mémoire pour modifier les pixels du bitmap, puis on appelle InteropBitmap.Invalidate() pour que WPF mette à jour le bitmap à l'écran. On peut faire ça plusieurs fois par seconde.

    Concernant plus spécifiquement la surface DirectX, il faudrait voir si l'on peut créer un InteropBitmap directement à partir d'un pointeur sur la zone mémoire de la surface DirectX. Ou alors, recopier la surface DirectX dans la zone mémoire de l'InteropBitmap chaque fois que l'animation DirectX change. Pour la recopie, il faut bien sûr que la copie soit rapide (C++, ou Marshal.Copy).

    Pierre
    mardi 1 décembre 2009 12:36
  • Merci de ta réponse.
    En fait, vu que c'est du code que j'ai repris et que je ne peux pas vraiment modifier, je suis bloqué avec ce que j'ai, cad recouvrir un control via un handle. J'aurais personnellement utilisé un D3DImage qui, comme le InteropBitmap, sert pour l'interop avec une surface DirectX à laquelle on transfère une texture. Je pense que le choix de retoucher ceci viendra de ma hiérarchie quand ils s'appercevront que c'est pas très pratique.
    Je garde ta solution sous le coude pour leur proposer plusieurs directions le cas échéant.

    J'ai pour l'instant mis le problème de coté en supprimant l'interaction en question.

    Fabrice
    mardi 1 décembre 2009 14:47