Bagikan melalui


Cara: Menangani Peristiwa ContextMenuOpening

Peristiwa ContextMenuOpening dapat ditangani dalam aplikasi untuk menyesuaikan menu konteks yang ada sebelum menampilkan atau untuk menekan menu yang akan ditampilkan dengan mengatur Handled properti ke true dalam data peristiwa. Alasan umum untuk mengatur Handled ke true dalam data peristiwa adalah untuk mengganti menu sepenuhnya dengan objek baru ContextMenu , yang terkadang mengharuskan membatalkan operasi dan memulai pembukaan baru. Jika Anda menulis handler untuk peristiwa tersebut ContextMenuOpening , Anda harus mengetahui masalah waktu antara ContextMenu kontrol dan layanan yang bertanggung jawab untuk membuka dan memosisikan menu konteks untuk kontrol secara umum. Topik ini menggambarkan beberapa teknik kode untuk berbagai skenario pembukaan menu konteks dan menggambarkan kasus di mana masalah waktu mulai dimainkan.

Ada beberapa skenario untuk menangani peristiwa:ContextMenuOpening

  • Menyesuaikan item menu sebelum ditampilkan.

  • Mengganti seluruh menu sebelum ditampilkan.

  • Sepenuhnya menekan menu konteks yang ada dan tidak menampilkan menu konteks.

Contoh

Menyesuaikan Item Menu Sebelum Ditampilkan

Menyesuaikan item menu yang ada cukup sederhana dan mungkin merupakan skenario yang paling umum. Anda dapat melakukan ini untuk menambahkan atau mengurangi opsi menu konteks sebagai respons terhadap informasi status saat ini di aplikasi Anda atau informasi status tertentu yang tersedia sebagai properti pada objek tempat menu konteks diminta.

Teknik umumnya adalah mendapatkan sumber peristiwa, yang merupakan kontrol spesifik yang diklik kanan, dan mendapatkan ContextMenu properti darinya. Anda biasanya ingin memeriksa Items koleksi untuk melihat item menu konteks apa yang sudah ada di menu, lalu menambahkan atau menghapus item baru MenuItem yang sesuai ke atau dari koleksi.

void AddItemToCM(object sender, ContextMenuEventArgs e)
{
    //check if Item4 is already there, this will probably run more than once
    FrameworkElement fe = e.Source as FrameworkElement;
    ContextMenu cm = fe.ContextMenu;
    foreach (MenuItem mi in cm.Items)
    {
        if ((String)mi.Header == "Item4") return;
    }
    MenuItem mi4 = new MenuItem();
    mi4.Header = "Item4";
    fe.ContextMenu.Items.Add(mi4);
}

Mengganti Seluruh Menu Sebelum Tampilan

Skenario alternatif adalah jika Anda ingin mengganti seluruh menu konteks. Anda tentu saja juga dapat menggunakan variasi kode sebelumnya, untuk menghapus setiap item menu konteks yang ada dan menambahkan yang baru dimulai dengan item nol. Tetapi pendekatan yang lebih intuitif untuk mengganti semua item di menu konteks adalah membuat baru ContextMenu, mengisinya dengan item, dan kemudian mengatur FrameworkElement.ContextMenu properti kontrol menjadi yang baru ContextMenu.

Berikut ini adalah kode handler sederhana untuk mengganti .ContextMenu Kode mereferensikan metode kustom BuildMenu , yang dipisahkan karena dipanggil oleh lebih dari salah satu contoh handler.

void HandlerForCMO(object sender, ContextMenuEventArgs e)
{
    FrameworkElement fe = e.Source as FrameworkElement;
    fe.ContextMenu = BuildMenu();
}
ContextMenu BuildMenu()
{
    ContextMenu theMenu = new ContextMenu();
    MenuItem mia = new MenuItem();
    mia.Header = "Item1";
    MenuItem mib = new MenuItem();
    mib.Header = "Item2";
    MenuItem mic = new MenuItem();
    mic.Header = "Item3";
    theMenu.Items.Add(mia);
    theMenu.Items.Add(mib);
    theMenu.Items.Add(mic);
    return theMenu;
}

Namun, jika Anda menggunakan gaya handler ini untuk ContextMenuOpening, Anda berpotensi mengekspos masalah waktu jika objek tempat Anda mengatur ContextMenu tidak memiliki menu konteks yang sudah ada sebelumnya. Saat pengguna mengklik kanan kontrol, ContextMenuOpening dinaikkan meskipun yang ada ContextMenu kosong atau null. Tetapi dalam hal ini, apa pun yang baru ContextMenu Anda tetapkan pada elemen sumber tiba terlambat untuk ditampilkan. Selain itu, jika pengguna kebetulan mengklik kanan untuk kedua kalinya, kali ini baru ContextMenu Anda muncul, nilainya bukan null, dan handler Anda akan mengganti dan menampilkan menu dengan benar saat handler berjalan untuk kedua kalinya. Ini menunjukkan dua kemungkinan solusi:

  1. Pastikan bahwa ContextMenuOpening handler selalu berjalan terhadap kontrol yang memiliki setidaknya tempat penampung ContextMenu yang tersedia, yang ingin Anda ganti dengan kode handler. Dalam hal ini, Anda masih dapat menggunakan handler yang ditampilkan dalam contoh sebelumnya, tetapi Biasanya Anda ingin menetapkan tempat penampung ContextMenu dalam markup awal:

    <StackPanel>
      <Rectangle Fill="Yellow" Width="200" Height="100" ContextMenuOpening="HandlerForCMO">
        <Rectangle.ContextMenu>
          <ContextMenu>
            <MenuItem>Initial menu; this will be replaced ...</MenuItem>
          </ContextMenu>
        </Rectangle.ContextMenu>
      </Rectangle>
      <TextBlock>Right-click the rectangle above, context menu gets replaced</TextBlock>
    </StackPanel>
    
  2. Asumsikan bahwa nilai awal ContextMenu mungkin null, berdasarkan beberapa logika awal. Anda dapat memeriksa ContextMenu null, atau menggunakan bendera dalam kode Anda untuk memeriksa apakah handler Anda telah dijalankan setidaknya sekali. Karena Anda berasumsi bahwa ContextMenu akan ditampilkan, handler Anda kemudian diatur Handled ke true dalam data peristiwa. ContextMenuService Untuk yang bertanggung jawab atas tampilan menu konteks, true nilai untuk Handled dalam data peristiwa mewakili permintaan untuk membatalkan tampilan untuk menu konteks / kombinasi kontrol yang menaikkan peristiwa.

Sekarang setelah Anda menekan menu konteks yang berpotensi mencurigai, langkah selanjutnya adalah menyediakan yang baru, lalu menampilkannya. Mengatur yang baru pada dasarnya sama dengan handler sebelumnya: Anda membangun yang baru ContextMenu dan mengatur properti sumber FrameworkElement.ContextMenu kontrol dengannya. Langkah tambahannya adalah Anda sekarang harus memaksa tampilan menu konteks, karena Anda menekan upaya pertama. Untuk memaksa tampilan, Anda mengatur Popup.IsOpen properti ke true dalam handler. Berhati-hatilah saat Anda melakukan ini, karena membuka menu konteks di handler akan menaikkan ContextMenuOpening acara lagi. Jika Anda memasukkan kembali handler Anda, itu menjadi rekursif tanpa batas. Inilah sebabnya mengapa Anda selalu perlu memeriksa null atau menggunakan bendera jika Anda membuka menu konteks dari dalam ContextMenuOpening penanganan aktivitas.

Menekan Menu Konteks yang Ada dan Menampilkan Menu Tanpa Konteks

Skenario terakhir, menulis handler yang menekan menu secara total, jarang terjadi. Jika kontrol tertentu tidak seharusnya menampilkan menu konteks, mungkin ada cara yang lebih tepat untuk memastikan ini daripada dengan menekan menu tepat ketika pengguna memintanya. Tetapi jika Anda ingin menggunakan handler untuk menekan menu konteks dan tidak menampilkan apa pun, maka handler Anda hanya boleh mengatur Handled ke true dalam data peristiwa. ContextMenuService Yang bertanggung jawab untuk menampilkan menu konteks akan memeriksa data peristiwa peristiwa yang diangkat pada kontrol. Jika peristiwa ditandai Handled di mana saja di sepanjang rute, maka menu konteks membuka tindakan yang memulai peristiwa ditekan.

    void HandlerForCMO2(object sender, ContextMenuEventArgs e)
    {
        if (!FlagForCustomContextMenu)
        {
            e.Handled = true; //need to suppress empty menu
            FrameworkElement fe = e.Source as FrameworkElement;
            fe.ContextMenu = BuildMenu();
            FlagForCustomContextMenu = true;
            fe.ContextMenu.IsOpen = true;
        }
    }
}

Baca juga