スキップしてメイン コンテンツに移動

.NET MAUIでドラッグ&ドロップ(したいわけじゃ無いけど、これしかなかった)

解決出来ないってグダグダ書いてしまったけど、解決方法がわかった
解決方法はこちら

 

本当はPANで実現しようと思ったのだけど、リリースした場所を自分で計算しないといけないようなので面倒なのでドラッグ&ドロップでごまかそうという話

実は俺がわかってないだけかもしれないけど

 


この人の動画にはずいぶん助けられた
もはや、アプリのコーディングは英語圏のページや動画を頼らないと無理なんだな

ほら、よくあるじゃ無い、スマホである項目を別の項目に移動して入れ替えるというやつ
あれをやりたいのよ。
本当はD&DじゃなくてPANで実現するのが正しそうなのだけどね・・・

 

ドラッグ&ドロップ

↑こんな感じのでお試しプログラムを作ってみた
要は、上の画像のイメージ画像をHello,World!のところに移動して、
上に乗っかられてしまったHello,World!をイメージ画像のところに追い出すというのを繰り返す

Visual Studioで.NET MAUIのプロジェクトをいつものように作ったら
まずはxamlの書換

  • Borderを削除
  • StackLayoutをグリッドに変更
  • Gridの設定をする(ColumnDefinitions,RowDefinition,x:Name)
  • 各コントロールにGridの位置を設定
  • イメージコントロールとラベルに名称(x:Name)
  • イメージコントロールにジェスチャー登録(Image.GestureRecognizers)
  • イメージコントロールのジェスチャーにドラック開始イベント(DragGestureRecognizer)
  • ドロップを受け入れるラベルにジェスチャー登録(Label.GestureRecognizers)
  • ラベルにドロップイベント(DropGestureRecognizer)
  • 後は・・・まあ適当に
    <!-- StackLayoutをグリッドに変更 -->
    <Grid x:Name="maingrid"  >

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
 
        <!-- imageにNameとClassIdを追加 grid位置も適当に決める -->
        <Image Grid.Column="3" Grid.Row="0" x:Name="bot" ClassId="bot"
            Source="dotnet_bot.png"
            SemanticProperties.Description="Cute dot net bot waving hi to you!"
            HeightRequest="200"
            HorizontalOptions="Center" >
            <Image.GestureRecognizers>
                <DragGestureRecognizer  DragStarting="DragGestureRecognizer_DragStarting"/>
            </Image.GestureRecognizers>
        </Image>

        <!-- LebelにNameとClassIdを追加 grid位置も適当に決める -->
        <Label Grid.Column="2" Grid.Row="1" x:Name="lbl1"  ClassId="lbl1"
            Text="Hello, World!"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="32"
            HorizontalOptions="Center" >
            <Label.GestureRecognizers>
                <DropGestureRecognizer  Drop="DropGestureRecognizer_Drop" />
            </Label.GestureRecognizers>
        </Label>

        <Label  Grid.Column="1" Grid.Row="2"
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                FontSize="18"
                HorizontalOptions="Center" >
        </Label>

        <Button Grid.Column="0" Grid.Row="3"
                x:Name="CounterBtn"
                Text="Click me"
                SemanticProperties.Hint="Counts the number of times you click"
                Clicked="OnCounterClicked"
                HorizontalOptions="Center" >
        </Button>

    </Grid>


CSのソースがこちら
元のテンプレートに色々追加

  • ラベルの書き戻し用変数(string _strOrg)
  • ラベルを書き戻すためのタイマー(Timer _tim using Timer = System.Timers.Timer;も必要 )
  • コンストラクタの中にタイマーの各種設定
  • changeTextメソッド(強制的に書き換えられたテキストを書き戻す)
  • タイマー発動イベント(_tim_Elapsed)
  • DragGestureRecognizer_DragStartingにコピー元データ(今回は捨てる)
  • DropGestureRecognizer_Dropに、移動などのコードを記述

メインはDropGestureRecognizer_Dropなのだけど、アニメの移動やなんかを実行してます
・・・・が、困っちゃうのがLabelのTextを何が何でも書き換えようとするのよね

なので、しょうがないので今回は一時的に待避してタイマーで書き戻すなんとことをやってるのよね
本当はPanGestureRecognizerを使うべき何だろうけど、リリースした場所のコントロールの判定を位置情報を自分で計算せねばならないのだよな

もしかしたら、俺が知らないだけで、PanGestureRecognizerを使った方法があるのかもしれないけど
スマートじゃ無いなぁ・・・・

誰か本当の方法を教えてください

まあデータのコピーしたいだけなら、D&Dでさらっとコピーできるので悩まないでいいのだけどコントロールの移動の本当の方法はどうやるのでしょう 

まあ、そんなわけでMAUI盤のえるいーだーはこんなトリッキーな方法でこの操作を実現するかも

public partial class MainPage : ContentPage
{
    /// <summary>サンプルに最初からあるカウンタ</summary>
    int count = 0;

    /// <summary>元のラベルの文字を一時保存する</summary>
    string _strOrg;
    /// <summary>タイマー</summary>
    Timer _tim;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainPage()
    {
        //タイマーの設定をする
        _tim = new Timer();
        _tim.Interval = 10;
        _tim.Elapsed += _tim_Elapsed;
        _tim.AutoReset = false; //タイマーはstart後に1回だけ発動

        InitializeComponent();
    }


    /// <summary>
    /// ラベルを書き戻すためにタイマーから呼ばれる
    /// </summary>
    private void changeText()
    {
        lbl1.Text = _strOrg;    //ラベルの書き戻し
        lbl1.IsEnabled = true;
    }

    /// <summary>
    /// タイマー発動
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void _tim_Elapsed(object sender, ElapsedEventArgs e)
    {
        //WPF上のスレッドに changeTextメソッドの操作を委任
        MainThread.BeginInvokeOnMainThread(changeText);
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        count++;

        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);
    }

    /// <summary>
    /// イメージコントロールのドラッグスタート
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void DragGestureRecognizer_DragStarting(object sender, DragStartingEventArgs e)
    {
        e.Data.Text = "開始"; //本来はコピー先に送りたいデータを書く
        Debug.WriteLine("ドラッグスタート");
    }

    /// <summary>
    /// イメージコントロールのドロップ
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void DropGestureRecognizer_Drop(object sender, DropEventArgs e)
    {
        _strOrg = lbl1.Text;                        //ラベルの文字を一旦保存
       
        double x = bot.X - lbl1.X;                  //イメージコントロールの移動量
        double y = bot.Y - lbl1.Y;

        await lbl1.TranslateTo(x, y, 100);          //ラベルをイメージのあった位置にゆっくり移動
        await bot.TranslateTo(-x, -y, 100);         //イメージコントロールをラベルの会った位置にゆっくりいどう

        string text = await e.Data.GetTextAsync();  //ドラッグ元から持ってきたデータ、いらないのだけどドロップすると無理やりラベルにコピーされる
                                                    //今回はあっても無くても一緒

        //イメージコントロールの元々あったGrid上の位置
        int botx = maingrid.GetColumn(bot);
        int boty = maingrid.GetRow(bot);

        //ラベルコントロールの元々あったGrid上の位置
        int lblx = maingrid.GetColumn(lbl1);        
        int lbly = maingrid.GetRow(lbl1);

        //Grid上の位置を書換(イメージコントロール)
        maingrid.SetColumn(bot, lblx);
        maingrid.SetRow(bot, lbly);
        bot.TranslationX = 0;                       //移動量は0に戻しておく
        bot.TranslationY=0;

        //Grid上の位置を書換(ラベルコントロール)
        maingrid.SetColumn(lbl1, botx);
        maingrid.SetRow(lbl1, boty);
        lbl1.TranslationX= 0;                       //移動量は0に戻しておく
        lbl1.TranslationY= 0;
       
        //lbl1.Text = "移動中";
        lbl1.IsEnabled= false;
        Debug.WriteLine("イメージのドロップ");

        //タイマー開始(ラベル上に勝手に書き戻された文字列をタイマーから書き戻す)
        _tim.Start();
    }
}


コメント

このブログの人気の投稿

ImDiskの後継アプリのAIM toolkitを試してみた

ImDiskの開発辞めちゃったらしい というわけで、じゃあ後継ソフトはあるのかと思ったらあった 遅いと言われているけど・・・ とインストールしてみた ImDiskのこの間の結果はこちら 再びRAM DISK(ImDisk)   blog.mazepin-led.com    そして今回のAIM Toolkit なんか、無茶苦茶遅くなってるな 下手するとSSDの方が速いじゃん CPUの使用状態はこんな感じ PIO転送なところは変わって無さそう まあ、遅いからと言ってその速度が体感できるのかというと出来ないんだけどね と思ったところで、なんか設定変えてみたらどうなるのだろう Allocate Memory Dynamicallyというのは、メモリを必要に応じてってことだからと思って以前試したことがあったけど、なんかImDiskのときは不安定だったんだよな  AdvancedのとこにあるUse AWE Physical Memoryというのが良くわからないけど、チェックしてみたら速くなった おお、大分速くなった。なったけど・・・・なんで? これだとImDiskよりちょっと遅いくらいになるのか もしやQuickFormatととかでも変わるのか?と思ったけどそこまでやる気も無かったので放置。速くなるわけないよね。 後は圧縮とか、取り外しメディアとかだし。   最近の性能の良いSSDのおかげで RAM DISKというものの存在意義が薄れちゃったね まあそれでとにかく速度を稼ぎたいぜって人は SoftPerfect RamDiskでも使ってください 昔はPrimo Ramdisk使ってました。当時使ってたけどなかなか良かった。 当時SSD無茶苦茶高かったし。 今はほぼ無用になってしまったが・・・ 未だにスタンダード版は8GBまでなんだな キャッシュ領域にしたいなら大人しく余ってるSSDを使った方がいいんじゃ無いかって気がするけど  おしまい

RAM DISKを使ってみた(使ったのはImDisk)

GWだし、まあちょっとラムディスクを入れてみました うちのPCはWindows11 使ったのはImDiskというRAM Disk。 まあ、この辺のインストールとかはあちこちで解説してる人がいるので適当にぐぐってくださいな で、とりあえずベンチマーク なかなかいいスピードだ で、大抵の人はブラウザのキャッシュをRAMディスクにするといいよ・・・と言うけど そもそもメインドライブがNVMeのSSDを使っている状態で、体感速度なんか上がらない(使い終わったキャッシュを再起動したら綺麗さっぱり捨て去ってくれるという利点はある)  うちで一番効果があるのは Adobe Audition というアプリ これが結構高速化する(キャッシュをちゃんとRAMディスクにしたら・・・だけど) ハイレゾ音源だと、1時間の音源が何かする度に4GBのファイルを作られてしまう なので、RAM DISKにすると、結構編集時間を短縮できる Premiere Rushも出力先をRam Diskにしておいて終わったら、SSDにコピーすると言う事をやるとかなりスピードアップになる 実はうちのPCは普段は99%のパワーで動作していて、CPUのターボブーストが掛からないようになっている 大体3.6GHz当たりで安定してるのだけど、これを100%にするとターボブースト機能がONになって一部のコアが4.5とか4.8GHzまで上がる まあ、毎回電源オプションをいじる事になるのだけどさ・・・ そうしてベンチを取ると こんな感じ とは言え、ブーストしてるからと行ってRam Diskのスピードの差を体感する事はさすがに無理 ・・・と言うかフォトショでもRAM DISKにしてよかった・・・と言うほど変わらない SSDの性能が上がってきたしもし次にPCを買い換えたらRAM DISKよりSSDの方が速いかもね  

QCC dongle proを買った→Windows11で接続までのみちのり

何があった  買ったけど最初の接続に手間取ったので そのメモ QuestyleのQCC dongle proを買ったのだけど、使っているイヤホンと接続できない なんでー? ペアリングしてるのに 説明書 説明書には外装のQRコードを読むように書いてあるのだけど そもそもpage not found 404になる   Windowsには・・・ ちゃんと  ヘッドホンのところには QCC Dongle Pro って出てるのよね しかし音が出ない つーか、新しいペアリングはどうやるのだ  スマホが必要でした 何だそりゃっていいたくなるがどうやらまず、スマホにQCC dongle Pro接続して専用アプリで ドングルとイヤホンをペアリングする必要があった 何だそりゃー Google PlayでQuestyleで検索かけるか こちら でまずはインストール そしてスマホにドングルを刺して、このアプリ内からイヤホンをペアリング そしてそのドングルをWindows11の空いてるUSB TYPE-Cポートに突き刺す   音が出た! やったね  結局音が良くなった?  どうなんだろうね 正直言ってよくわからん ただ、マイケルジャクソンのThe Jamのオープニングのガラスの割れる音は 今までよりも細かくパリパリ聞こえるようになったから効果はあったんだと思う それと遅延が少なくなった  finalのZE3000 SVというイヤホンを使っていたのだけど今までのただのUSBドングルで YouTubeを見てると明らかに音と画面のタイミングがずれてて、 Bluetoothイヤホンってこんなもんかと表他のだけどそれが亡くなったのはとても快適 LDACは遅延が酷いとか書いてあったからビビってたんだけどね(でも有線イヤホン使うと、あ、これが合ってる状態なんだってなるので遅延はあるみたい)   あと、たまにイヤホンと繋がらなくなるねぇ 相性問題というやつなのだろうか    今までのUSBドングルが捨てられない 最期に罠が一つ そりゃ無いぜって言いたくなるのが今までのUSBドングルが捨てられないこと このQCC dongle pro はWindowsからはイヤホンとして認識されるので他のBluetoo...