Можно было бы уже остановиться на достигнутом. Но бородатые дяди в очках все время придумывают какие-то серебрянные пули. Видите ли, уважаемый программист не будет писать кучу кода в форме, а будет использовать паттерн MVVM (Model-View-ViewModel). Не существует какого-то единственно правильного способа реализовать этот паттерн, поэтому предлагаю самый простейший вариант.
Нам понадобится интерфейс IVIew и базовый класс для ViewModel:
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
}
В нашем простом приложении будет две View и две ViewModel.
StrategyViewModel.cs
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();
}
}
StrategyView помечаем интерфейсом IView:
StrategyView.xaml.cs
public partial class StrategyView : UserControl, IView
{
public StrategyView()
{
InitializeComponent();
}
}
StrategyView.xaml
<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>
MainViewModel.cs
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);
}
}
}
MainWindow.xaml.cs
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();
}
}
MainWindow.xaml
<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>
Остается добавить к приложению стили и оно станет выглядеть как в статье “Создание роботов с помощью S#. Введение”.