1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-10-31

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

Storyboard関連

やりたいこと

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

イメージはシンプルにこんな感じ。
image.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

備考

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

バージョンアップ(200128)

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

動くものはこちら

image.png

settings\NameList.json
{
    "Names" : [
        "1さん",
        "2さん",
        "3さん",
        "4さん",
        "5さん",
        "6さん",
        "7さん",
        "8さん",
        "9さん",
        "10さん",
        "11さん"
    ]
}

コード

参考

++C++
https://ufcpp.net/study/dotnet/wpf_xamlani.html

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

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?