Wykorzystanie komend

This commit is contained in:
Kacper Donat 2019-10-26 19:23:37 +02:00
parent 879c29cc74
commit 093b590ee4
10 changed files with 162 additions and 71 deletions

View File

@ -23,6 +23,7 @@
<e p="MainWindow.xaml" t="Include" /> <e p="MainWindow.xaml" t="Include" />
<e p="MainWindow.xaml.cs" t="Include" /> <e p="MainWindow.xaml.cs" t="Include" />
<e p="Misc" t="Include"> <e p="Misc" t="Include">
<e p="DelegateCommand.cs" t="Include" />
<e p="DelegateSubscriber.cs" t="Include" /> <e p="DelegateSubscriber.cs" t="Include" />
<e p="DelegateUnsubscriber.cs" t="Include" /> <e p="DelegateUnsubscriber.cs" t="Include" />
<e p="Position.cs" t="Include" /> <e p="Position.cs" t="Include" />
@ -36,6 +37,7 @@
</e> </e>
</e> </e>
<e p="ViewModel" t="Include"> <e p="ViewModel" t="Include">
<e p="AutomatonViewModel.cs" t="Include" />
<e p="BaseViewModel.cs" t="Include" /> <e p="BaseViewModel.cs" t="Include" />
<e p="CellViewModel.cs" t="Include" /> <e p="CellViewModel.cs" t="Include" />
<e p="FieldViewModel.cs" t="Include" /> <e p="FieldViewModel.cs" t="Include" />

View File

@ -46,6 +46,11 @@ namespace Life.Automaton
_observers.ForEach(observer => observer.OnNext(this)); _observers.ForEach(observer => observer.OnNext(this));
} }
public void Notify()
{
_observers.ForEach(observer => observer.OnNext(this));
}
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]

View File

@ -2,9 +2,14 @@
namespace Life.Automaton namespace Life.Automaton
{ {
public static class GameOfLife public class GameOfLife : IAutomaton
{ {
public static Rule Rule(string born, string survive) public GameOfLife(int width, int height)
{
Initialize(new MooreField(width, height));
}
public static Rule MakeRule(string born, string survive)
{ {
bool WillSurvive(int neighbours) => survive.Contains(neighbours.ToString()); bool WillSurvive(int neighbours) => survive.Contains(neighbours.ToString());
bool WillBorn(int neighbours) => born.Contains(neighbours.ToString()); bool WillBorn(int neighbours) => born.Contains(neighbours.ToString());
@ -29,5 +34,22 @@ namespace Life.Automaton
return @new; return @new;
}; };
} }
public Rule Rule { get; set; } = MakeRule("3", "32");
public int Iteration { get; private set; }
public IField Field { get; private set; }
public void Initialize(IField field)
{
Field = field;
}
public void Advance()
{
Field.Transform(Rule);
Iteration++;
}
} }
} }

View File

@ -17,5 +17,6 @@ namespace Life.Automaton
IEnumerable<Cell> Neighbours(int x, int y); IEnumerable<Cell> Neighbours(int x, int y);
void Transform(Rule rule); void Transform(Rule rule);
void Notify();
} }
} }

View File

@ -47,7 +47,7 @@
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="Button"> <ControlTemplate TargetType="Button">
<Border x:Name="Border" <Border x:Name="Border"
BorderThickness="1" BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"> BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter Margin="2" <ContentPresenter Margin="2"
@ -73,8 +73,9 @@
<DataTemplate> <DataTemplate>
<Button Width="{Binding Size, RelativeSource={RelativeSource AncestorType={x:Type local:AutomatonField}}}" <Button Width="{Binding Size, RelativeSource={RelativeSource AncestorType={x:Type local:AutomatonField}}}"
Height="{Binding Size, RelativeSource={RelativeSource AncestorType={x:Type local:AutomatonField}}}" Height="{Binding Size, RelativeSource={RelativeSource AncestorType={x:Type local:AutomatonField}}}"
Click="Cell_Click"
Style="{DynamicResource Cell}" Style="{DynamicResource Cell}"
Command="{Binding ClickCommand, RelativeSource={RelativeSource AncestorType={x:Type local:AutomatonField}}}"
CommandParameter="{Binding Position}"
/> />
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View File

@ -1,9 +1,6 @@
using System; using System.Windows;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Input;
using System.Windows.Shapes;
using Life.Automaton; using Life.Automaton;
using Life.ViewModel; using Life.ViewModel;
@ -29,6 +26,12 @@ namespace Life
get { return (IField) GetValue(FieldProperty); } get { return (IField) GetValue(FieldProperty); }
set { SetValue(FieldProperty, value); } set { SetValue(FieldProperty, value); }
} }
public ICommand ClickCommand
{
get { return (ICommand) GetValue(ClickCommandProperty); }
set { SetValue(ClickCommandProperty, value); }
}
#endregion #endregion
public FieldViewModel ViewModel { get; set; } = new FieldViewModel(); public FieldViewModel ViewModel { get; set; } = new FieldViewModel();
@ -77,16 +80,12 @@ namespace Life
typeof(IField), typeof(AutomatonField), typeof(IField), typeof(AutomatonField),
new PropertyMetadata(OnFieldChanged) new PropertyMetadata(OnFieldChanged)
); );
public static readonly DependencyProperty ClickCommandProperty = DependencyProperty.Register(
nameof(ClickCommand),
typeof(ICommand), typeof(AutomatonField)
);
#endregion #endregion
private void Cell_Click(object sender, RoutedEventArgs e)
{
var vm = (sender as Button).DataContext as CellViewModel;
var cell = Field[vm.Position.X, vm.Position.Y];
cell.IsAlive = !cell.IsAlive;
ViewModel.Sync();
}
} }
} }

View File

@ -4,17 +4,18 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Life" xmlns:local="clr-namespace:Life"
xmlns:vm="clr-namespace:Life.ViewModel"
mc:Ignorable="d" mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" d:DataContext="{d:DesignInstance local:MainWindow}"> Title="MainWindow" Height="450" Width="800" d:DataContext="{d:DesignInstance vm:AutomatonViewModel}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" MinWidth="400"/> <ColumnDefinition Width="2*" MinWidth="400"/>
<ColumnDefinition Width="3"/> <ColumnDefinition Width="3"/>
<ColumnDefinition Width="1*" MinWidth="250"/> <ColumnDefinition Width="1*" MinWidth="300" MaxWidth="400"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<local:AutomatonField Field="{Binding Field}" Size="{Binding Size}" Separation="3"/> <local:AutomatonField Field="{Binding Field}" Size="{Binding Size}" Separation="3" ClickCommand="{Binding CellClicked}"/>
</ScrollViewer> </ScrollViewer>
<GridSplitter Grid.Column="1" Width="3" ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"/> <GridSplitter Grid.Column="1" Width="3" ResizeDirection="Columns" ResizeBehavior="PreviousAndNext"/>
@ -31,8 +32,7 @@
</StackPanel> </StackPanel>
<StackPanel Orientation="Vertical" Grid.Row="1" VerticalAlignment="Bottom"> <StackPanel Orientation="Vertical" Grid.Row="1" VerticalAlignment="Bottom">
<Button VerticalAlignment="Stretch" Margin="0 0 0 8">Step</Button> <Button VerticalAlignment="Stretch" Command="{Binding StepCommand}">Step</Button>
<Button VerticalAlignment="Stretch" Click="Step_OnClick">Step</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -1,63 +1,28 @@
using System; using System.Windows;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using Life.Annotations;
using Life.Automaton; using Life.Automaton;
using Life.ViewModel;
namespace Life namespace Life
{ {
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged public partial class MainWindow : Window
{ {
private IField _field; public AutomatonViewModel ViewModel;
private int _size;
private Rule _rule = GameOfLife.Rule("3", "23");
public IField Field
{
get => _field;
set
{
_field = value;
OnPropertyChanged(nameof(Field));
}
}
public int Size
{
get => _size;
set
{
_size = value;
OnPropertyChanged(nameof(Size));
}
}
public MainWindow() public MainWindow()
{ {
Size = 20; ViewModel = new AutomatonViewModel()
Field = new MooreField(20, 20); {
Field[5, 5].IsAlive = true; Automaton = new GameOfLife(20, 20),
DataContext = this; Size = 16,
Separation = 2
};
DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
} }
private void Step_OnClick(object sender, RoutedEventArgs e)
{
Field.Transform(_rule);
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} }
} }

View File

@ -0,0 +1,26 @@
using System;
using System.Windows.Input;
namespace Life.Misc
{
public class DelegateCommand<T> : ICommand
{
public Action<T> Action { get; set; }
public Predicate<T> Predicate { get; set; }
public bool CanExecute(object parameter)
{
if (Action == null)
return false;
return Predicate?.Invoke((T) parameter) ?? true;
}
public void Execute(object parameter)
{
Action?.Invoke((T)parameter);
}
public event EventHandler CanExecuteChanged;
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Windows.Input;
using Life.Automaton;
using Life.Misc;
namespace Life.ViewModel
{
public class AutomatonViewModel : BaseViewModel
{
private int _size;
private int _separation;
private IAutomaton _automaton;
public int Size
{
get => _size;
set
{
_size = value;
OnPropertyChanged(nameof(Size));
}
}
public int Separation
{
get => _separation;
set
{
_separation = value;
OnPropertyChanged(nameof(Separation));
}
}
public IAutomaton Automaton
{
get => _automaton;
set
{
_automaton = value;
OnPropertyChanged(nameof(Field));
OnPropertyChanged(nameof(Iteration));
}
}
public IField Field => Automaton.Field;
public int Iteration => Automaton.Iteration;
public ICommand CellClicked => new DelegateCommand<Position>
{
Action = HandleCellClick
};
public ICommand StepCommand => new DelegateCommand<object>
{
Action = Step
};
private void Step(object param)
{
Automaton.Advance();
}
private void HandleCellClick(Position position)
{
Field[position.X, position.Y].IsAlive = !Field[position.X, position.Y].IsAlive;
Field.Notify();
}
}
}