[WPF/xaml] xaml＋C#で当番決めのためのルーレットを作る

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

**Storyboard関連**
- [[WPF/xaml] xaml＋C#で当番決めのためのルーレットを作る](https://qiita.com/tera1707/items/2a94c262c93b93854688#_reference-8730f3e1be562848669f)
- [[WPF/xaml]Storyboardでアニメーションをつくる](https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b)
- [[WPF/xaml]Storyboardでアニメーションをつくる2(TargetPropertyの階層的な指定)](https://qiita.com/tera1707/items/ee6d72bb68a8e2131082)

# やりたいこと
週一回のミーティングの司会役を決めるためにネット上にあるルーレットページ？を使っているが、なんとなくxamlでもできそうな気がしたので試しに作ってみたい。

イメージはシンプルにこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/8214e45f-34c4-1edb-a44d-251c5178d6fe.png)

# やり方
下記のようにしてやる。
**キーワードは「StoryBoard」。**

- [xaml]ルーレットの円を作成。
- [C#]人数分の名前を、stringのListで持つ(以降、人数はListの件数で判断)
- [C#]人数分の名前を、円の中に書く。
- [C#]人数分の区切りの線を引く。
- [C#]名前と区切り線を、一人分の角度*List番号分回転させて表示する。
- [xaml]に、ルーレット(円の中身全体)を永遠に回し続けるStoryBoardを作成する。
- [C#]ボタンをおしたら、ルーレットを回すStoryBoardをスタートする。

※一人分の角度は`360/人数`。

# コード

## xaml

```MainWindow.xaml
<Window x:Class="WpfApp38.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:WpfApp38"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="600"
        Loaded="Window_Loaded"
        Name="Root">

    <Window.Resources>
        <!-- ルーレットのアニメーション定義 -->
        <Storyboard  x:Key="StartRoulettea">
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetName="RouletteMain" Storyboard.TargetProperty="(Grid.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"
                                           RepeatBehavior="Forever" >
                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0" />
                <LinearDoubleKeyFrame KeyTime="00:00:00.5" Value="360" />
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
    
    <Grid>
        <Viewbox Margin="20">
            <Grid Name="RouletteMain" RenderTransformOrigin="0.5,0.5">
                <Grid.RenderTransform>
                    <TransformGroup>
                        <!-- 回転の角度 -->
                        <RotateTransform Angle="0"/>
                    </TransformGroup>
                </Grid.RenderTransform>

                <!-- ルーレットの外側の円 -->
                <Ellipse Name="RouletteEllipse" Stroke="Black" StrokeThickness="5" Width="500" Height="500" >
                    <Ellipse.Fill>    
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">    
                            <GradientStop Color="Red" Offset="0.0" />    
                            <GradientStop Color="Orange" Offset="0.2" />    
                            <GradientStop Color="Yellow" Offset="0.4" />    
                            <GradientStop Color="LimeGreen" Offset="0.6" />    
                            <GradientStop Color="Blue" Offset="0.8" />    
                            <GradientStop Color="Violet" Offset="1.0" />    
                        </LinearGradientBrush>    
	                </Ellipse.Fill>
                </Ellipse>
            </Grid>

        </Viewbox>
        
        <!-- 上の線 -->
        <Rectangle Stroke="Black" StrokeThickness="10" Height="50" Width="5" VerticalAlignment="Top"/>
        
        <!-- スタートボタン -->
        <Button Name="StartButton" Width="100" Height="50" Content="スタート" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Click="Button_Click"/>
    </Grid>
</Window>
```

## コードビハインド(cs)

```MainWindow.xaml.cs
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace WpfApp38
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// ルーレット回転中かどうか
        /// </summary>
        bool IsRounding = false;

        /// <summary>
        /// メンバー一覧
        /// ここにメンバー名をAddしたら、
        /// Window_Loaded()の中で枠を自動でつくる
        /// </summary>
        List<string> Members = new List<string>();

        /// <summary>
        /// コンストラクタ
        /// ここでメンバー登録をする
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            Members.Add("Aさん");
            Members.Add("Bさん");
            Members.Add("Cさん");
            Members.Add("Dさん");
            Members.Add("Eさん");
            Members.Add("Fさん");
            Members.Add("Gさん");
            Members.Add("Hさん");
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // 一人あたりの使用する角度を決める
            int anglePerOne = 360 / Members.Count;

            // 人数分の線を引き、名前のテキストを作成する
            for (int i = 0; i < Members.Count; i++)
            {
                ////////////////////////
                // 人数分の区切り線を引く
                ////////////////////////
                var tfgLine = new TransformGroup();
                tfgLine.Children.Add(new RotateTransform(i * anglePerOne));

                var line = new Line()
                {
                    X1 = 0,
                    Y1 = 0,
                    X2 = 0,
                    Y2 = RouletteEllipse.Width / 2,
                    StrokeThickness = 5,
                    Stroke = Brushes.Red,
                    Fill = Brushes.Transparent,
                    VerticalAlignment = VerticalAlignment.Top,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    RenderTransformOrigin = new Point(0, 1.0),
                    RenderTransform = tfgLine
                };

                RouletteMain.Children.Add(line);

                ////////////////////////
                // 人数分の名前を書く
                ////////////////////////
                int textHeight = 30;
                var tfgText = new TransformGroup();
                tfgText.Children.Add(new RotateTransform(-90 + (anglePerOne / 2) + (i * anglePerOne)));

                var text = new TextBlock()
                {
                    Text = Members[i],
                    Width = RouletteEllipse.Width / 2,                  // ルーレットの円の半分
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Right,
                    TextAlignment = TextAlignment.Center,
                    FontSize = textHeight,
                    RenderTransformOrigin = new Point(0, 0.5),
                    RenderTransform = tfgText
                };

                RouletteMain.Children.Add(text);
            }
        }

        /// <summary>
        /// ルーレットのスタート/ストップ
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // ルーレットを回すためのStoryBoardを検索
            var sb = FindResource("StartRoulettea") as Storyboard;
            
            if (IsRounding == false)
            {
                // 回転開始(スタート)
                sb.Begin();

                StartButton.Content = "ストップ";
            }
            else
            {
                // 回転停止(ストップ)
                sb.Pause();

                StartButton.Content = "スタート";
            }

            IsRounding = !IsRounding;
        }
    }
}
```

# できあがり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/8ece3bf2-9f57-e981-0521-fa6da6979e63.png)

# 備考
背景のグラデーションあると見た目がさみしくないが、超目押し可能となるので問題。

# バージョンアップ(200128)
- 下記の機能を追加した。
 - ルーレット画像をクリップボードにコピーする
 - ルーレットスタート時、ドラムロール音を出す
 - ルーレットストップ時、シンバル音を出す
 - 音のONOFFをチェックボックスで切り替え可能
 - jsonファイルから、ルーレット上の名前を追加可能
 - 起動時、名前をランダムで並び替える
 - 当たった人を示す赤三角もまわる(目押し防止)

[動くものはこちら](https://github.com/tera1707/WPF-/tree/master/031_XamlRoulette/OutputZipfile)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/06b7ba94-5659-57f2-82bf-f024b0b856e2.png)

```settings\NameList.json
{
    "Names" : [
        "１さん",
        "２さん",
        "３さん",
        "４さん",
        "５さん",
        "６さん",
        "７さん",
        "８さん",
        "９さん",
        "１０さん",
        "１１さん"
    ]
}

```




# コード
https://github.com/tera1707/WPF-/tree/master/031_XamlRoulette

# 参考
＋＋C＋＋
https://ufcpp.net/study/dotnet/wpf_xamlani.html

自分のページ
[WPF/xaml]Storyboardでアニメーションをつくる
https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b