IMI docs

Trace: mvvm

Mvvm

This is an old revision of the document!


MVVM Pattern - Реалізація патерна —- Лейбли and Binding в XAML (в XAML забіндити можна все що завгодно!)

Foreground="Red"                                             Міняє колір вмісту
FontSize="50"                                                Величена вмісту
FontFamily="Segoe UI"                                        Які шрифти будуть використані
HorizontalContentAlignment="Center"                          Вірівнення по ширині
VerticalContentAlignment="Center"                            Вірівнення по висоті
Content="{Binding Time}"                                     Бінд який падає в клас StopwatchViewModel  з інтерфейсом  INotifyPropertyChanged
Content="{Binding Time, TargetNullValue='00.00 UAH'}"        TargetNullValue виводить введений текст зразу на екра 
Command="{Binding StartStopwatch}"                           Біндить якусь дію (в нашому випадку це кнопка)
<Label Content="{Binding Time}" Foreground="Red" FontFamily="Segoe UI" FontSize="50" VerticalContentAlignment="Center" ></Label>
<Label Margin="{Binding Xyi}"  FontFamily="Segoe UI" FontSize="50" HorizontalContentAlignment="Center"></Label>

xaml.cs (грубо кажучи це просто main метод!)

namespace CounterSalary
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var context = new StopwatchViewModel();
            var timer = new TimerMonitore(context);
            timer.startstopWatch();
            DataContext = context;
        }
    }
}
  • Любі класи в XAML використовується тільки для виклику (НЕ РЕАЛІЗАЦІЇ ЛОГІКИ)
  • DataContext потрібен для того щоб достукатись до МОДЕЛЕЙ класу StopwatchViewModel з інтерфейсом INotifyPropertyChanged (Дефолтна штука, якось працює під капотом)
  • timer реалізовує TimerMonitor який приймає конструктор класу StopwatchViewModel
    • При виклику timer.startstopWatch(); запускає DispatcherTimer і Stopwatch (тому що в конструкторі НЕ ПРИПУСТИМО робити запуск програми)

Реалізація класу ButtonCommand через інтерфейс ICommand

namespace CounterSalary.ViewModel
{
    public class ButtonCommand : ICommand
    {
        private readonly Action action;
        public event EventHandler CanExecuteChanged;

        public ButtonCommand(Action action)
        {
            this.action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            action();
        }

Робиться для того щоб потім можна було його реалізувати через моделі


Даємо івенти на nameof - будь це хоч Margin хоч Context (Це один з методів реалізування Біндінгів)

delegate { } - пустая функция, але може бути і не пустою, завдяки неї з варки мажна зробити метод
namespace CounterSalary.ViewModel
{
     public class StopwatchViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged=delegate { };
        string _count;
        string _count1;

        public string Time {
            get { return _count; }
            set { _count = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Time)));
            }
        }

        public string Xyi {
            get { return _count1; }
            set { _count1 = value;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Xyi)));
            }
        }
    }
}

Якщо ви імплементуєте інтерфейс INotifyPropertyChanged, то ви маєте реалізувати event PropertyChanged, і відправити його кожен раз, коли значення міняється.


Рівень сервісу (потрібен для реалізації логіки)

namespace CounterSalary.Service
{
    public class TimerMonitore
    {
        private Stopwatch stopWatch = new Stopwatch();
        private DispatcherTimer dt = new DispatcherTimer();
        private TimeSpan ts;
        private StopwatchViewModel _model;
        
        public TimerMonitore(StopwatchViewModel model)
        {
            dt.Tick += new EventHandler(dtTicker); 
            _model = model;
        }

        private void dtTicker(object sender, EventArgs e)
        {
            ts = stopWatch.Elapsed;
            string currentTime = String.Format("{0:00}.{1:00}", ts.TotalSeconds, ts.Milliseconds / 10);
            _model.Time = currentTime;
            _model.Xyi="400";
        }

        public void startstopWatch()
        {
            dt.Start();
            stopWatch.Start();
        }
    }
}

Цей клас реалізовує логіку таймера, а саме В конструкторі створюється івент на таймер _model.Time = currentTime Заходить в INotifyPropertyChanged і сетить нове значення рівне currentTime - і таким чином в лейблі міняється значення

Пояснення коду нище!👇

DispatcherTimer dt = new DispatcherTimer(); //Створення нового екземпляра DispatcherTimer 
dt.Interval = TimeSpan.FromSeconds(1); //Буде використовувати івент який нище прив*язаний 1 раз в секунду

dt.Tick += dtTicker; //Підписатись на івент
dt.Tick -= dtTicker;  //Відписатись від івенту
dt.Tick += new EventHandler(dtTicker); //Ідентичний пропис

dt.Start(); //Стартує івент в багатопоточносі 
stopWatch.Start(); //Стартує раніше обявлений Stopwatch (Секундомір)

private Stopwatch stopWatch = new Stopwatch(); //Створює новий екземпляр секундоміра
TimeSpan ts = stopWatch.Elapsed; //Cтворює секундомір

//0:ХХ Підписатись на перший івент - ts.TotalSeconds
//1:ХХ Підписатись на перший івент - ts.Milliseconds 
//ts.TotalSeconds Абсолютний час - іде дальше чим 60 секунд
//ts.Seconds - на 60 секунді скидується до 0
string currentTime = String.Format("{0:00}.{1:00}", ts.TotalSeconds, ts.Milliseconds / 10);