为给定控件类型的任何实例创建ContextMenu

时间:2012-07-31 07:53:59

标签: .net wpf binding contextmenu

我对WPF很新,我对同一类型的所有控件的逻辑“因式分解”有疑问。

更准确地说,对于Infragistrics DataGrid的任何实例,我想要一个带有“导出到Excel”按钮的上下文菜单,而不必在每次使用所述网格时都编写任何代码。 此外,如果默认情况下我将始终具有“导出到Excel”上下文菜单项,我还希望能够根据具体情况透明地添加更多项目。 奖励是能够删除特定实例上的“导出到Excel”按钮。

我的问题是实现这一目标的最佳方法是什么:

  • 对DataGrid进行子类化并在“OnInitialized”上添加ContextMenu programmaticaly?问题:我希望添加额外菜单项的网格怎么样...... XAML会覆盖已存在的内容?此外,我不想拥有我的控件的子类,只需直接使用控件本身并使用WPF添加预期的行为。
  • 使用“行为”?我可以为这种类型的网格添加一个新的附加属性,但不确定这是否是一个好的做法
  • 还有别的吗?

我希望我的问题有道理!

注意:这个问题不是关于导出到Excel,我知道用我的控件执行此操作的方法。

3 个答案:

答案 0 :(得分:0)

创建一个List<MenuItem> DependencyProperty(如果你想从ViewModel绑定。如果你只是想从后面的代码中分配然后在你的数据网格控件中创建简单的CLR属性)并将其绑定/分配给列表额外的menuItems。在event DataGridLoaded事件中,将这些额外的menuItems添加到Common MenuItems.I希望这会有所帮助。

答案 1 :(得分:0)

我最终使用了附加的属性解决方案。它非常干净,不会“污染”现有代码。您只需添加一行XAML(见下文)即可将导出功能添加到任何数据网格的excel功能。

<igDP:XamDataGrid
                    x:Name="summary"
                    Behaviours:XamDataGridBehaviours.ExportFileName="plop.xls"
                    ActiveDataItem="{Binding Path=SelectedSummary}">
[...]
</igDP:XamDataGrid>

附加的属性代码本身如下:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using Infragistics.Documents.Excel;
using Infragistics.Windows.DataPresenter;
using Infragistics.Windows.DataPresenter.ExcelExporter;
using Microsoft.Win32;

namespace MYCOMPANY.Plugin.Framework.Behaviours
{
    public class XamDataGridBehaviours
    {
        public static readonly DependencyProperty ExportFileNameProperty = DependencyProperty.RegisterAttached(
    "ExportFileName",
    typeof(string),
    typeof(XamDataGridBehaviours),
    new FrameworkPropertyMetadata(OnExportCommandChanged));

        private const string ExportToExcelHeader = "Export to Excel";

        [AttachedPropertyBrowsableForType(typeof(XamDataGrid))]
        public static string GetExportFileName(XamDataGrid d)
        {
            return (string)d.GetValue(ExportFileNameProperty);
        }

        public static void SetExportFileName(XamDataGrid d, string value)
        {
            d.SetValue(ExportFileNameProperty, value);
        }

        static void OnExportCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var grid = d as XamDataGrid;
            var fileName = (string) e.NewValue;

            if (grid != null && !string.IsNullOrEmpty(fileName))
            {
                CreateExcelExportMenu(grid, fileName);
            }
            else if (grid != null && grid.ContextMenu != null)
            {
                SafeDeleteMenuItem(grid.ContextMenu);
            }
        }

        private static void CreateExcelExportMenu(XamDataGrid grid, string fileName)
        {
            var contextMenu = grid.ContextMenu ?? new ContextMenu();

            var exportToExcel = GetOrCreateMenuItem(grid, contextMenu, fileName);

            contextMenu.Items.Add(exportToExcel);

            grid.ContextMenu = contextMenu;
        }

        private static void ExportToExcel(XamDataGrid grid, string fileName)
        {
            var saveFileDialog = new SaveFileDialog();
            saveFileDialog.FileName = fileName;
            saveFileDialog.DefaultExt = ".xls";
            saveFileDialog.Filter = "Excel spreadsheets (.xls)|*.xls";

            if (saveFileDialog.ShowDialog() == true)
            {
                var exporter = new DataPresenterExcelExporter();
                exporter.Export(grid, saveFileDialog.FileName, WorkbookFormat.Excel97To2003);
            }
        }

        private static MenuItem GetOrCreateMenuItem(XamDataGrid grid, ContextMenu menu, string fileName)
        {
            foreach (var item in menu.Items)
            {
                if (item is MenuItem)
                {
                    var menuitem = item as MenuItem;
                    if (menuitem.Header.ToString() == ExportToExcelHeader)
                    {
                        menuitem.Command = new RelayCommand(o => ExportToExcel(grid, fileName));
                        return menuitem;
                    }
                }
            }

            var exportToExcel = new MenuItem();
            exportToExcel.Header = ExportToExcelHeader;
            exportToExcel.Command = new RelayCommand(o => ExportToExcel(grid, fileName));

            var icon = new Image();
            var bmImage = new BitmapImage();
            bmImage.BeginInit();
            bmImage.UriSource = new Uri(@"..\..\Images\excel.png", UriKind.RelativeOrAbsolute);
            bmImage.EndInit();
            icon.Source = bmImage;
            icon.MaxWidth = 16;
            exportToExcel.Icon = icon;

            return exportToExcel;
        }

        private static void SafeDeleteMenuItem(ContextMenu menu)
        {
            MenuItem toDelete = null;

            foreach (var item in menu.Items)
            {
                if (item is MenuItem)
                {
                    var menuitem = item as MenuItem;
                    if (menuitem.Header.ToString() == ExportToExcelHeader)
                    {
                        toDelete = menuitem;
                        break;
                    }
                }
            }

            if (toDelete != null)
                menu.Items.Remove(toDelete);
        }
    }
}

正如您所看到的,每当我们为网格设置非空导出文件名时,代码都会向excel项添加导出。如果文件名最终为null,则代码将尝试删除导出项(如果存在)。这样,它不应该阻止xaml设置其他项目并且应该保持透明。

答案 2 :(得分:0)

如果你在XAML中这样做,你可以这样做:

<DataGrid>
  <DataGrid.ContextMenu>
     <!--Binding to your view model and the command is whatever your command name should be ->
     <MenuItem Header="Export to CSV" Command="{Binding Export}"/>
  </DataGrid.ContextMenu>
</DataGrid>

这允许您将新对象添加到上下文菜单中。