我試圖想出一個System.Windows.Interactivity.Behaviour,當應用於WPF DataGrid,添加一個上下文菜單(或項目到現有的上下文菜單),允許用戶顯示或隱藏列。WPF DataGridColumnHeader:隱藏/重新顯示列後ContextMenu消失
我想出了一個解決方案,差不多工作得很好。 一切都按預期工作 - 直到您隱藏然後重新顯示一列。一旦再次變得可見,上下文菜單似乎消失,右鍵單擊該列不會再做任何事情。
代碼如下,verbalyl我在做什麼:
- 有關安裝的行爲,我開始聽DataGrid的「加載」事件
- 在Loaded事件,我覺得所有的DataGridColumnHeader後裔數據網格
- 對於每一種,我生成一個單獨的上下文菜單,並將它附加到DataGridColumnHeader
- 對於每個上下文菜單中,我生成每列一個菜單項,並指定一個命令到它的是,在執行時,套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擴展方法主要基於從這裏的一個: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被稱爲LOT - 例如調整列的大小等在需要的時候我只開始收聽第一事件發生(=>列的可見性改變),然後從事件退訂再次(「取(1)」)。如果您發現解決方案存在任何性能問題,則可能需要執行類似的操作;實施取決於你的項目和要求 – Bogey
謝謝你的提示!你是對的,它經常被稱爲!當可視性更改(menuItem.Click())時我正在監聽並在調用它之後註銷事件(之後立即進行)。 –