Problématique pour adapter un bandeau défilant en WPF

Arriver à passer de Caliburn Micro vers Mvvm light...

a marqué ce sujet comme résolu.

Bonsoir ! J'ai hérité d'un projet plutôt complexe, celui de reproduire une application de type "DeskAlert" : écrire un message dans une interface web en définissant des critères (importance, destinataires…), pour qu'un terminal client affiche celui ci sous la forme choisie (popup ou bandeau défilant). Cette situation est d'autant plus complexe que je suis encore novice et que je n'ai pas de référent en dev sur ce projet (pas d'aide directe). Et en bonus pas de web sur mon poste de dev (rigolo quand l'extension est uniquement sous nuget). Malgré tout, je touche presque au but, en ayant développé un Webapp Mvc, un service WCF, une base SQL et un terminal WPF (environnement .NET donc).

Il y a une mécanique ou je suis réellement en difficulté. C'est de la gestion de la vue du bandeau défilant avec le terminal WPF. Je tente d'adapter une partie de ce projet : Ticker WPF pour qu'il puisse me lire mes données directement de mon service WCF plutôt que d'un flux RSS. Ce projet fonctionne avec le Framework Caliburn Micro, et je tente de l'adapter pour MVVM light, le framework que j'utilise.

Voici un extrait du ViewModel initial :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
        [ImportingConstructor]
        public MainViewModel()
        {
           manager.NewFeedItem += (o, e) => Execute.BeginOnUIThread(() => AddItem(e.Item));
           DTimer dtimer = new DTimer();
           dtimer.Start(10);
        }

        protected override void OnViewLoaded(object view)
        {
            ticker = new Ticker<TickerItemView>(((MainView)view).LayoutRoot);
            ticker.Speed = new TimeSpan(0, 1, 0);
            ticker.ItemDisplayed += ticker_ItemDisplayed;
            manager.Start();
            ticker.Start();
        }

-Le premier problème : Tout s’exécute dés le lancement du projet>affichage de la vue et démarrage du bandeau

-Le deuxième : Le Framework Caliburn detecte la vue ouverte et y place l'usercontrol servant de cadre au bandeau, et l'usercontrol servant d'objet de lecture pour le bandeau

Ce sont ces deux mécaniques je dois arriver à reproduire avec Mvvm light.

Ma tentative :

Première différence, je n'ai pas de vue de "base". C'est un timer qui va toute les 5 minutes, vérifier, si un message est disponible sur le serveur. Si c'est le cas, il récupère une liste, et va instancier la vue qu'il faut. Mon responsable veut que ce soit le terminal client qui aille chercher et pas le serveur qui envoie.

Extrait du Timer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
                if (service.IsAlertForMe(myIP) == true)
                {
                    MessageBox.Show("That for you !");
                    TickerLayout tick = new TickerLayout();
                    tb = App.Current.Resources["NotifyIcon"] as TaskbarIcon;
                    tb.ShowCustomBalloon(tick, PopupAnimation.Slide, 40000);
                }
                else
                {
                    MessageBox.Show("Rien pour toi !");
                }

L'objectif est ici d'afficher le Layout du bandeau et d'y charger l'objet servant à la lecture. C'est le soucis, je ne sais pas comment avec Mvvm light, charger un contrôle dans un contrôle. J'ai fais un test avec un relaycommand, mais je n'ai pas mon message, mon contrôle "TickerNormalView" ne se charge pas.

Extrait de mon ViewModel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
        private Ticker<TickerNormalView> ticker;
        private readonly IDataService _dataService;
        public RelayCommand TickerItemLoaded { get; private set; }

        private void OnTickerLoaded()
        {
            MessageBox.Show("TickerItem chargé !");
            TickerLayout layout = new TickerLayout();
            ticker = new Ticker<TickerNormalView>(layout.LayoutRoot);
            ticker.Speed = new TimeSpan(0, 1, 0);
            manager.Start();
            ticker.Start();
        }

        public TickerViewModel(IDataService dataService)
        {
            _dataService = dataService;
            TickerItemLoaded = new RelayCommand(() => OnTickerLoaded());
        }

J'ai une belle pelote, et je ne sais pas trop comment la dérouler. Je vais poser une question bête mais j'instancie donc deux fois mon objet TickerLayout (dans mon timer et dans mon viewmodel), j'imagine que cela n'est pas une bonne pratique.

J'ai besoin d’assistance pour savoir: -Revenir sous Caliburn -Tenter de fusionner Mvvm light et Caliburn(sachant que l'un et l'autre auront des actions bien distinctes) -Rester avec ma situation actuelle

J’espère que vous pourrez éclairer ma lanterne ! Cordialement.

+0 -0

J'ai tenté cette nouvelle approche. Dans mon Timer, lorsque celui ci Tick, j'ouvre une window

1
2
TickerView view = new TickerView();
view.Show();

Le ViewModel de mon Ticker

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    public class TickerViewModel : ViewModelBase 
    {
        private Ticker<TickerNormalView> ticker;
        private readonly FeedManager manager = new FeedManager();
        private readonly IDataService _dataService;
        public RelayCommand TickeViewLoaded { get; private set; }
        public RelayCommand TickerItemLoaded { get; private set; }

        private void OnItemLoaded()
        {
            MessageBox.Show("TickerItem chargé !");
        }

        private void OnTickerLoaded()
        {
            MessageBox.Show("TickerView chargé !");
            ticker = new Ticker<TickerNormalView>(new TickerLayout().LayoutRoot);
            ticker.Speed = new TimeSpan(0, 1, 0);
            manager.Start();
            ticker.Start();
        }

        private void AddItem(FeedItem itm)
        {
            ticker.Items.Enqueue(new TickerNormalView(itm));
        }

        public TickerViewModel(IDataService dataService)
        {
            _dataService = dataService;
            manager.NewFeedItem += (o, e) => Execute.BeginOnUIThread(() => AddItem(e.Item));
            TickeViewLoaded = new RelayCommand(() => OnTickerLoaded());
            TickerItemLoaded = new RelayCommand(() => OnItemLoaded());
        }
    }

J'ai donc deux messages qui doivent apparaître : le premier lorsque la window s'ouvre et celui ci fonctionne, et le deuxième lorsque la vue TickerNormalView fait défiler le texte. Or cette vue ne se charge pas.

Voici la classe du Ticker de l'appli originelle.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
          public class Ticker<T> where T : FrameworkElement
        {
            private readonly DispatcherTimer displayTimer = new DispatcherTimer();

            public EventHandler<ItemEventArgs<T>> ItemDisplayed;

            public bool IsRunning { get; set; }

            public void Stop()
            {
                displayTimer.Stop();
                IsRunning = false;
            }

            public void Start()
            {
                displayTimer.Start();
                displayTimer.Interval = new TimeSpan(0, 0, 0, 1);
                IsRunning = true;
            }

            public Ticker(Panel container)
            {
                SeperatorSize = 25;

                Container = container;

                Container.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                Container.Arrange(new Rect(Container.DesiredSize));

                Speed = new TimeSpan(0, 0, 0, 40);

                Items = new Queue<T>();

                displayTimer.Tick += displayTimer_Tick;
                displayTimer.Start();
                IsRunning = true;
            }

            public double SeperatorSize { get; set; }

            public TimeSpan Speed { get; set; }

            public Queue<T> Items { get; set; }
            public Panel Container { get; private set; }

            private void displayTimer_Tick(object sender, EventArgs e)
            {
                DisplayNextItem();
            }

            private void DisplayNextItem()
            {
                if (Items.Count == 0) return;

                T item = Items.Dequeue();

                Container.Children.Add(item);

                AnimateMove(item);

                if (ItemDisplayed != null) ItemDisplayed(this, new ItemEventArgs<T>(item));
            }

            private void AnimateMove(FrameworkElement e)
            {
                e.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                e.Arrange(new Rect(e.DesiredSize));

                double from = Container.ActualWidth;
                double to = -500;

                int unitsPerSec = Convert.ToInt32(Math.Abs(from - to) / Speed.TotalSeconds);
                int nextFire = Convert.ToInt32((e.ActualWidth + SeperatorSize) / unitsPerSec);

                displayTimer.Stop();
                displayTimer.Interval = new TimeSpan(0, 0, nextFire);
                displayTimer.Start();

                TaggedDoubleAnimation ani = new TaggedDoubleAnimation();
                ani.From = from;
                ani.To = to;
                ani.Duration = new Duration(Speed);
                ani.TargetElement = e;
                ani.Completed += ani_Completed;

                TranslateTransform trans = new TranslateTransform();
                e.RenderTransform = trans;

                trans.BeginAnimation(TranslateTransform.XProperty, ani, HandoffBehavior.Compose);
            }

            private void ani_Completed(object sender, EventArgs e)
            {
                Clock clock = (Clock)sender;
                TaggedDoubleAnimation ani = (TaggedDoubleAnimation)clock.Timeline;

                FrameworkElement element = ani.TargetElement;
                Container.Children.Remove(element);
            }
        }

Je serais très heureux de pouvoir comprendre cette mécanique !

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte