2進そろばんを作ってみた

siokoshou2008-09-14


こんばんは、siokoshou です。気になる色は Midori と Red です。あと .NET4.0。って色じゃないか。

こないだの本に3進法のそろばんが載ってました。写真が載ってるので本当に作ってしまったようですw それがおもしろかったので、2進そろばんを作ってみました。実物は作れないので WPF ですがw

サービスで10進表示とクリアボタン付きです。そういえば子供のころ、本物のそろばんにクリアボタンが付いてるのがあったなぁ…。
ぱちぱち音が出ればもっとおもしろいけど、そろばんがないので音が拾えませんでした。無念。

8bit CPU になったつもりで計算してみてくださいw 桁上がりしすぎ!とか楽しめます(^^;
突然ですが問題です。-1 はどうやって表すでしょう?って、ここを見てる人には簡単すぎか。

WPF の練習がてら作ったのでうまく使いこなせているかわかりませんが、コードも晒しときます。こうするともっといいよって所があれば教えてください。GUI なのにこの短さってのが驚きです。それにしても、スタイルとかテンプレートとかリソースとかややこしい。

XAML の見どころ。珠は ToggleButton そのもので、Trigger を使って XAML だけで絵を切り替えています。見方によっては XAML で if 文を実行してることになります。C# のコードを書かなくてもここまでできるのがスゲー。
さらに Template を設定する Style を書くことですべての ToggleButton の見た目を変えています。テンプレートって名前の印象から例えば Button のテンプレートを書けば、その後の Button すべてに影響があるのかと思ってたけど、そうじゃないんですね。ややこしい。本当にすべての ToggleButton の見た目を変えるには、この例のように「Style で ToggleButton の Template を Setter で設定する」とできました。

本当は Silverlight2 で公開したいところだけど、Go-Live ライセンスがどうのとかよくわからないのと、Trigger のあたりが動かない(ない?)のであきらめました。


XAML

<Window x:Class="Soroban.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="2進そろばん" Height="110" Width="450">
  <Window.Resources>
    <Style x:Key="{x:Type ToggleButton}" TargetType="{x:Type ToggleButton}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ToggleButton}">
          <Image x:Name="image" Stretch="Fill" Source="0.bmp" />

            <ControlTemplate.Triggers>
              <Trigger Property="IsChecked" Value="true">
                <Setter TargetName="image" Property="Source" Value="1.bmp" />
              </Trigger>
            </ControlTemplate.Triggers>

          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Window.Resources>

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <TextBlock x:Name="text" Grid.Row="0" Grid.Column="0"
      VerticalAlignment="Center" HorizontalAlignment="Right"
      Margin="20,3,20,3" Text="{Binding Path=Number}">
    </TextBlock>
    <Button Grid.Row="0" Grid.Column="1" Margin="3" Click="Button_Click" >Clear</Button>

    <Border Grid.Row="1" Grid.ColumnSpan="2" BorderThickness="5"
      BorderBrush="Black" CornerRadius="3">
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
          <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <ToggleButton Grid.Column="0" IsChecked="{Binding Path=[7]}" />
        <ToggleButton Grid.Column="1" IsChecked="{Binding Path=[6]}" />
        <ToggleButton Grid.Column="2" IsChecked="{Binding Path=[5]}" />
        <ToggleButton Grid.Column="3" IsChecked="{Binding Path=[4]}" />
        <ToggleButton Grid.Column="4" IsChecked="{Binding Path=[3]}" />
        <ToggleButton Grid.Column="5" IsChecked="{Binding Path=[2]}" />
        <ToggleButton Grid.Column="6" IsChecked="{Binding Path=[1]}" />
        <ToggleButton Grid.Column="7" IsChecked="{Binding Path=[0]}" />
      </Grid>
    </Border>

  </Grid>
</Window>

C#

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;

namespace Soroban
{
  public partial class Window1 : Window
  {
    private Bits number = new Bits();

    public Window1()
    {
      InitializeComponent();
      this.DataContext = this.number;
    }

    private void Button_Click( object sender, RoutedEventArgs e )
    {
      this.number.Number = 0;
    }
  }

  public class Bits : ObservableCollection<bool>
  {
    private const int Max = 8;

    public Bits()
    {
      for ( int i = 0; i < Max; i++ )
      {
        Add( false );
      }
    }

    protected override void SetItem( int index, bool item )
    {
      base.SetItem( index, item );
      this.OnPropertyChanged( new PropertyChangedEventArgs( "Number" ) );
    }

    public int Number
    {
      get
      {
        int n = this.Select( ( b, i ) => new { b, i } ).Sum( x => x.b ? 1 << x.i : 0 );
        return n;
      }

      set
      {
        for ( int i = 0; i < Max; i++ )
        {
          this[ i ] = ( value & ( 1 << i ) ) != 0;
        }
      }
    }
  }
}