Skip to content

Контекстные меню

Контекстные меню

Data Grid поддерживает встроенные контекстные меню для определенных элементов. В этом разделе показано, как заменить и изменить эти меню, а также отобразить пользовательские всплывающие меню для элементов Data Grid, которые не имеют встроенных контекстных меню.

Встроенное контекстное меню заголовка столбца

При щелчке правой кнопкой мыши по заголовку столбца отображается встроенное меню заголовка столбца. Меню содержит команды для сортировки и группировки данных, отображения панели поиска и средства выбора столбца.

datagrid-columnheadermenu1

Свойство DataGridControl.ColumnMenu позволяет вам получить доступ к этому меню и настроить его.

Чтобы заменить меню по умолчанию, назначьте объекту Eremex.AvaloniaUI.Controls.Bars.PopupMenu свойство DataGridControl.ColumnMenu.

Чтобы настроить существующее меню заголовка столбца (добавить новые элементы или удалить элементы по умолчанию), откройте меню после его инициализации (например, в обработчике событий Initialized вашего DataGrid), а затем измените меню.

Контекст данных

Свойство DataContext заголовочного меню столбца и его элементов (объектов ToolbarButtonItem) определяет объект GridColumn, для которого было вызвано меню.

Пример - Как заменить дефолтное меню заголовка столбца на "Меню заголовка столбца по умолчанию"

Следующий код создает пользовательское меню заголовка столбца, содержащее элемент меню Copy Column. Код привязывает элемент меню к команде CopyColumnDataCommand, определенной в ViewModel.

datagrid-contextmenus-columnmenu-replace-example

Обратите внимание на инициализацию свойств Command и CommandParameter для элемента меню в приведенном ниже коде. Выражение CommandParameter="{Binding FieldName}" определяет привязку к свойству FieldName элемента меню DataContext (GridColumn.FieldName).

DataContext в GridColumn соответствует DataContext в Data Grid (объект ViewModel в этом примере). Это позволяет вам получить доступ к модели представления и ее команде CopyColumnCommand, используя выражение: Command="{Binding DataContext.CopyColumnCommand}".

xmlns:mxdg="https://schemas.eremexcontrols.net/avalonia/datagrid"
xmlns:mxe="https://schemas.eremexcontrols.net/avalonia/editors"
xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxdg:DataGridControl Name="dataGrid1">
    <mxdg:DataGridControl.ColumnMenu>
        <mxb:PopupMenu>
            <mxb:ToolbarButtonItem
                Header="Copy Column"
                Command="{Binding DataContext.CopyColumnCommand}"
                CommandParameter="{Binding FieldName}">
            </mxb:ToolbarButtonItem>
        </mxb:PopupMenu>
    </mxdg:DataGridControl.ColumnMenu>
</mxdg:DataGridControl>
public MainView()
{
    DataContext = new ViewModel();
}

public partial class ViewModel : ObservableObject
{
    [RelayCommand]
    void CopyColumn(string fieldName)
    {
        //...
    }
}

Пример - Как изменить существующее меню заголовка столбца

Следующий пример добавляет пользовательскую команду в дефолтное меню заголовка столбца.

В примере обрабатывается событие DataGridControl.Initialized для доступа к дефолтному меню заголовка столбца после его инициализации и добавляется команда Refresh Data в меню.

datagrid-contextmenus-columnmenu-customize-example

xmlns:mxdg="https://schemas.eremexcontrols.net/avalonia/datagrid"
xmlns:mxe="https://schemas.eremexcontrols.net/avalonia/editors"
xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxdg:DataGridControl Name="dataGrid1" Initialized="OnInitialized">
    ...
</mxdg:DataGridControl>
using Eremex.AvaloniaUI.Controls.Bars;

private void OnInitialized(object sender, System.EventArgs e)
{
    ToolbarButtonItem btn1 = new ToolbarButtonItem();
    btn1.Header = "Refresh Data";
    btn1.ShowSeparator = true;
    btn1.Command = new RelayCommand<DataGridControl>(UpdateDataGrid);
    btn1.CommandParameter = dataGrid1;
    dataGrid1.ColumnMenu.Items.Add(btn1);
}

[RelayCommand]
void UpdateDataGrid(DataGridControl dataGrid)
{
    //...
}

Меню ячейки строки

Data Grid поддерживает встроенное контекстное меню для ячеек строк (смотрите свойство DataGridControlBase.RowCellMenu). Это меню изначально пусто и, следовательно, скрыто. Чтобы отобразить меню ячейки строки, заполните его элементами в XAML или в коде.

Контекст данных

DataContext меню ячейки строки и его элементов содержит объект Eremex.AvaloniaUI.Controls.DataControl.Visuals.CellData, который позволяет вам получать доступ к информации, зависящей от контекста:

  • CellData.DataControl — Возвращает контрол-контейнер (DataGridControl), для которого вызывается меню.
  • CellData.Row — Возвращает нижележащий объект данных щелкнутой строки.

Пример - Как отобразить одинаковые команды контекстного меню для всех строк

В следующем примере команда "Copy Row" добавляется в меню ячейки строки (DataGridControlBase.RowCellMenu) для всех строк.

datagrid-contextmenus-rowmenu-copyrow-example

Приведенный ниже код XAML присваивает свойству DataGridControl.RowCellMenu всплывающее меню. Всплывающее меню содержит один элемент (ToolbarButtonItem), привязанный к команде CopyRowCommand, определенной в модели представления (DataContext Data Grid).

xmlns:mxdg="https://schemas.eremexcontrols.net/avalonia/datagrid"
xmlns:mxe="https://schemas.eremexcontrols.net/avalonia/editors"
xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxdg:DataGridControl Name="dataGrid1">
    <mxdg:DataGridControl.RowCellMenu>
        <mxb:PopupMenu>
            <mxb:PopupMenu.Items>
                <mxb:ToolbarButtonItem Header="Copy Row"
                 Command="{Binding DataControl.DataContext.CopyRowCommand}"
                 CommandParameter="{Binding Row}"/>
            </mxb:PopupMenu.Items>
        </mxb:PopupMenu>
    </mxdg:DataGridControl.RowCellMenu>
</mxdg:DataGridControl>
public partial class MainView : UserControl
{
    ViewModel viewModel = new ViewModel();
    public MainView()
    {
        DataContext = viewModel;
        InitializeComponent();
    }
}

public partial class ViewModel : ObservableObject
{
    public ViewModel() { }
    [RelayCommand]
    void CopyRow(object row)
    {
        //...
    }
}

Пример - Как отобразить разные команды контекстного меню для разных строк

Контрол Data Grid в этом примере отображает список объектов Employee. В примере инициализируется контекстное меню для строк (DataGridControlBase.RowCellMenu) и отображаются разные элементы меню для разных строк данных.

datagrid-contextmenus-rowcellmenu-different-example

В контекстном меню отображается элемент меню Show Working Hours для строк, которые относятся к сотрудникам, работающим неполный рабочий день (свойство Employee.IsPartTime возвращает значение true).

В контекстном меню отображается элемент меню Open Contract Details для строк, которые ссылаются на подрядчиков (свойство Employee.IsContract возвращает значение true).

Приведенный ниже код XAML добавляет два элемента ("Show Working Hours" и "Open Contract Details") в контекстное меню. Видимостью этих элементов динамически управляют свойства бизнес-объекта IsPartTime и IsContract.

xmlns:mxdg="https://schemas.eremexcontrols.net/avalonia/datagrid"
xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"

<mxdg:DataGridControl Name="dataGrid1" AutoGenerateColumns="True" ItemsSource="{Binding Employees}">
    <mxdg:DataGridControl.RowCellMenu>
        <mxb:PopupMenu>
            <mxb:PopupMenu.Items>
                <mxb:ToolbarButtonItem
                    Header="Show Working Hours"
                    Command="{Binding DataControl.DataContext.ShowWorkingHoursCommand}"
                    CommandParameter="{Binding Row}"
                    Glyph="{SvgImage 'avares://AvaloniaApplication3/Assets/time.svg'}"
                    GlyphSize="20,20"
                    IsVisible="{Binding Row.IsPartTime}">
                </mxb:ToolbarButtonItem>
                <mxb:ToolbarButtonItem
                    Header="Open Contract Details"
                    Command="{Binding DataControl.DataContext.OpenContractDetailsCommand}"
                    CommandParameter="{Binding Row}"
                    Glyph="{SvgImage 'avares://AvaloniaApplication3/Assets/details.svg'}"
                    GlyphSize="20,20"
                    IsVisible="{Binding Row.IsContract}"
                    >
                </mxb:ToolbarButtonItem>
            </mxb:PopupMenu.Items>
        </mxb:PopupMenu>
    </mxdg:DataGridControl.RowCellMenu>
</mxdg:DataGridControl>
public partial class MainView : UserControl
{
    MainViewModel viewModel = new MainViewModel();

    public MainView()
    {
        DataContext = viewModel;

        viewModel.Employees.Add(new Employee() { 
            Name = "Herbert McGill", EmploymentType=EmploymentType.FullTime, 
            Position= "Customer Service" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Cora Gilmore", EmploymentType = EmploymentType.PartTime, 
            Position = "Accountant" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Earl Case", EmploymentType = EmploymentType.PartTime, 
            Position = "Software Engineer" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Julius Howell", EmploymentType = EmploymentType.Contract, 
            Position = "Financial Analyst" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Trevor Cameron", EmploymentType = EmploymentType.Contract, 
            Position = "Accountant" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Lon Monroe", EmploymentType = EmploymentType.FullTime, 
            Position = "Management" 
        });

        InitializeComponent();
    }
}

public partial class MainViewModel : ViewModelBase
{
    public MainViewModel() { }

    public ObservableCollection<Employee> Employees { get; } = new();

    [RelayCommand]
    void OpenContractDetails(Employee emp)
    {
        if(emp.EmploymentType == EmploymentType.Contract)
        {
            //...
        }
    }

    [RelayCommand]
    void ShowWorkingHours(Employee emp)
    {
        if(emp.EmploymentType == EmploymentType.PartTime)
        {
            //...
        }
    }
}

public partial class Employee : ObservableObject
{
    [ObservableProperty]
    public string name = "";

    [ObservableProperty]
    public string position = "";

    [ObservableProperty]
    public EmploymentType employmentType;

    [Browsable(false)]
    public bool IsContract => EmploymentType == EmploymentType.Contract;

    [Browsable(false)]
    public bool IsPartTime => EmploymentType == EmploymentType.PartTime;
}

public enum EmploymentType
{
    [Display(Name = "Full Time")]
    FullTime,
    [Display(Name = "Part Time")]
    PartTime,
    Contract
}

Пример - Как генерировать команды контекстного меню из ViewModel

В этом примере показано, как заполнить меню ячейки строки контрола DataGrid элементами, определенными в ViewModel.

datagrid-contextmenus-rowcellmenu-fromViewModel-example

Меню ячейки строки (DataGridControl.RowCellMenu) заполняется элементами (объектами ToolbarButtonItem) из источника объектов, указанного в коллекции PopupMenu.ItemsSource. В этом примере свойство PopupMenu.ItemsSource привязано к коллекции MenuItems, определенной в модели основного представления, используя следующее выражение привязки:

<mxb:PopupMenu ItemsSource="{Binding DataControl.DataContext.MenuItems}">

Когда для ячейки DataGrid отображается PopupMenu, DataContext меню содержит объект Eremex.AvaloniaUI.Controls.DataControl.Visuals.CellData. Объект CellData предоставляет свойство DataControl, которое позволяет вам получить доступ к контролу, для которого отображается меню. Модель основного вида назначается DataContext контрола. Таким образом, синтаксис DataControl.DataContext.MenuItems ссылается на коллекцию MenuItems, определенную в модели main View.

Объект CellData также содержит другие свойства, которые позволяют вам получать доступ к информации, связанной с ячейкой (объекту столбца, строки и т.д.).

Элементы меню инициализируются с использованием стилей. DataContext из элементов меню являются элементами коллекции PopupMenu.ItemsSource. В этом примере свойство PopupMenu.ItemsSource хранит коллекцию объектов MenuItemViewModel. Следующий фрагмент связывает свойства ToolbarButtonItem.Header и ToolbarButtonItem.Command со свойствами MenuItemViewModel.Header и MenuItemViewModel.Command соответственно.

<mxb:PopupMenu.Styles>
    <Style Selector="mxb|ToolbarButtonItem">
        <Setter Property="Header" Value="{Binding Header}"/>
        <Setter Property="Command" Value="{Binding Command}"  />
    </Style>
</mxb:PopupMenu.Styles>

Команде ToolbarButtonItem требуется информация о строке, по которой был произведен щелчок правой кнопкой мыши. Чтобы передать строку данных команде, XAML-код устанавливает свойство ToolbarButtonItem.CommandParameter следующим образом:

xmlns:mxvis="clr-namespace:Eremex.AvaloniaUI.Controls.DataControl.Visuals;
 assembly=Eremex.Avalonia.Controls"

<mxb:PopupMenu.Styles>
    <Style Selector="mxb|ToolbarButtonItem">
        ...
        <Setter Property="CommandParameter" 
         Value="{Binding $parent[mxvis:CellControl].DataContext.Row}"  />
    </Style>
</mxb:PopupMenu.Styles>

Здесь выражение $parent[mxvis:CellControl] обходит логическое дерево, чтобы найти объект CellControl (он является родительским для DataContext ToolbarButtonItem). Объект CellControl.DataContext содержит объект Eremex.AvaloniaUI.Controls.DataControl.Visuals.CellData, который позволяет вам получить доступ к строке данных из свойства CellData.Row.

Полный код приведен ниже.

xmlns:mxdg="https://schemas.eremexcontrols.net/avalonia/datagrid"
xmlns:mxb="https://schemas.eremexcontrols.net/avalonia/bars"
xmlns:mxvis="clr-namespace:Eremex.AvaloniaUI.Controls.DataControl.Visuals;
 assembly=Eremex.Avalonia.Controls"

<mxdg:DataGridControl Name="dataGrid1" AutoGenerateColumns="True" ItemsSource="{Binding Employees}">
    <mxdg:DataGridControl.RowCellMenu>
        <mxb:PopupMenu ItemsSource="{Binding DataControl.DataContext.MenuItems}">
            <mxb:PopupMenu.Styles>
                <Style Selector="mxb|ToolbarButtonItem">
                    <Setter Property="Header" Value="{Binding Header}"/>
                    <Setter Property="Command" Value="{Binding Command }"  />
                    <Setter Property="CommandParameter" 
                     Value="{Binding $parent[mxvis:CellControl].DataContext.Row}"  />
                </Style>
            </mxb:PopupMenu.Styles>
        </mxb:PopupMenu>
    </mxdg:DataGridControl.RowCellMenu>
</mxdg:DataGridControl>
public partial class MainView : UserControl
{
    MainViewModel viewModel = new MainViewModel();

    public MainView()
    {
        DataContext = viewModel;

        viewModel.Employees.Add(new Employee() { 
            Name = "Herbert McGill", EmploymentType=EmploymentType.FullTime, 
            Position= "Customer Service" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Cora Gilmore", EmploymentType = EmploymentType.PartTime, 
            Position = "Accountant" 
        });
        viewModel.Employees.Add(new Employee() { 
            Name = "Earl Case", EmploymentType = EmploymentType.PartTime, 
            Position = "Software Engineer" 
        });

        InitializeComponent();
    }
}

public partial class MainViewModel : ViewModelBase
{
    public ObservableCollection<Employee> Employees { get; } = new();

    public MainViewModel()
    {
        MenuItemViewModel menuItem1 = new MenuItemViewModel();
        menuItem1.Header = "Copy Row";
        menuItem1.Command = new RelayCommand<object>(CopyRow);
        MenuItems.Add(menuItem1);

        MenuItemViewModel menuItem2 = new MenuItemViewModel();
        menuItem2.Header = "Delete Row";
        menuItem2.Command = new RelayCommand<object>(DeleteRow);
        MenuItems.Add(menuItem2);

    }

    public ObservableCollection<MenuItemViewModel> MenuItems { get; } = new();

    [RelayCommand]
    void CopyRow(object row)
    {
        //...
    }
    [RelayCommand]
    void DeleteRow(object row)
    {
        //...
    }
}

public partial class MenuItemViewModel : ObservableObject
{
    [ObservableProperty]
    string? header;

    [ObservableProperty]
    ICommand? command;
}

public partial class Employee : ObservableObject
{
    [ObservableProperty]
    public string name = "";

    [ObservableProperty]
    public string position = "";

    [ObservableProperty]
    public EmploymentType employmentType;

    [Browsable(false)]
    public bool IsContract => EmploymentType == EmploymentType.Contract;

    [Browsable(false)]
    public bool IsPartTime => EmploymentType == EmploymentType.PartTime;
}

public enum EmploymentType
{
    [Display(Name = "Full Time")]
    FullTime,
    [Display(Name = "Part Time")]
    PartTime,
    Contract
}

Настройка меню при отображении

Вы можете обработать событие PopupMenu.Opening для динамической настройки контекстных меню DataGrid. Событие происходит, когда вот-вот будет отображен PopupMenu.

Пример - Как отобразить контекстное меню для первого столбца

В следующем примере свойству DataGridControlBase.RowCellMenu присваивается пустое значение PopupMenu, а затем обрабатывается событие PopupMenu.Opening для заполнения меню элементами, когда пользователь щелкает правой кнопкой мыши ячейки в первом видимом столбце DataGrid. Меню остается пустым (и, следовательно, не отображается), когда пользователь щелкает правой кнопкой мыши в других столбцах.

Созданное меню содержит кнопку-переключатель "Show/Hide Group Panel", которая переключает видимость групповой панели DataGrid (свойство DataGridControlBase.ShowGroupPanel).

datagrid-contextmenus-customizeonshowing

<mxdg:DataGridControl Name="dataGrid1" ...>
    <mxdg:DataGridControl.RowCellMenu>
        <mxb:PopupMenu Opening="RowCellMenuOpening"/>
    </mxdg:DataGridControl.RowCellMenu>
</mxdg:DataGridControl>
using Eremex.AvaloniaUI.Controls.DataControl.Visuals;

void RowCellMenuOpening(object sender, CancelEventArgs e)
{
    if (sender == null) return;
    PopupMenu menu = sender as PopupMenu;
    menu.Items.Clear();

    CellData cellData = menu.DataContext as CellData;
    DataGridControl control = cellData.DataControl as DataGridControl;
    GridColumn column = cellData.Column as GridColumn;
    //// Access the underlying data row, when required.
    //object dataRow = cellData.Row;

    if(column.VisibleIndex == 0)
    {
        ToolbarCheckItem btn1 = new ToolbarCheckItem();
        btn1.IsChecked = control.ShowGroupPanel;
        btn1.Header = (control.ShowGroupPanel) ? "Hide Group Panel" : "Show Group Panel";
        btn1.Command = new RelayCommand<DataGridControl>(ShowGroupPanelCommand);
        btn1.CommandParameter = control;
        menu.Items.Add(btn1);
    }
}

[RelayCommand]
void ShowGroupPanelCommand(DataGridControl dataGrid)
{
    dataGrid.ShowGroupPanel = !dataGrid.ShowGroupPanel;
}

Отображение контекстного меню для контролов, использующих Attached-свойство ToolbarManager в ToolbarManager в ToolbarManager в ToolbarManager в ToolbarManager.

Компонент Eremex.AvaloniaUI.Controls.Bars.ToolbarManager предоставляет attached-свойство ContextPopup, которое позволяет назначать контекстное меню любому контролу, включая DataGrid. Это контекстное меню отображается для регионов DataGrid, в которых нет контекстных меню по умолчанию, и для регионов с пустыми меню по умолчанию.

Пример - Как назначить контекстное меню, используя attached-свойство ToolbarManager.ContextPopup.

Следующий код использует attached-свойство ToolbarManager.ContextPopup для указания контекстного меню для контрола DataGrid. Меню содержит переключающий элемент меню Show Column Header Panel/Hide Column Header Panel, который переключает видимость опции DataGridControl.ShowColumnHeaders.

datagrid-contextmenus-toolbarmanager-example

<mxdg:DataGridControl Name="dataGrid1">
    <mxdg:DataGridControl.Styles>
        <Style Selector="mxdg|DataGridControl">
            <Setter Property="mxb:ToolbarManager.ContextPopup" >
                <Template>
                    <mxb:PopupMenu Tag="dataGrid1">
                        <mxb:ToolbarCheckItem Header="Show Column Header Panel" 
                         IsChecked="{Binding $parent[mxdg:DataGridControl].ShowColumnHeaders, Mode=TwoWay}" 
                         IsVisible="{Binding !$parent[mxdg:DataGridControl].ShowColumnHeaders}"/>
                        <mxb:ToolbarCheckItem Header="Hide Column Header Panel" 
                         IsChecked="{Binding $parent[mxdg:DataGridControl].ShowColumnHeaders, Mode=TwoWay}" 
                         IsVisible="{Binding $parent[mxdg:DataGridControl].ShowColumnHeaders}"/>
                    </mxb:PopupMenu>
                </Template>
            </Setter>
        </Style>
    </mxdg:DataGridControl.Styles>
</mxdg:DataGridControl>



* Эта страница была создана автоматически с помощью сервиса машинного перевода Яндекс Переводчик.