片手用ControlPadなUserControlを作ってみた

環境はWindowsPhoneSilverLight8.1ですが、基本的にはC#+XAMLなのであまり変更せずにWinRTにも移植可能ではないかと思います。

まずは外観から
名称未設定 1

XAMLで「ChangePropertyAction」を利用して押した時の反応などを定義してあります

<UserControl
    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:es="clr-namespace:Microsoft.Expression.Shapes;assembly=Microsoft.Expression.Drawing" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" xmlns:eim="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions" x:Class="CryEarthWPLib.Controller.View.SingleControllerPad"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="150" d:DesignWidth="150" Opacity="0.7">
    
	<Grid x:Name="LayoutRoot" Width="150" Height="150">
		<VisualStateManager.VisualStateGroups>
			<VisualStateGroup x:Name="VisualStateGroup"/>
		</VisualStateManager.VisualStateGroups>
		<Path x:Name="TopPath" UseLayoutRounding="False" VerticalAlignment="Top" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="25.503,35.355,25.503,0" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" MouseEnter="RingPath_MouseEnter" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="MouseLeftButtonDown">
					<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseEnter">
					<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeftButtonUp">
					<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeave">
					<ec:ChangePropertyAction TargetName="TopPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=TopPath_State}">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
			<Path.Fill>
				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="#FF71A0E4" Offset="1"/>
					<GradientStop Color="#FF17335B" Offset="0.246"/>
				</LinearGradientBrush>
			</Path.Fill>
			<Path.RenderTransform>
				<CompositeTransform ScaleY="-1"/>
			</Path.RenderTransform>
		</Path>
		<Path x:Name="BottomPath" UseLayoutRounding="False" VerticalAlignment="Bottom" Data="M14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,0 L98.995,14.1422 C86.3275,26.8097 68.8275,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1422 z" Height="34.645" Margin="25.503,0,25.503,5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="MouseLeftButtonDown">
					<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseEnter">
					<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeftButtonUp">
					<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeave">
					<ec:ChangePropertyAction TargetName="BottomPath_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
			<Path.Fill>
				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="#FF71A0E4" Offset="1"/>
					<GradientStop Color="#FF17335B" Offset="0.246"/>
				</LinearGradientBrush>
			</Path.Fill>
			<Path.RenderTransform>
				<CompositeTransform/>
			</Path.RenderTransform>
		</Path>
		<Path x:Name="RightPath" Data="M14.1422,0 C26.8097,12.6675 34.6447,30.1675 34.6447,49.4975 C34.6447,68.2234 27.2917,85.2319 15.3155,97.7933 L14.1422,98.995 L0,84.8528 C9.04823,75.8046 14.6447,63.3046 14.6447,49.4975 C14.6447,36.5533 9.72592,24.758 1.65557,15.8786 L0,14.1422 z" Margin="110.355,25.503,5,25.503" Stretch="Fill" UseLayoutRounding="False" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="MouseLeftButtonDown">
					<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseEnter">
					<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeftButtonUp">
					<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeave">
					<ec:ChangePropertyAction TargetName="RightPath_State" PropertyName="Visibility" TargetObject="{Binding ElementName=RightPath_State}">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
			<Path.Fill>
				<LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
					<LinearGradientBrush.RelativeTransform>
						<CompositeTransform CenterY="0.5" CenterX="0.5" Rotation="-90"/>
					</LinearGradientBrush.RelativeTransform>
					<GradientStop Color="#FF71A0E4" Offset="1"/>
					<GradientStop Color="#FF17335B" Offset="0.246"/>
				</LinearGradientBrush>
			</Path.Fill>
		</Path>
		<Path x:Name="LeftPath" UseLayoutRounding="False" HorizontalAlignment="Left" VerticalAlignment="Center" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="-11.997,72.855,0,42.5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Width="98.994" Stroke="#667088B2" MouseLeftButtonDown="RingPath_MouseLeftButtonDown" MouseLeftButtonUp="RingPath_MouseLeftButtonUp" MouseLeave="RingPath_MouseLeave" MouseEnter="RingPath_MouseEnter">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="MouseLeftButtonDown">
					<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseEnter">
					<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeftButtonUp">
					<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeave">
					<ec:ChangePropertyAction TargetName="LeftPath_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
			<Path.Fill>
				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="#FF71A0E4" Offset="1"/>
					<GradientStop Color="#FF17335B" Offset="0.246"/>
				</LinearGradientBrush>
			</Path.Fill>
			<Path.RenderTransform>
				<CompositeTransform Rotation="90"/>
			</Path.RenderTransform>
		</Path>
		<es:Arc x:Name="TapArc" ArcThickness="1" ArcThicknessUnit="Percent" EndAngle="360" Height="40" Stretch="None" UseLayoutRounding="False" Width="40" HorizontalAlignment="Center" VerticalAlignment="Center" Tap="TapArc_Tap" Stroke="#33999DCB" d:IsLocked="True">
			<i:Interaction.Triggers>
				<i:EventTrigger EventName="MouseLeftButtonDown">
					<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility"/>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeftButtonUp">
					<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
				<i:EventTrigger EventName="MouseLeave">
					<ec:ChangePropertyAction TargetName="TapArc_State" PropertyName="Visibility">
						<ec:ChangePropertyAction.Value>
							<Visibility>Collapsed</Visibility>
						</ec:ChangePropertyAction.Value>
					</ec:ChangePropertyAction>
				</i:EventTrigger>
			</i:Interaction.Triggers>
			<es:Arc.Fill>
				<RadialGradientBrush>
					<GradientStop Color="#FF2E4A93" Offset="0.884"/>
					<GradientStop Color="Black"/>
					<GradientStop Color="#FF4D77E8" Offset="0.53"/>
					<GradientStop Offset="1" Color="Transparent"/>
					<GradientStop Color="#FF162D66" Offset="0.944"/>
					<GradientStop Color="#FF2E4A93" Offset="0.19"/>
				</RadialGradientBrush>
			</es:Arc.Fill>
		</es:Arc>
		<es:RegularPolygon x:Name="TopTriangle" HorizontalAlignment="Center" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Top" Width="15" Fill="#66FFFFFF" Margin="67.5,7,67.5,0" IsHitTestVisible="False" d:IsLocked="True"/>
		<es:RegularPolygon x:Name="BottomTriangle" HorizontalAlignment="Center" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Bottom" Width="15" Fill="#66FFFFFF" Margin="67.5,0,67.5,7" RenderTransformOrigin="0.5,0.5" IsHitTestVisible="False" d:IsLocked="True">
			<es:RegularPolygon.RenderTransform>
				<CompositeTransform ScaleY="-1"/>
			</es:RegularPolygon.RenderTransform>
		</es:RegularPolygon>
		<es:RegularPolygon x:Name="LeftTriangle" HorizontalAlignment="Left" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="15" Fill="#66FFFFFF" RenderTransformOrigin="0.5,0.5" Margin="7,0,0,0" IsHitTestVisible="False" d:IsLocked="True">
			<es:RegularPolygon.RenderTransform>
				<CompositeTransform ScaleY="-1" Rotation="90"/>
			</es:RegularPolygon.RenderTransform>
		</es:RegularPolygon>
		<es:RegularPolygon x:Name="RightTriangle" HorizontalAlignment="Right" Height="15" InnerRadius="1" PointCount="3" Stretch="Fill" UseLayoutRounding="False" VerticalAlignment="Center" Width="15" Fill="#66FFFFFF" RenderTransformOrigin="0.5,0.5" Margin="0,0,7,0" IsHitTestVisible="False" d:IsLocked="True">
			<es:RegularPolygon.RenderTransform>
				<CompositeTransform Rotation="-90" ScaleY="-1" ScaleX="-1"/>
			</es:RegularPolygon.RenderTransform>
		</es:RegularPolygon>
        <Path x:Name="TopPath_State" UseLayoutRounding="False" VerticalAlignment="Top" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="25.503,35.355,25.503,0" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
			<Path.RenderTransform>
				<CompositeTransform ScaleY="-1"/>
			</Path.RenderTransform>
		</Path>
		<Path x:Name="RightPath_State" Data="M14.1422,0 C26.8097,12.6675 34.6447,30.1675 34.6447,49.4975 C34.6447,68.2234 27.2917,85.2319 15.3155,97.7933 L14.1422,98.995 L0,84.8528 C9.04823,75.8046 14.6447,63.3046 14.6447,49.4975 C14.6447,36.5533 9.72592,24.758 1.65557,15.8786 L0,14.1422 z" Margin="110.355,25.503,5,25.503" Stretch="Fill" UseLayoutRounding="False" Stroke="#667088B2"  Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True"/>
		<Path x:Name="BottomPath_State" UseLayoutRounding="False" VerticalAlignment="Bottom" Data="M14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,0 L98.995,14.1422 C86.3275,26.8097 68.8275,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1422 z" Height="34.645" Margin="25.503,0,25.503,5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
			<Path.RenderTransform>
				<CompositeTransform/>
			</Path.RenderTransform>
		</Path>
		<Path x:Name="LeftPath_State" UseLayoutRounding="False" HorizontalAlignment="Left" VerticalAlignment="Center" Data="M84.8528,0 L98.9949,14.1422 L97.7933,15.3155 C85.2319,27.2917 68.2234,34.6447 49.4975,34.6447 C30.1675,34.6447 12.6675,26.8097 0,14.1421 L14.1421,0 C23.1904,9.04823 35.6904,14.6447 49.4975,14.6447 C63.3046,14.6447 75.8046,9.04823 84.8528,4E-06 z" Height="34.645" Margin="-11.997,72.855,0,42.5" RenderTransformOrigin="0.5,0.061905" Stretch="Fill" Width="98.994" Stroke="#667088B2" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True">
			<Path.RenderTransform>
				<CompositeTransform Rotation="90"/>
			</Path.RenderTransform>
		</Path>
		<es:Arc x:Name="TapArc_State" ArcThickness="1" ArcThicknessUnit="Percent" EndAngle="360" Height="40" Stretch="None" UseLayoutRounding="False" Width="40" HorizontalAlignment="Center" VerticalAlignment="Center" Tap="TapArc_Tap" Stroke="#33999DCB" Margin="55" Fill="#99FFFFFF" Visibility="Collapsed" IsHitTestVisible="False" d:IsLocked="True"/>

	</Grid>
</UserControl>

続いてコード部分

    public partial class SingleControllerPad : UserControl
    {
        /// <summary>
        /// Key判断イベントHANDLE
        /// </summary>
        /// <param name="sender">this</param>
        /// <param name="keyId">0=Enter,1=TOP,2=Bottom,3=Left,4=Right,5=non</param>
        public delegate void TapKeyControlEventHandler(object sender, int keyId);

        public event TapKeyControlEventHandler TapKeyControl;

        /// <summary>
        /// Keyイベント
        /// </summary>
        /// <param name="keyId">0=Enter,1=TOP,2=Bottom,3=Left,4=Right,5=non</param>
        protected virtual void OnTopKeyControl(int keyId)
        {
            if (TapKeyControl != null)
            {
                TapKeyControl(this, keyId);
            }
        }

        public SingleControllerPad()
        {
            InitializeComponent();
        }

		/// <summary>
		/// 確定Button
		/// 所謂Aボタンを押したとか
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void TapArc_Tap(object sender, System.Windows.Input.GestureEventArgs e)
		{
            // 確定なので0で固定
            // イベントをチェックしているところでイベント発生後、イベントをリセットする
		    OnTopKeyControl(0);
		}

        /// <summary>
        /// 領域に入ってきたことを検知
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RingPath_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            var ul = e.OriginalSource as UIElement;
            if (ul != null)
            {
                var name = ((System.Windows.Shapes.Path)ul).Name;
                CallRingPathEvent(name);
            }
        }

        /// <summary>
        /// 領域から出たことを検知
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RingPath_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            // 何も押していないことを通知
            CallRingPathEvent("");
        }

        /// <summary>
        /// Buttonを選択
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RingPath_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var ul = e.OriginalSource as UIElement;
            if (ul != null)
            {
                var name = ((System.Windows.Shapes.Path)ul).Name;
                CallRingPathEvent(name);
            }
        }

        /// <summary>
        /// Buttonを離す
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RingPath_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var ul = e.OriginalSource as UIElement;
            if (ul != null)
            {
                var name = ((System.Windows.Shapes.Path)ul).Name;
                CallRingPathEvent(name);
            }
        }

        /// <summary>
        /// 外部にどのボタンを押しているかを知らせる
        /// </summary>
        /// <param name="pathName"></param>
        void CallRingPathEvent(string pathName)
        {
            switch (pathName)
            {
                case "TopPath":
                    OnTopKeyControl(1);
                    break;

                case "BottomPath":
                    OnTopKeyControl(2);
                    break;

                case "LeftPath":
                    OnTopKeyControl(3);
                    break;

                case "RightPath":
                    OnTopKeyControl(4);
                    break;

                default:
                    OnTopKeyControl(5);
                    break;
            }
        }

    }

このコントロール自体はあくまでもキー情報を通知するだけの機能にしています。
使うときは、イベントでKeyIDを確保して、ゲームループでそのIDを判断して各操作を行うという形にしています。

{
            var timer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(GameLoopTimerCallback), TimeSpan.FromMilliseconds(35));
            // メインUIのDispatcherを取得
            dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
}

        private async void GameLoopTimerCallback(ThreadPoolTimer timer)
        {
            if (dispatcher != null)
                await dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                    () =>
                    {
                            // 各種処理関数を呼び出す
                    });
        }

大分手抜き&片手コントロール用なのでいろいろ不足していますし、キャンセルボタンの扱いという問題もあります。
まあ、入り口ということで、こんなのもありかなと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください