WPF DataGridに適用されたときに、コンテキストメニュー(または既存の項目を追加)を追加するSystem.Windows.Interactivity.Behaviourを考え出しています。コンテキストメニュー)を使用して、列を表示または非表示にすることができます。WPF DataGridColumnHeader:列を非表示または再表示した後にContextMenuが消える
私は、ほぼがうまく機能するという解決策を考え出しました。 すべては、列を非表示にしてから再表示するまで、期待通りに機能します。もう一度目に見えるようになると、コンテクストメニューは消えるように見えますが、右クリックするだけでもう何もしません。私がやっているverbalyl
コードは、以下の通りです:
- 行動を取り付けるには、私はLoadedイベントには、「ロード」のDataGridイベント
- を聴き始める、私はすべてのDataGridColumnHeaderの子孫を見つけますDataGrid
- 個々のコンテキストメニューを生成し、DataGridColumnHeaderにアタッチします
- 各コンテキストメニューに対して、1つの列につき1つのメニュー項目を生成し、実行時に設定するコマンドを割り当てますDataGridColumnの可視性私は最も単純なケースのために最低限の例にコードをストリップダウンしました
表示または非表示に:これをテストするには、単にのContextMenuは、現在割り当てられていないのDataGridにその動作を適用します。
public class DgColumnBehavior : Behavior<DataGrid>
{
protected ICommand ToggleColumnVisibilityCmd;
protected DataGrid _AssociatedObject;
protected override void OnAttached()
{
this.ToggleColumnVisibilityCmd = new DelegateCommand<DataGridColumn>(ToggleColumnVisibilityCmdExecute);
this._AssociatedObject = (DataGrid)this.AssociatedObject;
Observable.FromEventPattern(this._AssociatedObject, "Loaded")
.Take(1)
.Subscribe(x => _AssociatedObject_Loaded());
base.OnAttached();
}
void _AssociatedObject_Loaded()
{
var columnHeaders = this._AssociatedObject.SafeFindDescendants<DataGridColumnHeader>(); // see second code piece for the SafeFindDescendants extension method
foreach (var columnHeader in columnHeaders)
{
EnsureSeparateContextMenuFor(columnHeader);
if (columnHeader.ContextMenu.ItemsSource != null)
{
// ContextMenu has an ItemsSource, so need to add items to that -
// ommitted though as irrelevant for example
}
else
{
// No ItemsSource assigned to the Menu, so we can just add directly
foreach (var item in CreateMenuItemsFor(columnHeader))
columnHeader.ContextMenu.Items.Add(item);
}
}
}
/// Ensures that the columnHeader ...
/// A) has a ContextMenu, and
/// B) that is has an individual context menu, i.e. one that isn't shared with any other DataGridColumnHeaders.
///
/// I'm doing that as in practice, I'm adding some further items that are specific to each column, so I can't have a shared context menu
private void EnsureSeparateContextMenuFor(DataGridColumnHeader columnHeader)
{
if (columnHeader.ContextMenu == null)
{
columnHeader.ContextMenu = new ContextMenu();
}
else
{
// clone the existing menu
// ommitted as irrelevant for example
}
}
/// Creates one menu item for each column of the underlying DataGrid to toggle that column's visibility
private IEnumerable<FrameworkElement> CreateMenuItemsFor(DataGridColumnHeader columnHeader)
{
foreach (var column in _AssociatedObject.Columns)
{
var item = new MenuItem();
item.Header = String.Format("Toggle visibility for {0}", column.Header);
item.Command = ToggleColumnVisibilityCmd;
item.CommandParameter = column;
yield return item;
}
}
// Gets executed when the user clicks on one of the ContextMenu items
protected void ToggleColumnVisibilityCmdExecute(DataGridColumn column)
{
bool isVisible = (column.Visibility == Visibility.Visible);
Visibility newVisibility = (isVisible) ? Visibility.Hidden : Visibility.Visible;
column.Visibility = newVisibility;
}
}
SafeFindDescendants伸長法は大きく、ここから1に基づいています。私は何が起こっているのを把握することはできませんDataGridColumnHeader ContextMenu programmatically
public static class Visual_ExtensionMethods
{
public static IEnumerable<T> SafeFindDescendants<T>(this Visual @this, Predicate<T> predicate = null) where T : Visual
{
if (@this != null)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(@this);
for (int i = 0; i < childrenCount; i++)
{
var currentChild = VisualTreeHelper.GetChild(@this, i);
var typedChild = currentChild as T;
if (typedChild == null)
{
var result = ((Visual)currentChild).SafeFindDescendants<T>(predicate);
foreach (var r in result)
yield return r;
}
else
{
if (predicate == null || predicate(typedChild))
{
yield return typedChild;
}
}
}
}
}
}
。列を隠したり再表示した後にコンテキストメニューが表示されないのはなぜですか?
アイデアを気に入ってください!ありがとう。
こんにちは、ないよりよい解決策: は、これが私の解決策(私もLayoutUpdatedイベントをリッスンしています)です。私が上に投稿した修正はこれまでのところうまくいくようです。 コードを再入力してください:LayoutUpdatedはたくさんの名前で呼ばれています。カラムのサイズを変更しながら何回も必要なときに最初のイベントの発生を聞き始め(=>列の可視性が変更された)、イベントからの再登録を解除します( ".Take(1)")。ソリューションでパフォーマンスの問題が発生した場合は、同様のことを行うことをお勧めします。実装はあなたのプロジェクトと要件に少し依存する – Bogey
あなたのヒントありがとう!あなたは正しかった、それは非常に頻繁に呼び出されます! Visibilityが変更されたとき(menuItem.Click())、呼び出された後にイベントの登録を解除する(直後に何が行われるのか)、今聞いています。 –