Inizializzazione progetto WPF .NET 8.0
- Aggiunto il file di progetto `Amaltea.csproj` per un'applicazione WPF .NET 8.0 con supporto per `Nullable` e `ImplicitUsings`. - Creata la soluzione `Amaltea.sln` con configurazioni `Debug` e `Release`. - Aggiunti `App.xaml` e `App.xaml.cs` per la configurazione globale e il punto di ingresso dell'applicazione. - Creata la finestra principale `MainWindow.xaml` con navigazione, barra di stato e contenuto dinamico. - Implementata logica di connessione simulata alle API di Betfair in `MainWindow.xaml.cs`. - Aggiunti convertitori `BlockTypeToBrushConverter` e `NullToVisibilityConverter`. - Creati `ViewModel` per blocchi strategici e editor di strategie (`StrategyBlockViewModel`, `StrategyEditorViewModel`). - Aggiunti `StrategyEditorView.xaml` e `StrategyEditorView.xaml.cs` per l'editor di strategie con supporto drag-and-drop e salvataggio in JSON. - Definiti colori, pennelli e stili globali per uniformare l'interfaccia. - Popolato l'editor di strategie con dati demo e connessioni di esempio.
This commit is contained in:
11
Amaltea/Amaltea.csproj
Normal file
11
Amaltea/Amaltea.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
25
Amaltea/Amaltea.sln
Normal file
25
Amaltea/Amaltea.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36429.23 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amaltea", "Amaltea.csproj", "{11514D9A-D918-4E19-84CA-0D72FFA124B1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{11514D9A-D918-4E19-84CA-0D72FFA124B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{11514D9A-D918-4E19-84CA-0D72FFA124B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{11514D9A-D918-4E19-84CA-0D72FFA124B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{11514D9A-D918-4E19-84CA-0D72FFA124B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {02F963B3-CBB5-49DB-9A2B-3182AE54069A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
29
Amaltea/Amaltea/Converters/BlockTypeToBrushConverter.cs
Normal file
29
Amaltea/Amaltea/Converters/BlockTypeToBrushConverter.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using Amaltea.ViewModels;
|
||||
|
||||
namespace Amaltea.Converters;
|
||||
|
||||
public class BlockTypeToBrushConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is StrategyBlockType type)
|
||||
{
|
||||
var resourceKey = type switch
|
||||
{
|
||||
StrategyBlockType.Trigger => "Brush.Block.Trigger",
|
||||
StrategyBlockType.Condition => "Brush.Block.Condition",
|
||||
StrategyBlockType.Action => "Brush.Block.Action",
|
||||
StrategyBlockType.Risk => "Brush.Block.Risk",
|
||||
_ => "Brush.Block.Trigger"
|
||||
};
|
||||
return App.Current.TryFindResource(resourceKey) as Brush;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
|
||||
}
|
||||
21
Amaltea/Amaltea/Converters/NullToVisibilityConverter.cs
Normal file
21
Amaltea/Amaltea/Converters/NullToVisibilityConverter.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Amaltea.Converters;
|
||||
|
||||
public class NullToVisibilityConverter : IValueConverter
|
||||
{
|
||||
// If parameter == "Invert" then returns Visible when value is null.
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
bool invert = string.Equals(parameter as string, "Invert", StringComparison.OrdinalIgnoreCase);
|
||||
bool isNull = value is null;
|
||||
if (invert)
|
||||
return isNull ? Visibility.Visible : Visibility.Collapsed;
|
||||
return isNull ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException();
|
||||
}
|
||||
20
Amaltea/Amaltea/ViewModels/BaseViewModel.cs
Normal file
20
Amaltea/Amaltea/ViewModels/BaseViewModel.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Amaltea.ViewModels;
|
||||
|
||||
public abstract class BaseViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (Equals(storage, value)) return false;
|
||||
storage = value;
|
||||
RaisePropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
44
Amaltea/Amaltea/ViewModels/StrategyBlockViewModel.cs
Normal file
44
Amaltea/Amaltea/ViewModels/StrategyBlockViewModel.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Amaltea.ViewModels;
|
||||
|
||||
public enum StrategyBlockType
|
||||
{
|
||||
Trigger,
|
||||
Condition,
|
||||
Action,
|
||||
Risk
|
||||
}
|
||||
|
||||
public class StrategyBlockViewModel : BaseViewModel
|
||||
{
|
||||
private double _x;
|
||||
private double _y;
|
||||
private string _title = string.Empty;
|
||||
private string? _subtitle;
|
||||
private StrategyBlockType _blockType;
|
||||
private bool _isSelected;
|
||||
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
|
||||
public double X { get => _x; set => SetProperty(ref _x, value); }
|
||||
public double Y { get => _y; set => SetProperty(ref _y, value); }
|
||||
|
||||
public string Title { get => _title; set => SetProperty(ref _title, value); }
|
||||
public string? Subtitle { get => _subtitle; set => SetProperty(ref _subtitle, value); }
|
||||
|
||||
public StrategyBlockType BlockType { get => _blockType; set { if (SetProperty(ref _blockType, value)) RaisePropertyChanged(nameof(BlockBrushKey)); } }
|
||||
|
||||
public bool IsSelected { get => _isSelected; set => SetProperty(ref _isSelected, value); }
|
||||
|
||||
public string BlockBrushKey => BlockType switch
|
||||
{
|
||||
StrategyBlockType.Trigger => "Brush.Block.Trigger",
|
||||
StrategyBlockType.Condition => "Brush.Block.Condition",
|
||||
StrategyBlockType.Action => "Brush.Block.Action",
|
||||
StrategyBlockType.Risk => "Brush.Block.Risk",
|
||||
_ => "Brush.Block.Trigger"
|
||||
};
|
||||
}
|
||||
151
Amaltea/Amaltea/ViewModels/StrategyEditorViewModel.cs
Normal file
151
Amaltea/Amaltea/ViewModels/StrategyEditorViewModel.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Amaltea.ViewModels;
|
||||
|
||||
public class StrategyEditorViewModel : BaseViewModel
|
||||
{
|
||||
private StrategyBlockViewModel? _selectedBlock;
|
||||
private readonly ObservableCollection<StrategyBlockViewModel> _selectedBlocks = new();
|
||||
|
||||
public ObservableCollection<StrategyBlockViewModel> Blocks { get; } = new();
|
||||
public ObservableCollection<StrategyConnectionViewModel> Connections { get; } = new();
|
||||
|
||||
public IEnumerable<StrategyBlockViewModel> SelectedBlocks => _selectedBlocks;
|
||||
|
||||
public StrategyBlockViewModel? SelectedBlock
|
||||
{
|
||||
get => _selectedBlock;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedBlock, value))
|
||||
{
|
||||
_selectedBlocks.Clear();
|
||||
if (value != null)
|
||||
{
|
||||
value.IsSelected = true;
|
||||
_selectedBlocks.Add(value);
|
||||
}
|
||||
RaisePropertyChanged(nameof(SelectedBlocks));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StrategyEditorViewModel()
|
||||
{
|
||||
// Seed with demo blocks
|
||||
var b1 = new StrategyBlockViewModel { Title = "Event Trigger", Subtitle = "In-Play Start", X = 60, Y = 60, BlockType = StrategyBlockType.Trigger };
|
||||
var b2 = new StrategyBlockViewModel { Title = "Condition: Odds", Subtitle = "Back Odds < 3.0", X = 260, Y = 160, BlockType = StrategyBlockType.Condition };
|
||||
var b3 = new StrategyBlockViewModel { Title = "Action: Back", Subtitle = "Stake 10 EUR", X = 520, Y = 180, BlockType = StrategyBlockType.Action };
|
||||
var b4 = new StrategyBlockViewModel { Title = "Risk Mgmt", Subtitle = "Stop Loss 5%", X = 760, Y = 260, BlockType = StrategyBlockType.Risk };
|
||||
|
||||
Blocks.Add(b1);
|
||||
Blocks.Add(b2);
|
||||
Blocks.Add(b3);
|
||||
Blocks.Add(b4);
|
||||
|
||||
Connections.Add(new StrategyConnectionViewModel(b1.Id, b2.Id));
|
||||
Connections.Add(new StrategyConnectionViewModel(b2.Id, b3.Id));
|
||||
Connections.Add(new StrategyConnectionViewModel(b3.Id, b4.Id));
|
||||
}
|
||||
|
||||
public StrategyBlockViewModel AddBlock(StrategyBlockType type, string title, string subtitle, double x, double y)
|
||||
{
|
||||
var block = new StrategyBlockViewModel { BlockType = type, Title = title, Subtitle = subtitle, X = x, Y = y };
|
||||
Blocks.Add(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
public void AddConnection(StrategyBlockViewModel from, StrategyBlockViewModel to)
|
||||
{
|
||||
if (from == to) return;
|
||||
if (Connections.Any(c => c.From == from.Id && c.To == to.Id)) return;
|
||||
Connections.Add(new StrategyConnectionViewModel(from.Id, to.Id));
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
foreach (var b in _selectedBlocks) b.IsSelected = false;
|
||||
_selectedBlocks.Clear();
|
||||
_selectedBlock = null;
|
||||
RaisePropertyChanged(nameof(SelectedBlocks));
|
||||
RaisePropertyChanged(nameof(SelectedBlock));
|
||||
}
|
||||
|
||||
public void ToggleSelection(StrategyBlockViewModel block, bool add)
|
||||
{
|
||||
if (!add)
|
||||
{
|
||||
ClearSelection();
|
||||
block.IsSelected = true;
|
||||
_selectedBlocks.Add(block);
|
||||
_selectedBlock = block;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_selectedBlocks.Contains(block))
|
||||
{
|
||||
block.IsSelected = false;
|
||||
_selectedBlocks.Remove(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
block.IsSelected = true;
|
||||
_selectedBlocks.Add(block);
|
||||
}
|
||||
_selectedBlock = _selectedBlocks.LastOrDefault();
|
||||
}
|
||||
RaisePropertyChanged(nameof(SelectedBlocks));
|
||||
RaisePropertyChanged(nameof(SelectedBlock));
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var dto = new StrategyGraphDto
|
||||
{
|
||||
Blocks = Blocks.Select(b => new StrategyBlockDto
|
||||
{
|
||||
Id = b.Id,
|
||||
X = b.X,
|
||||
Y = b.Y,
|
||||
Title = b.Title,
|
||||
Subtitle = b.Subtitle,
|
||||
BlockType = b.BlockType
|
||||
}).ToList(),
|
||||
Connections = Connections.Select(c => new StrategyConnectionDto { From = c.From, To = c.To }).ToList()
|
||||
};
|
||||
return JsonSerializer.Serialize(dto, new JsonSerializerOptions { WriteIndented = true });
|
||||
}
|
||||
|
||||
public void SaveToFile(string path)
|
||||
{
|
||||
File.WriteAllText(path, Serialize());
|
||||
}
|
||||
}
|
||||
|
||||
public record StrategyConnectionViewModel(System.Guid From, System.Guid To);
|
||||
|
||||
public class StrategyGraphDto
|
||||
{
|
||||
public List<StrategyBlockDto> Blocks { get; set; } = new();
|
||||
public List<StrategyConnectionDto> Connections { get; set; } = new();
|
||||
}
|
||||
|
||||
public class StrategyBlockDto
|
||||
{
|
||||
public System.Guid Id { get; set; }
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
public StrategyBlockType BlockType { get; set; }
|
||||
}
|
||||
|
||||
public class StrategyConnectionDto
|
||||
{
|
||||
public System.Guid From { get; set; }
|
||||
public System.Guid To { get; set; }
|
||||
}
|
||||
110
Amaltea/Amaltea/Views/StrategyEditorView.xaml
Normal file
110
Amaltea/Amaltea/Views/StrategyEditorView.xaml
Normal file
@@ -0,0 +1,110 @@
|
||||
<UserControl x:Class="Amaltea.Views.StrategyEditorView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:Amaltea.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600" d:DesignWidth="1000">
|
||||
<UserControl.Resources>
|
||||
<DataTemplate x:Key="StrategyBlockTemplate" DataType="{x:Type vm:StrategyBlockViewModel}">
|
||||
<Border CornerRadius="6" Padding="10 6" BorderThickness="1"
|
||||
Background="#262B31"
|
||||
BorderBrush="{Binding BlockType, Converter={StaticResource BlockTypeToBrushConverter}}"
|
||||
Tag="Block" MouseLeftButtonDown="Block_MouseLeftButtonDown" MouseMove="Block_MouseMove" MouseLeftButtonUp="Block_MouseLeftButtonUp">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Title}" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding Subtitle}" FontSize="11" Foreground="{StaticResource Brush.TextSecondary}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="170"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="320"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Workspace Area -->
|
||||
<Border Grid.Row="0" Grid.Column="0" Margin="0 0 8 8" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="1" Padding="0">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="#1D1F23">
|
||||
<Canvas x:Name="WorkspaceCanvas" MinWidth="1200" MinHeight="800" Background="#1D1F23" MouseLeftButtonDown="WorkspaceCanvas_MouseLeftButtonDown">
|
||||
<!-- Connections Layer -->
|
||||
<ItemsControl ItemsSource="{Binding Connections}" IsHitTestVisible="False">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:StrategyConnectionViewModel}">
|
||||
<Path StrokeThickness="2" Stroke="#3A8DFF" Opacity="0.8">
|
||||
<Path.Data>
|
||||
<PathGeometry>
|
||||
<PathFigure x:Name="pf" StartPoint="0,0"/>
|
||||
</PathGeometry>
|
||||
</Path.Data>
|
||||
</Path>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Blocks -->
|
||||
<ItemsControl ItemsSource="{Binding Blocks}" ItemTemplate="{StaticResource StrategyBlockTemplate}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style TargetType="ContentPresenter">
|
||||
<Setter Property="Canvas.Left" Value="{Binding X}"/>
|
||||
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
|
||||
</Style>
|
||||
</ItemsControl.ItemContainerStyle>
|
||||
</ItemsControl>
|
||||
</Canvas>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<!-- Components Library (Right) -->
|
||||
<Border Grid.Row="0" Grid.Column="1" Margin="0 0 0 8" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="1" Padding="12">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Components" FontWeight="Bold" FontSize="16" Margin="0 0 0 12"/>
|
||||
<Button Content="Add Trigger" Style="{StaticResource BaseButtonStyle}" Margin="0 4 0 4" Click="AddTrigger_Click"/>
|
||||
<Button Content="Add Condition" Style="{StaticResource BaseButtonStyle}" Margin="0 4 0 4" Click="AddCondition_Click"/>
|
||||
<Button Content="Add Action" Style="{StaticResource BaseButtonStyle}" Margin="0 4 0 4" Click="AddAction_Click"/>
|
||||
<Button Content="Add Risk" Style="{StaticResource BaseButtonStyle}" Margin="0 4 0 4" Click="AddRisk_Click"/>
|
||||
<Separator Margin="0 10 0 10"/>
|
||||
<Button Content="Save Strategy (JSON)" Style="{StaticResource AccentButtonStyle}" Click="SaveStrategy_Click"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Properties Panel (Bottom) -->
|
||||
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="1" Padding="16">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="320"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Selected Block" FontWeight="Bold" FontSize="16"/>
|
||||
<TextBlock Text="None" Foreground="{StaticResource Brush.TextSecondary}" FontStyle="Italic" Visibility="{Binding SelectedBlock, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Invert}"/>
|
||||
<StackPanel Margin="0 8 0 0" Visibility="{Binding SelectedBlock, Converter={StaticResource NullToVisibilityConverter}}">
|
||||
<TextBlock Text="Title" FontSize="12" Foreground="{StaticResource Brush.TextSecondary}"/>
|
||||
<TextBox Text="{Binding SelectedBlock.Title, UpdateSourceTrigger=PropertyChanged}" Margin="0 2 0 8"/>
|
||||
<TextBlock Text="Subtitle" FontSize="12" Foreground="{StaticResource Brush.TextSecondary}"/>
|
||||
<TextBox Text="{Binding SelectedBlock.Subtitle, UpdateSourceTrigger=PropertyChanged}" Margin="0 2 0 8"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="(Future) Connections / Advanced Settings" FontSize="12" Foreground="{StaticResource Brush.TextSecondary}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
95
Amaltea/Amaltea/Views/StrategyEditorView.xaml.cs
Normal file
95
Amaltea/Amaltea/Views/StrategyEditorView.xaml.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Amaltea.ViewModels;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Amaltea.Views
|
||||
{
|
||||
public partial class StrategyEditorView : UserControl
|
||||
{
|
||||
private StrategyEditorViewModel Vm => (StrategyEditorViewModel)DataContext;
|
||||
private bool _dragging;
|
||||
private StrategyBlockViewModel? _dragBlock;
|
||||
private Point _dragStartPos;
|
||||
private Point _blockStartPos;
|
||||
private StrategyBlockViewModel? _pendingConnectionFrom;
|
||||
|
||||
public StrategyEditorView()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new StrategyEditorViewModel();
|
||||
}
|
||||
|
||||
private void AddTrigger_Click(object sender, RoutedEventArgs e) => Vm.AddBlock(StrategyBlockType.Trigger, "Event Trigger", "Custom", 80, 80);
|
||||
private void AddCondition_Click(object sender, RoutedEventArgs e) => Vm.AddBlock(StrategyBlockType.Condition, "Condition", "Edit...", 120, 120);
|
||||
private void AddAction_Click(object sender, RoutedEventArgs e) => Vm.AddBlock(StrategyBlockType.Action, "Action", "Edit...", 160, 160);
|
||||
private void AddRisk_Click(object sender, RoutedEventArgs e) => Vm.AddBlock(StrategyBlockType.Risk, "Risk", "Edit...", 200, 200);
|
||||
|
||||
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not Border border || border.DataContext is not StrategyBlockViewModel block) return;
|
||||
|
||||
bool ctrl = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
|
||||
|
||||
if (_pendingConnectionFrom != null && _pendingConnectionFrom != block && ctrl)
|
||||
{
|
||||
Vm.AddConnection(_pendingConnectionFrom, block);
|
||||
_pendingConnectionFrom = null;
|
||||
}
|
||||
else if (ctrl)
|
||||
{
|
||||
_pendingConnectionFrom = block;
|
||||
}
|
||||
|
||||
Vm.ToggleSelection(block, ctrl);
|
||||
|
||||
_dragging = true;
|
||||
_dragBlock = block;
|
||||
_dragStartPos = e.GetPosition(WorkspaceCanvas);
|
||||
_blockStartPos = new Point(block.X, block.Y);
|
||||
border.CaptureMouse();
|
||||
}
|
||||
|
||||
private void Block_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (!_dragging || _dragBlock == null) return;
|
||||
var current = e.GetPosition(WorkspaceCanvas);
|
||||
var delta = current - _dragStartPos;
|
||||
_dragBlock.X = _blockStartPos.X + delta.X;
|
||||
_dragBlock.Y = _blockStartPos.Y + delta.Y;
|
||||
}
|
||||
|
||||
private void Block_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is not Border border) return;
|
||||
_dragging = false;
|
||||
_dragBlock = null;
|
||||
border.ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
private void WorkspaceCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.Source == WorkspaceCanvas)
|
||||
{
|
||||
Vm.ClearSelection();
|
||||
_pendingConnectionFrom = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveStrategy_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dialog = new SaveFileDialog
|
||||
{
|
||||
Filter = "Strategy JSON (*.json)|*.json",
|
||||
FileName = "strategy.json"
|
||||
};
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
Vm.SaveToFile(dialog.FileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Amaltea/App.xaml
Normal file
162
Amaltea/App.xaml
Normal file
@@ -0,0 +1,162 @@
|
||||
<Application x:Class="Amaltea.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Amaltea"
|
||||
xmlns:conv="clr-namespace:Amaltea.Converters"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<!-- Converters -->
|
||||
<conv:BlockTypeToBrushConverter x:Key="BlockTypeToBrushConverter"/>
|
||||
<conv:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
|
||||
|
||||
<!-- Color & Brush Resources -->
|
||||
<Color x:Key="Color.Background.Window">#1E1F23</Color>
|
||||
<Color x:Key="Color.Background.Panel">#24262B</Color>
|
||||
<Color x:Key="Color.Background.PanelAlt">#2C2F33</Color>
|
||||
<Color x:Key="Color.Border.Light">#3A3D43</Color>
|
||||
<Color x:Key="Color.Accent">#2E8CFF</Color>
|
||||
<Color x:Key="Color.Accent.Hover">#4FA2FF</Color>
|
||||
<Color x:Key="Color.Text.Primary">#FFFFFF</Color>
|
||||
<Color x:Key="Color.Text.Secondary">#B0B4BA</Color>
|
||||
<Color x:Key="Color.Status.Ok">#2ECC71</Color>
|
||||
<Color x:Key="Color.Status.Warn">#E5C457</Color>
|
||||
<Color x:Key="Color.Status.Error">#E55252</Color>
|
||||
<!-- Strategy block category colors -->
|
||||
<Color x:Key="Color.Block.Trigger">#2475D8</Color>
|
||||
<Color x:Key="Color.Block.Condition">#8E44AD</Color>
|
||||
<Color x:Key="Color.Block.Action">#2ECC71</Color>
|
||||
<Color x:Key="Color.Block.Risk">#E67E22</Color>
|
||||
|
||||
<SolidColorBrush x:Key="Brush.WindowBackground" Color="{StaticResource Color.Background.Window}"/>
|
||||
<SolidColorBrush x:Key="Brush.PanelBackground" Color="{StaticResource Color.Background.Panel}"/>
|
||||
<SolidColorBrush x:Key="Brush.PanelAltBackground" Color="{StaticResource Color.Background.PanelAlt}"/>
|
||||
<SolidColorBrush x:Key="Brush.BorderLight" Color="{StaticResource Color.Border.Light}"/>
|
||||
<SolidColorBrush x:Key="Brush.Accent" Color="{StaticResource Color.Accent}"/>
|
||||
<SolidColorBrush x:Key="Brush.AccentHover" Color="{StaticResource Color.Accent.Hover}"/>
|
||||
<SolidColorBrush x:Key="Brush.TextPrimary" Color="{StaticResource Color.Text.Primary}"/>
|
||||
<SolidColorBrush x:Key="Brush.TextSecondary" Color="{StaticResource Color.Text.Secondary}"/>
|
||||
<SolidColorBrush x:Key="Brush.Status.Ok" Color="{StaticResource Color.Status.Ok}"/>
|
||||
<SolidColorBrush x:Key="Brush.Status.Warn" Color="{StaticResource Color.Status.Warn}"/>
|
||||
<SolidColorBrush x:Key="Brush.Status.Error" Color="{StaticResource Color.Status.Error}"/>
|
||||
<SolidColorBrush x:Key="Brush.Block.Trigger" Color="{StaticResource Color.Block.Trigger}"/>
|
||||
<SolidColorBrush x:Key="Brush.Block.Condition" Color="{StaticResource Color.Block.Condition}"/>
|
||||
<SolidColorBrush x:Key="Brush.Block.Action" Color="{StaticResource Color.Block.Action}"/>
|
||||
<SolidColorBrush x:Key="Brush.Block.Risk" Color="{StaticResource Color.Block.Risk}"/>
|
||||
|
||||
<!-- Global Window Style -->
|
||||
<Style TargetType="Window">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.WindowBackground}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.TextPrimary}"/>
|
||||
<Setter Property="FontFamily" Value="Segoe UI"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
</Style>
|
||||
|
||||
<!-- ScrollViewer default (hide horizontal) -->
|
||||
<Style TargetType="ScrollViewer">
|
||||
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||
</Style>
|
||||
|
||||
<!-- Base Button Style -->
|
||||
<Style x:Key="BaseButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.PanelAltBackground}"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.TextPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.BorderLight}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="10 6"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="4">
|
||||
<ContentPresenter Margin="2" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#33373E"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#3E434B"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5"/>
|
||||
<Setter Property="Cursor" Value="Arrow"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Navigation Button Style -->
|
||||
<Style x:Key="NavButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
|
||||
<Setter Property="Margin" Value="0,2,0,2"/>
|
||||
<Setter Property="Padding" Value="14 10"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||
</Style>
|
||||
|
||||
<!-- Accent Button Style (e.g., Connect) -->
|
||||
<Style x:Key="AccentButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.Accent}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Accent}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource Brush.AccentHover}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.AccentHover}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#1F6BBF"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Status Text Style -->
|
||||
<Style x:Key="StatusTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource Brush.TextSecondary}"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Strategy Block Base Style -->
|
||||
<Style x:Key="StrategyBlockBaseStyle" TargetType="Border">
|
||||
<Setter Property="CornerRadius" Value="6"/>
|
||||
<Setter Property="Padding" Value="12 8"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.BorderLight}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Background" Value="{StaticResource Brush.PanelAltBackground}"/>
|
||||
<Setter Property="Effect">
|
||||
<Setter.Value>
|
||||
<DropShadowEffect BlurRadius="8" ShadowDepth="0" Opacity="0.25" Color="#000000"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Accent}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style x:Key="StrategyBlockTriggerStyle" TargetType="Border" BasedOn="{StaticResource StrategyBlockBaseStyle}">
|
||||
<Setter Property="Background" Value="#202A36"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Block.Trigger}"/>
|
||||
</Style>
|
||||
<Style x:Key="StrategyBlockConditionStyle" TargetType="Border" BasedOn="{StaticResource StrategyBlockBaseStyle}">
|
||||
<Setter Property="Background" Value="#252130"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Block.Condition}"/>
|
||||
</Style>
|
||||
<Style x:Key="StrategyBlockActionStyle" TargetType="Border" BasedOn="{StaticResource StrategyBlockBaseStyle}">
|
||||
<Setter Property="Background" Value="#1F2D24"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Block.Action}"/>
|
||||
</Style>
|
||||
<Style x:Key="StrategyBlockRiskStyle" TargetType="Border" BasedOn="{StaticResource StrategyBlockBaseStyle}">
|
||||
<Setter Property="Background" Value="#30271F"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource Brush.Block.Risk}"/>
|
||||
</Style>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
14
Amaltea/App.xaml.cs
Normal file
14
Amaltea/App.xaml.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace Amaltea
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
10
Amaltea/AssemblyInfo.cs
Normal file
10
Amaltea/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
85
Amaltea/MainWindow.xaml
Normal file
85
Amaltea/MainWindow.xaml
Normal file
@@ -0,0 +1,85 @@
|
||||
<Window x:Class="Amaltea.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Amaltea"
|
||||
mc:Ignorable="d"
|
||||
Title="StrategyBet Desktop" Height="800" Width="1200" MinWidth="960" MinHeight="600" WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="220"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left Navigation Panel -->
|
||||
<Border Grid.Column="0" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="0,0,1,0">
|
||||
<DockPanel>
|
||||
<!-- Logo / Title -->
|
||||
<StackPanel DockPanel.Dock="Top" Margin="12 16 12 24">
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 0 12" VerticalAlignment="Center">
|
||||
<Image Source="pack://application:,,,/Resources/logo.png" Width="28" Height="28" Margin="0,0,8,0"/>
|
||||
<TextBlock Text="StrategyBet" FontSize="18" FontWeight="Bold"/>
|
||||
</StackPanel>
|
||||
<Button x:Name="ConnectButton" Content="Connect" Style="{StaticResource AccentButtonStyle}" Click="ConnectButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Navigation Buttons -->
|
||||
<ScrollViewer DockPanel.Dock="Top">
|
||||
<StackPanel x:Name="NavStackPanel" Margin="12 0 12 12">
|
||||
<Button Style="{StaticResource NavButtonStyle}" Content="Dashboard" Tag="Dashboard" Click="NavButton_Click"/>
|
||||
<Button Style="{StaticResource NavButtonStyle}" Content="Strategy Editor" Tag="StrategyEditor" Click="NavButton_Click"/>
|
||||
<Button Style="{StaticResource NavButtonStyle}" Content="Backtesting" Tag="Backtesting" Click="NavButton_Click"/>
|
||||
<Button Style="{StaticResource NavButtonStyle}" Content="Account" Tag="Account" Click="NavButton_Click"/>
|
||||
<Button Style="{StaticResource NavButtonStyle}" Content="Settings" Tag="Settings" Click="NavButton_Click"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Status Section at Bottom -->
|
||||
<Border DockPanel.Dock="Bottom" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="1,1,0,0" Background="{StaticResource Brush.PanelAltBackground}" Padding="12 10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="Betfair Connection" FontSize="11" Foreground="{StaticResource Brush.TextSecondary}"/>
|
||||
<TextBlock x:Name="ConnectionStatusTextBlock" Text="Disconnected" Style="{StaticResource StatusTextStyle}" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="60"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="28"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<Border Grid.Row="0" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="0,0,0,1">
|
||||
<DockPanel LastChildFill="True" Margin="16 0">
|
||||
<TextBlock Text="Dashboard" x:Name="HeaderTitleTextBlock" FontSize="20" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right" VerticalAlignment="Center">
|
||||
<TextBlock Text="v0.1" Foreground="{StaticResource Brush.TextSecondary}" VerticalAlignment="Center" Margin="16,0,0,0"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Scrollable Content -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<Border Margin="16" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="1" Padding="24" CornerRadius="6">
|
||||
<StackPanel x:Name="MainContentArea">
|
||||
<TextBlock Text="Benvenuto in StrategyBet Desktop" FontSize="22" FontWeight="Bold"/>
|
||||
<TextBlock Text="Clicca 'Connect' per iniziare la connessione alle API Betfair. In futuro qui verrà mostrata la dashboard con metriche, strategie attive, mercati live e log attività." TextWrapping="Wrap" Foreground="{StaticResource Brush.TextSecondary}" Margin="0,12,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Footer / StatusBar -->
|
||||
<Border Grid.Row="2" Background="{StaticResource Brush.PanelBackground}" BorderBrush="{StaticResource Brush.BorderLight}" BorderThickness="0,1,0,0" Padding="12 0 16 0">
|
||||
<DockPanel>
|
||||
<TextBlock x:Name="GlobalStatusTextBlock" Text="Ready" Style="{StaticResource StatusTextStyle}"/>
|
||||
<TextBlock Text="StrategyBet Desktop" DockPanel.Dock="Right" Style="{StaticResource StatusTextStyle}"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
156
Amaltea/MainWindow.xaml.cs
Normal file
156
Amaltea/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Amaltea.Views;
|
||||
|
||||
namespace Amaltea
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private enum ConnectionState
|
||||
{
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Failed
|
||||
}
|
||||
|
||||
private ConnectionState _connectionState = ConnectionState.Disconnected;
|
||||
private CancellationTokenSource? _connectionCts;
|
||||
private readonly Random _random = new();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void ConnectButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_connectionState == ConnectionState.Connected)
|
||||
{
|
||||
SetConnectionState(ConnectionState.Disconnected, "Disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
await ConnectToBetfairAsync();
|
||||
}
|
||||
|
||||
private void SetConnectionState(ConnectionState state, string? overrideText = null)
|
||||
{
|
||||
_connectionState = state;
|
||||
switch (state)
|
||||
{
|
||||
case ConnectionState.Disconnected:
|
||||
ConnectButton.Content = "Connect";
|
||||
ConnectionStatusTextBlock.Text = overrideText ?? "Disconnected";
|
||||
GlobalStatusTextBlock.Text = "Ready";
|
||||
break;
|
||||
case ConnectionState.Connecting:
|
||||
ConnectButton.Content = "Cancel";
|
||||
ConnectionStatusTextBlock.Text = overrideText ?? "Connecting...";
|
||||
GlobalStatusTextBlock.Text = "Connecting to Betfair...";
|
||||
break;
|
||||
case ConnectionState.Connected:
|
||||
ConnectButton.Content = "Disconnect";
|
||||
ConnectionStatusTextBlock.Text = overrideText ?? "Connected";
|
||||
GlobalStatusTextBlock.Text = "Connected to Betfair";
|
||||
break;
|
||||
case ConnectionState.Failed:
|
||||
ConnectButton.Content = "Retry";
|
||||
ConnectionStatusTextBlock.Text = overrideText ?? "Connection Failed";
|
||||
GlobalStatusTextBlock.Text = "Connection failed";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates an async connection to the Betfair API.
|
||||
/// Later this will be replaced with real authentication / session logic.
|
||||
/// </summary>
|
||||
private async Task ConnectToBetfairAsync()
|
||||
{
|
||||
// If already connecting allow cancel.
|
||||
if (_connectionState == ConnectionState.Connecting)
|
||||
{
|
||||
_connectionCts?.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
_connectionCts = new CancellationTokenSource();
|
||||
var token = _connectionCts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
SetConnectionState(ConnectionState.Connecting);
|
||||
|
||||
// Simulate variable latency 3-5 seconds.
|
||||
var delay = TimeSpan.FromSeconds(_random.Next(3, 6));
|
||||
await Task.Delay(delay, token);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
// Randomly simulate success or failure (80% success for now)
|
||||
var success = _random.NextDouble() < 0.8;
|
||||
if (success)
|
||||
{
|
||||
SetConnectionState(ConnectionState.Connected);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConnectionState(ConnectionState.Failed);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
SetConnectionState(ConnectionState.Disconnected, "Canceled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetConnectionState(ConnectionState.Failed, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void NavButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not Button btn) return;
|
||||
var tag = btn.Tag?.ToString() ?? string.Empty;
|
||||
HeaderTitleTextBlock.Text = tag switch
|
||||
{
|
||||
"Dashboard" => "Dashboard",
|
||||
"StrategyEditor" => "Strategy Editor",
|
||||
"Backtesting" => "Backtesting",
|
||||
"Account" => "Account",
|
||||
"Settings" => "Settings",
|
||||
_ => "Dashboard"
|
||||
};
|
||||
|
||||
// Replace main content based on tag (placeholder for now)
|
||||
MainContentArea.Children.Clear();
|
||||
|
||||
if (tag == "StrategyEditor")
|
||||
{
|
||||
MainContentArea.Children.Add(new StrategyEditorView());
|
||||
return;
|
||||
}
|
||||
|
||||
MainContentArea.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"Sezione: {HeaderTitleTextBlock.Text}",
|
||||
FontSize = 22,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Margin = new Thickness(0,0,0,12)
|
||||
});
|
||||
MainContentArea.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "Contenuto placeholder. Qui verrà caricata l'interfaccia specifica della sezione.",
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = (System.Windows.Media.Brush)FindResource("Brush.TextSecondary")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user