none
У меня вопрос по MVP ! RRS feed

  • Вопрос

  • Нужно создать программу секундомер. Требуется выводить показания секундомера в окне. Окно имеет кнопки запуска, останова и сброса секундомера. Для реализации секундомера используется паттерн MVP

    У меня кнопка сброса не работает! Точнее сказать она все обнуляет , но далее таймер не запускается. Если кто знает объясните почему. Заранее спасибо!

     class Model
        {
           
            public int Start(ref int value)
            { return ++value; }
            public int Reset(ref int value)
            { return value=0; }
        }
    
     class Presenter
        {
    
            Model model = null;
            private MainWindow mainWindow = null;
    
            public Presenter(MainWindow mainWindow)
            {
                // TODO: Complete member initialization
                this.mainWindow = mainWindow;
                this.model = new Model();
    
                this.mainWindow.dispatcherTimer_2.Tick += new EventHandler(dispatcherTimer_Tick_2);
                this.mainWindow.dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            }
    
           
            int count = 0;
    
            private void dispatcherTimer_Tick(object sender, EventArgs e)
            {
               
                this.mainWindow.textBox1.Text = model.Start(ref count).ToString();
            }
            private void dispatcherTimer_Tick_2(object sender, EventArgs e)
            {
                this.mainWindow.textBox1.Text = model.Reset(ref count).ToString();
               // this.mainWindow.dispatcherTimer_2.Tick -= new EventHandler(dispatcherTimer_Tick_2);
               
     
            }
        }
    //view
     public partial class MainWindow : Window
        {
            public DispatcherTimer dispatcherTimer;
            public DispatcherTimer dispatcherTimer_2;
            public MainWindow()
            {
                InitializeComponent();
                dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
                dispatcherTimer_2 = new System.Windows.Threading.DispatcherTimer();
                new Presenter(this);
                dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
    
            }
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                dispatcherTimer.Start();
    
            }
    
            private void button3_Click(object sender, RoutedEventArgs e)
            {
                dispatcherTimer.Stop();
    
            }
    
            private void button2_Click(object sender, RoutedEventArgs e)
            {
                
                dispatcherTimer_2.Start();
    
            }
    
        }

    А это XAML код формы

    <Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="197" Width="410">
    <Grid>
    <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="41,100,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
    <Button Content="Reset" Height="23" HorizontalAlignment="Left" Margin="279,100,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" />
    <Button Content="Stop" Height="23" HorizontalAlignment="Left" Margin="164,100,0,0" Name="button3" VerticalAlignment="Top" Width="75" Click="button3_Click" />
    <TextBox Height="46" HorizontalAlignment="Left" Margin="97,29,0,0" Name="textBox1" VerticalAlignment="Top" Width="193" FontWeight="Bold" FontStyle="Italic" Opacity="10" />
    </Grid>
    </Window>


    • Изменено _espinosa 7 ноября 2012 г. 14:46
    7 ноября 2012 г. 14:45

Ответы

  • Object sender-содержит  ссылку на объект, формирующий событие и мы его приводим к  DispatcherTimer на котором вызываем м-д Stop().

    Да, именно так устроены события в C#, в них принято в качестве первого параметра передавать ссылку на объект вызвавший событие.

    Вопрос? Почему  он его зануляет а не останавливает?

     ((DispatcherTimer)sender).Stop();

    Не очень понял в чем вопрос, но идея такая. Если вы запускаете таймер, то он работает, работает и работает. Соответственно у вас работало два таймера, первый который писал 1,2,3,4... И второй, который срабаытвая чаще первого обнулял можель. Остановка второго таймера проблему решила.

    По поводу MVP!

     Есть небольшая статья : http://karatheodory.blogspot.com/2009/11/mvc-mvp.html (хотя там я этого не нашел )

    А разве Presenter не является связующим звеном между сущностью(model) и GUi(View)


    Да, презентер должен знать, но как правило это делается на уровне интрефейсов. Обычно есть интерфейс IView, в котором собержатся требования к поведению View. И Презентер работает с реальным View через ссылку типа интерфейс. Именно за счет этого достигается гибкость. Ведь у вас есть возможность написать другой класс View, который будет поддерживать тот же самый интерфейс IView и с ним также великолепно будет работать презентер. В таком случае, например, вы сможете модель и презентер вынести в отдельную dll и подключать ее в WPF и WinForm проект, в которых View будет разная и все будет работать великолепно. Сейчас, за счет высокой связности, вы не сможете презентер вынести в другой проект не изменяя его. А раз так, то вы не правильно реализуете MVP.

    P.s. Еще раз, настоятельно рекомендую, разбираться с MVVM, так как именно он рекомендован к применению в WPF приложениях.

    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 14:09
    8 ноября 2012 г. 3:48
    Отвечающий
  • Добрый день, коллега.

    У вас происходит следующее, по кнопке Reset запускается второй таймер, который без остановок сбрасывает состояние модели. Т.е. проблему можно решить его сотановкой. Например вот так:

    private void dispatcherTimer_Tick_2(object sender, EventArgs e)
    {
        this.mainWindow.textBox1.Text = model.Reset(ref count).ToString();
        ((DispatcherTimer)sender).Stop();
        // this.mainWindow.dispatcherTimer_2.Tick -= new EventHandler(dispatcherTimer_Tick_2);
    }

    Теперь, если позволите небольшой комментарий и совет.

    Все патерны, в том числе и MVP разрабатывались для того, чтобы структуировать код, т.е. уменьшить количество взаимосвязей между объектами (так называемая связность), сократить зоны визимости, упростить разработку. В вашем случае все эти благие цели не достигаются.

    Например, у вас отображение знает про презентатор:

    new Presenter(this);

    А с другой стороны, презентатор знает про отображение:

    this.mainWindow.dispatcherTimer_2.Tick += new EventHandler(dispatcherTimer_Tick_2);
    

    Именно, чтобы избавиться от таких кросзависимостей и разрабатывался данный паттерн.

    Ну и совет. Для WPF рекомендуется использовать паттерн не MVP, а MVVM.

    • Предложено в качестве ответа LXGDARKEditor 8 ноября 2012 г. 3:24
    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 14:09
    7 ноября 2012 г. 17:15
    Отвечающий

Все ответы

  • Добрый день, коллега.

    У вас происходит следующее, по кнопке Reset запускается второй таймер, который без остановок сбрасывает состояние модели. Т.е. проблему можно решить его сотановкой. Например вот так:

    private void dispatcherTimer_Tick_2(object sender, EventArgs e)
    {
        this.mainWindow.textBox1.Text = model.Reset(ref count).ToString();
        ((DispatcherTimer)sender).Stop();
        // this.mainWindow.dispatcherTimer_2.Tick -= new EventHandler(dispatcherTimer_Tick_2);
    }

    Теперь, если позволите небольшой комментарий и совет.

    Все патерны, в том числе и MVP разрабатывались для того, чтобы структуировать код, т.е. уменьшить количество взаимосвязей между объектами (так называемая связность), сократить зоны визимости, упростить разработку. В вашем случае все эти благие цели не достигаются.

    Например, у вас отображение знает про презентатор:

    new Presenter(this);

    А с другой стороны, презентатор знает про отображение:

    this.mainWindow.dispatcherTimer_2.Tick += new EventHandler(dispatcherTimer_Tick_2);
    

    Именно, чтобы избавиться от таких кросзависимостей и разрабатывался данный паттерн.

    Ну и совет. Для WPF рекомендуется использовать паттерн не MVP, а MVVM.

    • Предложено в качестве ответа LXGDARKEditor 8 ноября 2012 г. 3:24
    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 14:09
    7 ноября 2012 г. 17:15
    Отвечающий
  • Слава Богу!

    Большое спасибо!

    Объясни мне глупому как это работает! Object sender-содержит  ссылку на объект, формирующий событие и мы его приводим к  DispatcherTimer на котором вызываем м-д Stop().

    Вопрос? Почему  он его зануляет а не останавливает?

     ((DispatcherTimer)sender).Stop();
    По поводу MVP!

     Есть небольшая статья : http://karatheodory.blogspot.com/2009/11/mvc-mvp.html (хотя там я этого не нашел )

    А разве Presenter не является связующим звеном между сущностью(model) и GUi(View)

    7 ноября 2012 г. 19:46
  • Object sender-содержит  ссылку на объект, формирующий событие и мы его приводим к  DispatcherTimer на котором вызываем м-д Stop().

    Да, именно так устроены события в C#, в них принято в качестве первого параметра передавать ссылку на объект вызвавший событие.

    Вопрос? Почему  он его зануляет а не останавливает?

     ((DispatcherTimer)sender).Stop();

    Не очень понял в чем вопрос, но идея такая. Если вы запускаете таймер, то он работает, работает и работает. Соответственно у вас работало два таймера, первый который писал 1,2,3,4... И второй, который срабаытвая чаще первого обнулял можель. Остановка второго таймера проблему решила.

    По поводу MVP!

     Есть небольшая статья : http://karatheodory.blogspot.com/2009/11/mvc-mvp.html (хотя там я этого не нашел )

    А разве Presenter не является связующим звеном между сущностью(model) и GUi(View)


    Да, презентер должен знать, но как правило это делается на уровне интрефейсов. Обычно есть интерфейс IView, в котором собержатся требования к поведению View. И Презентер работает с реальным View через ссылку типа интерфейс. Именно за счет этого достигается гибкость. Ведь у вас есть возможность написать другой класс View, который будет поддерживать тот же самый интерфейс IView и с ним также великолепно будет работать презентер. В таком случае, например, вы сможете модель и презентер вынести в отдельную dll и подключать ее в WPF и WinForm проект, в которых View будет разная и все будет работать великолепно. Сейчас, за счет высокой связности, вы не сможете презентер вынести в другой проект не изменяя его. А раз так, то вы не правильно реализуете MVP.

    P.s. Еще раз, настоятельно рекомендую, разбираться с MVVM, так как именно он рекомендован к применению в WPF приложениях.

    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 14:09
    8 ноября 2012 г. 3:48
    Отвечающий
  • Слава Богу!

    Все понятно, спасибо!

    Если можно то еще маленький вопрос: как реализовать событие в этом примере через ключевые слова add  и remove? Чтобы подписка на событие проходила именно через  add?  ( просто для понимания )

     public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                new Presenter(this);
            }
    
            public event EventHandler myEvent = null;
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                myEvent.Invoke(sender, e);
            }
        }
    
     class Model
        {
            public string Logic(string s)
            {
                return string.Format("Работа: Model.Logic({0})", s);
            }
        }
    
     class Presenter
        {
            Model model = null;
            MainWindow mainWindow = null;
    
            public Presenter(MainWindow mainWindow)
            {
                this.model = new Model();
                this.mainWindow = mainWindow;
                this.mainWindow.myEvent += new EventHandler(mainWindow_myEvent);
            }
    
            void mainWindow_myEvent(object sender, System.EventArgs e)
            {
                string variable = model.Logic(this.mainWindow.textBox1.Text);
    
                this.mainWindow.textBox1.Text = variable;
            }
        }

    Код формы:

    <Window x:Class="MVP.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="204" Width="525">
    <Grid>
    <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="185,95,0,0" Name="button1" VerticalAlignment="Top" Width="120" Click="button1_Click" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="76,47,0,0" Name="textBox1" VerticalAlignment="Top" Width="338" />
    </Grid>
    </Window>

    8 ноября 2012 г. 13:01
  • Не очень понял что вы хотите сделать. Но посмотрите вот этот раздел MSDN. Может он вам чем поможет... Или переформулируйте вопрос.

    8 ноября 2012 г. 13:12
    Отвечающий
  •  public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                new Presenter(this);
            }
    
            public event EventHandler myEvent = null;
     public event EventHandler MyEvent
            {
                add { myEvent += value; }      
                remove { myEvent -= value; }   
            }
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                myEvent.Invoke(sender, e);
            }
        }
    
     class Model
        {
            public string Logic(string s)
            {
                return string.Format("Работа: Model.Logic({0})", s);
            }
        }
    
     class Presenter
        {
            Model model = null;
            MainWindow mainWindow = null;
    
            public Presenter(MainWindow mainWindow)
            {
                this.model = new Model();
                this.mainWindow = mainWindow;
                this.mainWindow.myEvent += new EventHandler(mainWindow_myEvent);
            }
    
            void mainWindow_myEvent(object sender, System.EventArgs e)
            {
                string variable = model.Logic(this.mainWindow.textBox1.Text);
    
                this.mainWindow.textBox1.Text = variable;
            }
        }

    public event EventHandler MyEvent
    {
      add { myEvent += value; }        //в этом  не прикрепляется  обработчик  событие 
    remove { myEvent -= value; }
      }

    Я попробывал создать другой (собственный делегат) вместо  EventHandler ,но там тоже не прикрепляется через add. Просто компилятор не заходит в эту часть кода т.е.  add  (невидимо для пользователя)автоматически создается компилятором при объявлении события. А как сделать чтобы он это явно сделал именно в этом "месте ".

    Это учебный пример! 

        


    • Изменено _espinosa 8 ноября 2012 г. 14:46
    8 ноября 2012 г. 14:34
  • Привет.

    Вот пример - Практическое руководство. Реализация пользовательских методов доступа к событиям (Руководство по программированию в C#):

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            MyClass cClass = new MyClass();
            // добавление события
            private void button1_Click(object sender, EventArgs e)
            {
                cClass.MyEvent += cClass_MyEvent;
            }
            // удаления события
            private void button2_Click(object sender, EventArgs e)
            {
                cClass.MyEvent -= cClass_MyEvent;
            }
            // зажигание события
            private void button3_Click(object sender, EventArgs e)
            {
                cClass.onFire();
            }
            // само событие
            void cClass_MyEvent(object sender, EventArgs e)
            {
                MessageBox.Show("fire");
            }
        }
        public class MyClass
        {
            private EventHandler eventHandler;
            public event EventHandler MyEvent
            {
                add
                {
                    System.Diagnostics.Debug.WriteLine("add handler");
                    eventHandler += value;
                }
                remove
                {
                    System.Diagnostics.Debug.WriteLine("remove handler");
                    eventHandler -= value;
                }
            }
            public void onFire()
            {
                EventHandler handler = eventHandler;
                if (handler != null)
                {
                    handler(this, new EventArgs());
                }
            }
        }


    Для связи [mail]

    21 ноября 2012 г. 10:11