0
1

More than 3 years have passed since last update.

[WPF] 重ねたGridの上を押しながら移動(マウスでドラッグ、タッチでスライド)したときのイベントの流れ(IsManipulationEnabledをON/OFF切り替えて実験)

Last updated at Posted at 2020-09-27

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

イベント関連のもくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E9%96%A2%E9%80%A3wpfxaml

やりたいこと

IsManipulationEnabledをONにして、指でピンチイン/アウトをして、拡大縮小の操作ができる、みたいなことをしたが、その際、もともとあった「画面上を指やマウスでドラッグすると動く機能」が、

  • マウスでドラッグしても動かなくなった
  • タッチで動かしても動かなくなった、のに、
  • Stylusペンで動かすと動く

というような動きになってしまった。なんでなのか?確認したい。

コード

重ねたGridの上で、マウス移動やクリック、タッチやタッチ移動、スタイラスで押したり移動するイベントが起きたときにDebug出力に残すようにしたもの。

※画面イメージ
黄色のところが、Gridが2つ重なった部分。
そこをマウスやタッチ、スタイラスで押すor押しながら移動した時にどうなるか見るための実験プログラム。
image.png

MainWindow.xaml
<Window x:Class="WpfApp60.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:WpfApp60"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>

        <Grid x:Name="MainGrid"
              Grid.Row="0"
              Background="Red"
              Margin="50"
              IsManipulationEnabled="False" 
              ManipulationStarting="MainGrid_ManipulationStarting" 
              ManipulationDelta="MainGrid_ManipulationDelta"
              ManipulationCompleted="MainGrid_ManipulationCompleted"

              PreviewMouseDown=             "MainGrid_PreviewMouseDown"             MouseDown="MainGrid_MouseDown"
              PreviewMouseUp=               "MainGrid_PreviewMouseUp"               MouseUp="MainGrid_MouseUp"
              PreviewTouchDown=             "MainGrid_PreviewTouchDown"             TouchDown="MainGrid_TouchDown"
              PreviewTouchUp=               "MainGrid_PreviewTouchUp"               TouchUp="MainGrid_TouchUp"
              PreviewStylusDown=            "MainGrid_PreviewStylusDown"            StylusDown="MainGrid_StylusDown"
              PreviewStylusUp=              "MainGrid_PreviewStylusUp"              StylusUp="MainGrid_StylusUp"
              PreviewMouseLeftButtonDown=   "MainGrid_PreviewMouseLeftButtonDown"   MouseLeftButtonDown="MainGrid_MouseLeftButtonDown"
              PreviewMouseLeftButtonUp=     "MainGrid_PreviewMouseLeftButtonUp"     MouseLeftButtonUp="MainGrid_MouseLeftButtonUp"
              PreviewMouseRightButtonDown=  "MainGrid_PreviewMouseRightButtonDown"  MouseRightButtonDown="MainGrid_MouseRightButtonDown"
              PreviewMouseRightButtonUp=    "MainGrid_PreviewMouseRightButtonUp"    MouseRightButtonUp="MainGrid_MouseRightButtonUp"
              PreviewMouseMove=             "MainGrid_PreviewMouseMove"             MouseMove="MainGrid_MouseMove"
              PreviewTouchMove=             "MainGrid_PreviewTouchMove"             TouchMove="MainGrid_TouchMove"
              PreviewStylusMove=            "MainGrid_PreviewStylusMove"            StylusMove="MainGrid_StylusMove">

            <Grid Margin="60" Background="Yellow"
                PreviewMouseDown="SubGrid_PreviewMouseDown"                          MouseDown="SubGrid_MouseDown"
                PreviewMouseUp="SubGrid_PreviewMouseUp"                              MouseUp="SubGrid_MouseUp"
                PreviewTouchDown="SubGrid_PreviewTouchDown"                          TouchDown="SubGrid_TouchDown"
                PreviewTouchUp="SubGrid_PreviewTouchUp"                              TouchUp="SubGrid_TouchUp"
                PreviewStylusDown="SubGrid_PreviewStylusDown"                        StylusDown="SubGrid_StylusDown"
                PreviewStylusUp="SubGrid_PreviewStylusUp"                            StylusUp="SubGrid_StylusUp"
                PreviewMouseLeftButtonDown="SubGrid_PreviewMouseLeftButtonDown"      MouseLeftButtonDown="SubGrid_MouseLeftButtonDown"
                PreviewMouseLeftButtonUp="SubGrid_PreviewMouseLeftButtonUp"          MouseLeftButtonUp="SubGrid_MouseLeftButtonUp"
                PreviewMouseRightButtonDown="SubGrid_PreviewMouseRightButtonDown"    MouseRightButtonDown="SubGrid_MouseRightButtonDown"
                PreviewMouseRightButtonUp="SubGrid_PreviewMouseRightButtonUp"        MouseRightButtonUp="SubGrid_MouseRightButtonUp"
                PreviewMouseMove="SubGrid_PreviewMouseMove"                          MouseMove="SubGrid_MouseMove"
                PreviewTouchMove="SubGrid_PreviewTouchMove"                          TouchMove="SubGrid_TouchMove"
                PreviewStylusMove="SubGrid_PreviewStylusMove"                        StylusMove="SubGrid_StylusMove"
            />
        </Grid>

        <Button Name="KirikaeBtn" Grid.Row="1"
                Content="IsManipulationEnabled = false" 
                Click="IsManipulationEnabledChange"/>
    </Grid>
</Window>
MainWindow.xaml.cs
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Input;

namespace WpfApp60
{
    public partial class MainWindow : Window
    {
        public MainWindow() => InitializeComponent();

        private void SubGrid_Click(object sender, RoutedEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewTouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_TouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewStylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); /* e.Handled = true; */ }
        private void SubGrid_StylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_ManipulationStarting(object sender, ManipulationStartingEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewTouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_TouchDown(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewStylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_StylusDown(object sender, StylusDownEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewTouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_TouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewStylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_StylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseUp(object sender, MouseButtonEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewTouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_TouchUp(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewStylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_StylusUp(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewMouseMove(object sender, MouseEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_MouseMove(object sender, MouseEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewTouchMove(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_TouchMove(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_PreviewStylusMove(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void MainGrid_StylusMove(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewMouseMove(object sender, MouseEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_MouseMove(object sender, MouseEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewTouchMove(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_TouchMove(object sender, TouchEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_PreviewStylusMove(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }
        private void SubGrid_StylusMove(object sender, StylusEventArgs e) { Debug.WriteLine(MethodBase.GetCurrentMethod().Name); }

        private void IsManipulationEnabledChange(object sender, RoutedEventArgs e)
        {
            if (MainGrid.IsManipulationEnabled)
            {
                MainGrid.IsManipulationEnabled = false;
                KirikaeBtn.Content = "false";
            }
            else
            {
                MainGrid.IsManipulationEnabled = true;
                KirikaeBtn.Content = "true";
            }
        }

    }
}

①動作確認(IsManipulationEnabledはOFF)

①-1 指でタッチ、タッチのままスライドし、離したときのイベントの流れ

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove
MainGrid_PreviewStylusDown
SubGrid_PreviewStylusDown
SubGrid_StylusDown
MainGrid_StylusDown
MainGrid_PreviewTouchDown
SubGrid_PreviewTouchDown
SubGrid_TouchDown
MainGrid_TouchDown

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
SubGrid_PreviewTouchMove
SubGrid_TouchMove
MainGrid_TouchMove

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
SubGrid_PreviewTouchMove
SubGrid_TouchMove
MainGrid_TouchMove

   ・
   ・繰り返し
   ・
   ・

MainGrid_PreviewMouseRightButtonDown
MainGrid_PreviewMouseDown
SubGrid_PreviewMouseRightButtonDown
SubGrid_PreviewMouseDown
SubGrid_MouseRightButtonDown
SubGrid_MouseDown
MainGrid_MouseRightButtonDown
MainGrid_MouseDown


MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
SubGrid_PreviewTouchMove
SubGrid_TouchMove
MainGrid_TouchMove

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
SubGrid_PreviewTouchMove
SubGrid_TouchMove
MainGrid_TouchMove

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove

   ・
   ・MouseRightButtonDown、MouseDownが来た後は、
   ・MouseMove系のイベントも加わり繰り返し
   ・(Mouse、Stylus、TouchのUp系イベントが来るまで)
   ・※ただし、PreviewMouseMove系はなぜかたまに抜けるときがある
   ・
   ・


MainGrid_PreviewStylusUp
SubGrid_PreviewStylusUp
SubGrid_StylusUp
MainGrid_StylusUp
MainGrid_PreviewTouchUp
SubGrid_PreviewTouchUp
SubGrid_TouchUp
MainGrid_TouchUp
MainGrid_PreviewMouseRightButtonUp
MainGrid_PreviewMouseUp
SubGrid_PreviewMouseRightButtonUp
SubGrid_PreviewMouseUp
SubGrid_MouseRightButtonUp
SubGrid_MouseUp
MainGrid_MouseRightButtonUp
MainGrid_MouseUp

image.png
※上向き矢印:トンネリングイベント 下向き矢印:バブリングイベント

①-2 マウスで左クリック、そのままドラッグし、離したときのイベントの流れ

MainGrid_PreviewMouseLeftButtonDown
MainGrid_PreviewMouseDown
SubGrid_PreviewMouseLeftButtonDown
SubGrid_PreviewMouseDown
SubGrid_MouseLeftButtonDown
SubGrid_MouseDown
MainGrid_MouseLeftButtonDown
MainGrid_MouseDown

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove

  ・
  ・Up系イベントが来るまで
  ・繰り返し
  ・

MainGrid_PreviewMouseLeftButtonUp
MainGrid_PreviewMouseUp
SubGrid_PreviewMouseLeftButtonUp
SubGrid_PreviewMouseUp
SubGrid_MouseLeftButtonUp
SubGrid_MouseUp
MainGrid_MouseLeftButtonUp
MainGrid_MouseUp

image.png
※上向き矢印:トンネリングイベント 下向き矢印:バブリングイベント

②動作確認(IsManipulationEnabledはON)

②-1 指でタッチ、タッチのままスライドし、離したときのイベントの流れ

MainGrid_PreviewMouseMove
SubGrid_PreviewMouseMove
SubGrid_MouseMove
MainGrid_MouseMove
MainGrid_PreviewStylusDown
SubGrid_PreviewStylusDown
SubGrid_StylusDown
MainGrid_StylusDown
MainGrid_PreviewTouchDown
SubGrid_PreviewTouchDown
SubGrid_TouchDown
MainGrid_TouchDown

MainGrid_ManipulationStarting

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
MainGrid_TouchMove              //★TouchMoveがSubGridまでいかない!!

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
MainGrid_TouchMove

MainGrid_ManipulationDelta

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
MainGrid_TouchMove

MainGrid_ManipulationDelta

MainGrid_PreviewStylusMove
SubGrid_PreviewStylusMove
SubGrid_StylusMove
MainGrid_StylusMove

MainGrid_PreviewTouchMove
MainGrid_TouchMove

MainGrid_ManipulationDelta

  ・
  ・Up系イベントが来るまで繰り返し
  ・
  ・

MainGrid_PreviewStylusUp
SubGrid_PreviewStylusUp
SubGrid_StylusUp
MainGrid_StylusUp

MainGrid_PreviewTouchUp
MainGrid_TouchUp

MainGrid_ManipulationCompleted

image.png
※上向き矢印:トンネリングイベント 下向き矢印:バブリングイベント

②-2 マウスで左クリック、そのままドラッグし、離したときのイベントの流れ

IsManipulationEnabledがOFFのときの結果(ログ)と全く同じになった。
マウスで操作した場合は、IsManipulationEnabledのONOFFはイベントの流れに関係ないっぽい。

①-1と②-1を比較して、IsManipulationEnabledのONOFFでイベントの流れがどう変わるか見る

image.png

結果

上の「やりたいこと」で「なんでなのか調べる」と言っていた下記の件は、それぞれこういうことだった。

マウスでドラッグしても動かなくなった、のはなぜ?

IsManipulationEnabledをONにしていると、ONにしたコントロールの子コントロールのマウスのDown系/Move系イベントが来なくなる。
そのため、子コントロールのMouseMoveに書いていた「ドラッグしたときの動作」が行われなくなっていた。

タッチで動かしても動かなくなった

同様に、IsManipulationEnabledをONにしていると、ONにしたコントロールの子コントロールのタッチのMove系イベントが来なくなる。
そのため、子コントロールのTouchMoveに書いていた「タッチで動かした(指でスライドした)ときの動作」が行われなくなっていた。

Stylusペンで動かすと動く

Stylus系のイベントは、IsManipulationEnabledがONでもOFFでも、Donn/Move/Upのいずれも同じように来る。そのため、Stylusの操作は変わりなく実施できていた。
(投稿後に追記、よく考えると、stylusを使えるデバイスがないので、Stylusで動作したときにイベントがどう来るか、この実験コードで見れていない。そのため、この結果については現状「推測」です)

結果からみる、実装時に注意したほうがよいこと

  • IsManipulationEnabledをONすると来なくなるイベント
  • IsManipulationEnabledがONでもOFFでも来るイベント

がある。
IsManipulationEnabledをONにして、ピンチイン/アウトの操作を実現する際に、
マウスやタッチ、スタイラスでの操作も併用したい場合は、上記結果を見てどこになにを書くか、またそれ以前に、どういう仕様にするか、をよく考える必要がある。

→どんなデバイス(指を含む)でも動くようにする、という仕様にせず、この場合は指、この場合はマウス、それ以外はマウス、というように、同時に使うデバイスを限定するような仕様にする方がバグりにくそうに思う。

備考

IsManipulationEnabledとは関係ない部分でも、ルーティングイベントにはいろいろ強めのクセがある気がする。
その辺は別で見たので、こちらのルーティングイベント関連の目次を参照。

参考

MSかずきさん イベント解説
https://qiita.com/okazuki/items/43e98bf7107c3e710177

0
1
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
0
1