Создание роботов с помощью S#. Часть 5. Использование паттерна MVVM~/topic/355/sozdanie-robotov-s-pomoshshyu-s_-chast-5_-ispolzovanie-patterna-mvvm/Copyright @ StockSharp Platform LLC 2010 - 20242024-03-29T09:12:16Zhttps://stocksharp.ru/images/logo.pnghttps://stocksharp.ru/posts/m/137/Можно было бы уже остановиться на достигнутом. Но бородатые дяди в очках все время придумывают какие...2012-03-18T21:59:03Z2012-12-17T15:06:40ZFinDirectorhttps://stocksharp.ru/users/473/info@stocksharp.ruМожно было бы уже остановиться на достигнутом. Но бородатые дяди в очках все время придумывают какие-то серебрянные пули. Видите ли, уважаемый программист не будет писать кучу кода в форме, а будет использовать паттерн MVVM (Model-View-ViewModel). Не существует какого-то единственно правильного способа реализовать этот паттерн, поэтому предлагаю самый простейший вариант.<br />Нам понадобится интерфейс IVIew и базовый класс для ViewModel:<br /><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
public interface IView
{
object DataContext { get; set; }
}
public abstract class ViewModel : INotifyPropertyChanged
{
public IView View { get; protected set; }
protected ViewModel() { }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}</pre>
</div></div><br /><br />В нашем простом приложении будет две View и две ViewModel.<br /><b>StrategyViewModel.cs</b><br /><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
class StrategyViewModel : ViewModel
{
public StandardStrategy Strategy { get; private set; }
public DelegateCommand StartCommand { get; private set; }
public DelegateCommand StopCommand { get; private set; }
public DelegateCommand RunTerminalCommand { get; private set; }
public StrategyViewModel(StandardStrategy strategy)
{
this.Strategy = strategy;
this.StartCommand = new DelegateCommand(OnStartExecuted, CanStart);
this.StopCommand = new DelegateCommand(OnStopExecuted, CanStop);
this.RunTerminalCommand = new DelegateCommand(RunTerminal);
this.Strategy.ProcessStateChanged += s =>
{
this.StartCommand.RaiseCanExecuteChanged();
this.StopCommand.RaiseCanExecuteChanged();
};
this.View = new StrategyView();
this.View.DataContext = this;
}
public bool CanStart()
{
return Strategy.ProcessState == ProcessStates.Stopped;
}
public void OnStartExecuted()
{
if (!CanStart())
return;
Task.Factory.StartNew(Strategy.Start);
}
public bool CanStop()
{
return Strategy.ProcessState == ProcessStates.Started;
}
public void OnStopExecuted()
{
if (!CanStop())
return;
MessageBoxResult result = MessageBox.Show(
String.Format("Вы уверены, что хотите остановить стратегию {0}?", Strategy.Name),
"Подтверждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
Strategy.Stop();
}
public void RunTerminal()
{
MessageBoxResult result = MessageBox.Show(
String.Format("Вы уверены, что хотите запустить терминал {0}?", Strategy.TraderBuilder.Title),
"Подтверждение", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
Strategy.TraderBuilder.RunTerminal();
}
}</pre>
</div></div><br /><br />StrategyView помечаем интерфейсом IView:<br /><br /><b>StrategyView.xaml.cs</b><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
public partial class StrategyView : UserControl, IView
{
public StrategyView()
{
InitializeComponent();
}
}</pre>
</div></div><br /><br /><b>StrategyView.xaml</b><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:xml">
<UserControl.Resources>
<Style x:Key="stValue" TargetType="FrameworkElement">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</UserControl.Resources>
<StackPanel Margin="3">
<StackPanel Margin="5">
<TextBlock Text="{Binding Strategy.Name}" FontSize="14" />
<TextBlock Text="{Binding Strategy.Description}" FontSize="9" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Терминал: " Grid.Row="0" Grid.Column="0" />
<Button Grid.Row="0" Grid.Column="1"
HorizontalAlignment="Right"
Content="{Binding Strategy.TraderBuilder.Title}"
Command="{Binding Path=RunTerminalCommand}" />
<TextBlock Text="Портфель: " Grid.Row="1" Grid.Column="0" />
<TextBlock Text="{Binding Strategy.PortfolioSelector.Title}" Grid.Row="1" Grid.Column="1" Style="{StaticResource stValue}"/>
<TextBlock Text="Инструмент: " Grid.Row="2" Grid.Column="0" />
<TextBlock Text="{Binding Strategy.SecuritySelector.Title}" Grid.Row="2" Grid.Column="1" Style="{StaticResource stValue}"/>
<TextBlock Text="Состояние: " Grid.Row="3" Grid.Column="0" />
<TextBlock Text="{Binding Strategy.ProcessState}" Grid.Row="3" Grid.Column="1" Style="{StaticResource stValue}"/>
<TextBlock Text="Прибыль: " Grid.Row="4" Grid.Column="0"/>
<TextBlock Text="{Binding Strategy.PnLManager.PnL}" Grid.Row="4" Grid.Column="1" Style="{StaticResource stValue}"/>
<TextBlock Text="Позиция: " Grid.Row="5" Grid.Column="0"/>
<TextBlock Text="{Binding Strategy.PositionManager.Position}" Grid.Row="5" Grid.Column="1" Style="{StaticResource stValue}"/>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0" HorizontalAlignment="Center">
<Button x:Name="btnStartStrategy" Content="Старт" Command="{Binding Path=StartCommand}" Margin="2,0,0,0" />
<Button x:Name="btnStopStrategy" Content="Стоп" Command="{Binding Path=StopCommand}" Margin="2,0,0,0" />
</StackPanel>
</StackPanel>
</StackPanel></pre>
</div></div><br /><br /><b>MainViewModel.cs</b><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
class MainViewModel : IDisposable
{
public ObservableCollection<StrategyViewModel> Strategies { get; private set; }
public ILogListener LogView { get; private set; }
private LogManager LogManager;
public MainViewModel()
{
Strategies = new ObservableCollection<StrategyViewModel>();
CreateLoggers();
}
private void CreateLoggers()
{
LogManager = new LogManager();
LogView = new StockSharp.Xaml.Monitor();
LogManager.Listeners.Add(LogView);
var fileListener = new FileLogListener(
string.Format(CultureInfo.InvariantCulture, "{0:yyyyMMdd}.txt", DateTime.Today));
LogManager.Listeners.Add(fileListener);
if (!string.IsNullOrEmpty(Settings.GoogleLogin))
{
var smsListener = new SmsLogListener(Settings.GoogleLogin, Settings.GooglePassword);
smsListener.Filters.Add(LogListener.AllErrorFilter);
LogManager.Listeners.Add(smsListener);
}
}
public void Dispose()
{
if (LogManager != null)
LogManager.Dispose();
if (Strategies != null)
{
foreach (ITraderBuilder traderBuilder in Strategies.Select(s => s.Strategy.TraderBuilder).Distinct())
{
if (traderBuilder != null)
traderBuilder.Dispose();
}
}
}
public void LoadStrategies()
{
StrategyLoader strategyLoader = StrategyLoader.Load("Strategies.config");
Strategies.Clear();
foreach (StandardStrategy strategy in strategyLoader.Strategies)
{
LogManager.Sources.Add(strategy);
StrategyViewModel strategyModel = new StrategyViewModel(strategy);
Strategies.Add(strategyModel);
}
foreach (ILogSource logSource in strategyLoader.Strategies
.Select(s => s.TraderBuilder).OfType<ILogSource>().Distinct())
{
LogManager.Sources.Add(logSource);
}
}
}</pre>
</div></div><br /><br /><b>MainWindow.xaml.cs</b><br /><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
public partial class MainWindow : Window
{
private MainViewModel viewModel;
public MainWindow()
{
InitializeComponent();
this.Loaded += Window_Loaded;
this.viewModel = new MainViewModel();
this.DataContext = viewModel;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
viewModel.LoadStrategies();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
MessageBoxResult result = MessageBox.Show(
"Вы уверены, что хотите завершить работу?",
"Подтверждение", MessageBoxButton.OKCancel);
if (result != MessageBoxResult.OK)
e.Cancel = true;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
if (viewModel != null)
viewModel.Dispose();
}
}</pre>
</div></div><br /><br /><b>MainWindow.xaml</b><br /><div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:xml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Path=Strategies}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ></WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding View}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<ContentControl Grid.Row="1" Content="{Binding LogView}" />
</Grid></pre>
</div></div><br /><br />Остается добавить к приложению стили и оно станет выглядеть как в статье <a href="http://stocksharp.com/algo/article.aspx?aid=4" title="http://stocksharp.com/algo/article.aspx?aid=4">“Создание роботов с помощью S#. Введение”</a>.Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/22746/Автор использовал using Microsoft.Practices.Prism.Commands; http://compositewpf.codeplex.com/release...2012-11-30T17:41:08Z2012-11-30T17:41:08ZVassilSanychhttps://stocksharp.ru/users/6491/info@stocksharp.ruАвтор использовал<br />using Microsoft.Practices.Prism.Commands;<br /><a target="_blank" rel="nofollow" href="https://stocksharp.ru/away/?u=AQAAAAAAAAB1DJoDnRq2AZhPhA8paAWwWEdY6U83fWhkgqk6pCLA8xquyhMXLMnH5TcGAr9puVxQ0zi9Tt5gs6k7bn1IeVu9" title="http://compositewpf.codeplex.com/releases/view/95815">http://compositewpf.codeplex.com/releases/view/95815</a>Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20435/По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет. То, что хорошо при напис...2012-07-19T10:44:37Z2012-07-19T10:44:37ZVassilSanychhttps://stocksharp.ru/users/6491/info@stocksharp.ru<div class="quote"><span class="quotetitle">anothar <a href="https://stocksharp.ru/posts/m/20354/"><img src="https://stocksharp.ru/images/icon_latest_reply.gif" title="Перейти" alt="Перейти" /></a></span><div class="innerquote"><div class="quote"><span class="quotetitle">Цитата:</span><div class="innerquote">По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет.</div></div><br />То, что хорошо при написании калькулятора, может не подойти при написании реального приложения.<br /></div></div><br /> В "реальном приложении" обычно бывает так:<br /> - архитектор/ведущий разработчик/etc накрутил 4 звена, в каждом по 5 "правильных" слоёв (действительно правильных со стройной архитектурой),<br /> - документацию по архитектуре ему писать либо лень, либо "потом напишу", либо она просто устаревает намного быстрее полёта его мысли,<br /> - в команде 10-20 разработчиков,<br /> - все более-менее в курсе зон ответственности звеньев (архитектор за этим старается следить, да VS не даст использовать зависимости чужого звена), но не особо разбираются в идеях зон ответственности слоёв<br /> - соответственно при кодировании зоны ответственности слоёв нарушаются, в проекте хренова туча кода - за каждым не уследишь (при том, что всё замечательно работает и проходит все тесты)<br /> - на выходе имеем жесточайший бардак в проекте -> проблемы с рефакторингом -> медленная и мучительная смерть проекта<br /><br />"Сделай настолько просто, насколько это возможно, но не проще." Эйнштейн <br /><br />PS<br />Я видел это в коде бизнес-приложения Microsoft, например. Кстати это тоже было интеграционное решение, как и StockSharp. C похожими проблемами политики качества (Для обеспечения регрессионного тестирования таких приложений нужно примерно в 3 раза больше ресурсов, чем собственно для создания приложения. Причём ресурсов как минимум такого же качества).<br />Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20354/По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет. Вот пример ответа на sta...2012-07-14T10:04:44Z2012-07-14T10:04:44Zanotharhttps://stocksharp.ru/users/6089/info@stocksharp.ru<div class="quote"><span class="quotetitle">Цитата:</span><div class="innerquote">По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет.</div></div><br />Вот пример ответа на stackoverflow.com:<a target="_blank" rel="nofollow" href="https://stocksharp.ru/away/?u=AQAAAAAAAADaUgqeUiMvqHjAqLN3xr_cYQ12WhxUIL45Q--L0kVVWtwTzMtuvQm-j2KCmtlRyAaXFXqdah6UrkBHc7sMiyp9" title="http://stackoverflow.com/questions/2798447/is-mvvm-pointless">wpf mvvm</a>. Любой паттерн служит определенной цели и модели построения приложений. Нужно его применять там, где он будет востребован и приносить пользу.<br /><div class="quote"><span class="quotetitle">Цитата:</span><div class="innerquote">А цель программирования, как известно, - борьба со сложностью. </div></div><br />Сложности бывают как известно разные. То, что хорошо при написании калькулятора, может не подойти при написании реального приложения.<br />Хорошо бы еще раскрыть в статье использование каких-то известных фреймворков типа Prism, Caliburn и т.д.<br />Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20350/ По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет. Потому что WPF и так ор...2012-07-13T16:01:45Z2012-07-13T16:01:45Zra81https://stocksharp.ru/users/16581/info@stocksharp.ru<div class="quote"><span class="quotetitle">VassilSanych <a href="https://stocksharp.ru/posts/m/20348/"><img src="https://stocksharp.ru/images/icon_latest_reply.gif" title="Перейти" alt="Перейти" /></a></span><div class="innerquote"><br />По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет. Потому что WPF и так организован по принципу MVVM.<br /></div></div><br />Если системы делаются в виде отдельных библиотек включающих код и отображение, без паттерна не обойтись. Интеграция библиотеки в общую оболочку может встать дорого :). Я тоже реализовал себе сей паттерн. Меня устраиват полностью.<br />Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20349/Сама стратегия оформляется в виде библиотеки. Кстати. Никто не пробовал csscript? 2012-07-13T15:43:32Z2012-07-13T15:43:32ZVassilSanychhttps://stocksharp.ru/users/6491/info@stocksharp.ru<div class="quote"><span class="quotetitle">ra81 <a href="https://stocksharp.ru/posts/m/20074/"><img src="https://stocksharp.ru/images/icon_latest_reply.gif" title="Перейти" alt="Перейти" /></a></span><div class="innerquote">Сама стратегия оформляется в виде библиотеки. </div></div><br />Кстати.<br />Никто не пробовал <a target="_blank" rel="nofollow" href="https://stocksharp.ru/away/?u=AQAAAAAAAAC_lj18XuWpJ9BHq-ZJkHTgAcYye_lXsUTzSY1UzRa7lA" title="http://www.csscript.net/">csscript</a>?<br /><br />Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20348/Но вообще-то это была шутка (хотя в контексте статьи шуткой и не является). Сейчас объясню почему. П...2012-07-13T15:23:42Z2012-07-13T15:39:34ZVassilSanychhttps://stocksharp.ru/users/6491/info@stocksharp.ruНо вообще-то это была шутка (хотя в контексте статьи шуткой и не является).<br />Сейчас объясню почему.<br />По моему скромному мнению, накручивать ViewModel в WPF большого смысла нет. Потому что WPF и так организован по принципу MVVM.<br />Где behind code является фактически этим самым ViewModel (в отличии от форм WinForms), потому что он уже слабо отвечает за отображение формы (должен слабо отвечать), а больше отвечает за её поведение. А в процессе отображения уже участвует не только behind code, но и куча сервисов собственно WPF.<br />В общем в контексте WPF: view - это XAML, viewModel - это behind code, а model - это собственно модель.<br />Накручивать свои архитектурные нахлобучки на WPF конечно можно ("Любую проблему можно решить введением дополнительного уровня абстракции, кроме проблемы слишком большого количества уровней абстракции" :) )<br />Но, как показывает практика, это обычно усложняет систему и увеличивает трудоёмкость (даже в очень крупных системах). А цель программирования, как известно, - борьба со сложностью. <br />;)Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20347/Где, где у нас new MainViewModel()? В MainWindow : Window? WinForms Hell detected :) MVVM и рядом не...2012-07-13T14:18:23Z2012-07-13T14:18:23ZVassilSanychhttps://stocksharp.ru/users/6491/info@stocksharp.ruГде, где у нас <div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
new MainViewModel()</pre>
</div></div>?<br />В <div class="code"><strong>Код</strong><div class="innercode"><pre class="brush:csharp">
MainWindow : Window</pre>
</div></div>?<br />WinForms Hell detected :)<br />MVVM и рядом не стояло. Даже на MVP не тянет :)Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20082/Спасибо за ответ. Все просто и наглядно. 2012-06-30T14:59:07Z2012-06-30T14:59:07ZAlexLan73https://stocksharp.ru/users/6005/info@stocksharp.ruСпасибо за ответ.<br />Все просто и наглядно.<br />Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20074/Замечательные примеры. Большое Вам спасибо. А Вы можете сделать пример - вывод индикаторов RSI, MACD...2012-06-30T08:00:02Z2012-06-30T08:00:02Zra81https://stocksharp.ru/users/16581/info@stocksharp.ru<div class="quote"><span class="quotetitle">AlexLan73 <a href="https://stocksharp.ru/posts/m/20068/"><img src="https://stocksharp.ru/images/icon_latest_reply.gif" title="Перейти" alt="Перейти" /></a></span><div class="innerquote">Замечательные примеры.<br />Большое Вам спасибо.<br />А Вы можете сделать пример - вывод индикаторов RSI, MACD ... в новом окне?<br /></div></div><br />Я сделал несколько иначе. Сама стратегия оформляется в виде библиотеки. Библиотека подключается в общую систему, в ней же лежит визуализацию самой стратегии. В том числе и кнопка и график.<br /><br />По сути система это коробка, куда пихаются кубики.Copyright @ StockSharp Platform LLC 2010 - 2024https://stocksharp.ru/posts/m/20068/Замечательные примеры. Большое Вам спасибо. А Вы можете сделать пример - вывод индикаторов RSI, MACD...2012-06-29T17:56:48Z2012-06-29T17:56:48ZAlexLan73https://stocksharp.ru/users/6005/info@stocksharp.ruЗамечательные примеры.<br />Большое Вам спасибо.<br />А Вы можете сделать пример - вывод индикаторов RSI, MACD ... в новом окне?<br />Copyright @ StockSharp Platform LLC 2010 - 2024