デスクトップアプリで、マルチタッチとマウス操作の両方に対応できない!

自作ソフトKMeterをWindows8対応してみた。

と言っても、Windows8用に修正したものはなく、通常のバージョンアップ程度の修正。
KMeter 2.00では、Windows7のマルチタッチ操作に対応していたけど、この挙動が怪しいため修正を試みてみたが、どうもうまく直せない。

Windows8でマルチタッチ環境が増えてきたため、マルチタッチ対応したいところだけど、現状のスキルでは解決できないためマルチタッチ機能を廃止することにした。

そもそも、マルチタッチ操作は、フォームというか、WPFのウインドウ(外枠)自体に対しイベントを受け取れないようで、これら操作はウインドウ内の図形やコントロールに対して行わなければならない。

だから、KMeter2.00では、苦肉の策としてウインドウをスクリーンサイズに最大化し、透明なウインドウで見えなくしておき、その中にメーター(コントロール)を表示しタッチ操作を実装していた。

ここまでなら正常に動くんだけど、これに、マウス操作が加わると矛盾が生じる。

そもそも、Windowsデスクトップアプリは、あまりタッチ操作に向いていないし、タッチは今後Windowsストアアプリ(旧MetroUI)がメインだから無理して対応しなくてもいいと思えてきた。

そんな訳で、まともに動作しないマルチタッチ操作を廃止した。
サンプル
KMeter2.00では痛々しいマルチタッチが実装されているが、以下のような方法(かなり割愛したサンプル)で実装していた。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Name="mWindow" Title="MainWindow" Height="500" Width="500" ManipulationStarting="mWindow_ManipulationStarting" ManipulationDelta="mWindow_ManipulationDelta" WindowStartupLocation="Manual" ShowInTaskbar="False" Background="#0000000F" AllowsTransparency="True" WindowStyle="None" Left="0" Top="0" WindowState="Maximized"  >
    <Window.ContextMenu>
    <ContextMenu>
        <MenuItem Name="exitMeterMenuItem" Header="終了(_E)"/>
        </ContextMenu>
    </Window.ContextMenu>
    <Canvas Name="meterCanvas">
        <Grid Name="meterGrid" IsManipulationEnabled="True" MouseLeftButtonDown="meterGrid_MouseLeftButtonDown" MouseLeftButtonUp="meterGrid_MouseLeftButtonUp" MouseMove="meterGrid_MouseMove">
        <Grid.LayoutTransform>
            <ScaleTransform x:Name="gridBase" ScaleX="1" ScaleY="1" />
        </Grid.LayoutTransform>
        <Ellipse  Fill="#FF303030" Stroke="#FF000000" StrokeThickness="20" Width="200" Height="200" Name="ellipseMeter"
                Canvas.Left="0" Canvas.Top="0" >
            <Ellipse.RenderTransform>
                <MatrixTransform>
                    <MatrixTransform.Matrix>
                        <Matrix OffsetX="0" OffsetY="0"/>
                    </MatrixTransform.Matrix>
                </MatrixTransform>
            </Ellipse.RenderTransform>
        </Ellipse >
        <Line X1="100" Y1="100" X2="100" Y2="190" Stroke="red" StrokeThickness="5"/>
        </Grid>
    </Canvas>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            exitMeterMenuItem.Click += (sender, e) => exitItem_Click(sender, e);
        }

        private bool isDrag = false;
        private Point dragOffset;

        // マニピュレーション開始時
        private void mWindow_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {
            e.ManipulationContainer = this;
            e.Handled = true;
        }

        // マニピュレーション操作時
        private void mWindow_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            Grid grd = e.OriginalSource as Grid;
            Matrix grdMatrix = ((MatrixTransform)grd.RenderTransform).Matrix;

            // 傾き
            grdMatrix.RotateAt(
            e.DeltaManipulation.Rotation,
            e.ManipulationOrigin.X,
            e.ManipulationOrigin.Y);

            // 拡大・縮小
            grdMatrix.ScaleAt(
            e.DeltaManipulation.Scale.X,
            e.DeltaManipulation.Scale.Y,
            e.ManipulationOrigin.X,
            e.ManipulationOrigin.Y);

            // 移動
            grdMatrix.Translate(
            e.DeltaManipulation.Translation.X,
            e.DeltaManipulation.Translation.Y);
            grd.RenderTransform = new MatrixTransform(grdMatrix);
            e.Handled = true;
        }
        // マウスダウン時
        private void meterGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            UIElement el = sender as UIElement;
            if (el != null)
            {
                isDrag = true;
                dragOffset = e.GetPosition(el);
                el.CaptureMouse();
            }
        }

        // マウスアップ時
        private void meterGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDrag == true)
            {
                UIElement el = sender as UIElement;
                el.ReleaseMouseCapture();
                isDrag = false;
            }
        }

        // マウス移動時
        private void meterGrid_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDrag == true)
            {
                Point pt = Mouse.GetPosition(meterCanvas);
                UIElement el = sender as UIElement;
                Canvas.SetLeft(el, pt.X - dragOffset.X);
                Canvas.SetTop(el, pt.Y - dragOffset.Y);
            }
        }

        // [終了]メニュー
        private void exitItem_Click(object sender, EventArgs e)
        {
            System.Windows.Application.Current.Shutdown();
        }
    }
}

KMeter
KMeter

← ブログ一覧へ戻る