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

.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();
    }
}


コメント

このブログの人気の投稿

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の方が速いかもね  

.NET MAUI BLE(Bluetooth Low Energy)も上手く行った

2023/10/11更新 ↓こちらの記事で更新しれました。 .NET MAUI PLUGIN BLEがWindows(10/11)に対応してた Windows対応 イヤッッホォォォオオォオウ Bluetooth LE plugin for Xamarin &amp; MAUI がWindowsに対応してた~ nugetでver3.00をみんな早速ゲットだ。   ちなみにこちらはAndroid版のサンプルアプリ   GitHub...   [ブログカード風リンクタグ作成] ------------------------------------------------------------------------------ 昔、スマホとESP32の接続確認用に作ったプログラムをXamarinからMAUIに移植したら動いちゃった   まあ、ESP32からスマホへの一方通行なアプリなんだけど 面倒くさいので github に公開した 下手くそなコード書きやがってとか思われそう 「間違ってるぞこのやろう」というのを見つけたら教えてくれると嬉しいです 要素技術の調査はこれで完了かな

.NET MAUIでスプラッシュスクリーン Android12で地獄を見る

まずは起動するところから・・・・  スプラッシュスクリーンだぬ マイクロソフトのサイト 見てると簡単そう(実際簡単で細かい事を気にしなければsvgファイルを用意して1行だけ書き換えておしまい)   なんかプロジェクトファイルに自動で記述されるらしい よし、プロジェクト作った                     つーか、もうスプラッシュがあるんだけど・・・・ そして自作のsvgファイル くうっ・・・デザインセンスない・・・・   それはともかく、このSVGファイルを Resources\Images にドラッグ&ドラッグ プロジェクトを右クリックして「プロジェクトファイルの編集」 して、編集できるようになったプロジェクトファイルを 自分のプロジェクトファイルに書き換える <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="168,168" /> ↓ <MauiSplashScreen Include="Resources\Splash\ splashtestmaui .svg" Color="#512BD4" BaseSize="168,168" />   とりあえずAndroidで実行 お、おう・・・・ まあ最初はこんなもんよね 最初に紹介したマイクロソフトのサイトでもBaseSizeを書き換えてくださいって言ってるし 言われたとおりに <MauiSplashScreen Include="Resources\Splash\splashtestmaui.svg" Color="#512BD4" BaseSize=" 320,600 " /> 書き換えてみると いい感じじゃーん じゃあアンドロイドのバージョン毎に試してみよう   Android 7 Android 11 Android 12 Android 13