1

我在 WPF MVVM 应用程序中有一个 Xceed 数据网格,它设置为保存主从记录。选择子行时,我希望 ViewModel 检测到选定的子行。我想最好使用零代码隐藏来做到这一点。我编写了在单击上下文菜单时对所选项目执行操作的代码。这在选择父项时可以正常工作,但在选择子项时始终返回 null。

我已经将我想要实现的目标放在了一个非常简化的版本中:

我的 XAML 是:

    <Window x:Class="MasterDetailSelection.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:xcdg="clr-namespace:Xceed.Wpf.DataGrid;assembly=Xceed.Wpf.DataGrid.v4.2"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <xcdg:DataGridCollectionViewSource x:Key="cvs_parents" Source="{Binding Path=Parents}">
                <xcdg:DataGridCollectionViewSource.DetailDescriptions>
                    <xcdg:PropertyDetailDescription RelationName="Children"
                                                    AutoCreateDetailDescriptions="False">
                    </xcdg:PropertyDetailDescription>
                </xcdg:DataGridCollectionViewSource.DetailDescriptions>
            </xcdg:DataGridCollectionViewSource>
        </Grid.Resources>
        <xcdg:DataGridControl x:Name="ParentGrid"
                              NavigationBehavior="RowOnly"
                              ItemsSource="{Binding Source={StaticResource cvs_parents}}"
                              SelectedItem="{Binding SelectedItem}"
           AutoCreateDetailConfigurations="True"
                              ReadOnly="True">
            <xcdg:DataGridControl.ContextMenu>
                <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
                    <MenuItem Header="Execute Command" 
                              CommandParameter="{Binding DataContext.SelectedItem}"
                              Command="{Binding DataContext.SampleCommand}" />
                </ContextMenu>
            </xcdg:DataGridControl.ContextMenu>
        </xcdg:DataGridControl>

    </Grid>
</Window>

视图模型是:

    namespace MasterDetailSelection
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using Microsoft.Practices.Composite.Presentation.Commands;

    public class ViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Parent> _parents;
        public event PropertyChangedEventHandler PropertyChanged;
        private DelegateCommand<Object> _sampleCommand;
        private object _selectedItem;

        public ObservableCollection<Parent> Parents
        {
            get { return _parents; }

            set
            {
                _parents = value;
                OnPropertyChanged("Parents");
            }
        }

        public DelegateCommand<Object> SampleCommand
        {
            get
            {
                if (_sampleCommand == null)
                {
                    _sampleCommand = new DelegateCommand<object>(ExecuteSampleCommand, CanExecuteSampleCommand);
                    OnPropertyChanged("SampleCommand");
                }
                return _sampleCommand;
            }
        }

        public bool CanExecuteSampleCommand(Object commandParameter)
        {
            return true;
        }

        public void ExecuteSampleCommand(Object commandParameter)
        {
            Console.WriteLine("ExecuteSampleCommand");
        }

        public object SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (_selectedItem != value)
                {
                    _selectedItem = value;
                    OnPropertyChanged("SelectedItem");
                }
            }
        }

        public void LoadParents()
        {
            var parents = new ObservableCollection<Parent>()
                {
                    new Parent()
                        {
                            Id=1, 
                            Description = "Parent1",
                            Children  = new List<Child>(){new Child()    {Id = 1, Description = "Child1"}    }
                        }
                };

            Parents = parents;
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

有两个简单的实体:

public class Parent
{
    public int Id { get; set;}
    public string Description { get; set;}
    public IEnumerable<Child> Children { get; set;}
}
public class Child
{
    public int Id { get; set; }
    public string Description { get; set; }
}

App.xaml.cs 中的 OnStartup 覆盖包含以下内容:

var viewModel = new ViewModel();
var window = new MainWindow();
window.DataContext = viewModel;
viewModel.LoadParents();
window.Show();

每当我选择父行时,都会使用填充的对象调用 SelectedItem 设置器。当我选择一个子行时,会调用相同的 setter,但具有空值。

当在子行上单击上下文菜单时,有什么方法可以获取对所选项目的引用 - 并且在没有代码隐藏的情况下执行此操作。如果没有,是否可以使用代码隐藏?

4

1 回答 1

0

也许您应该直接在单元格或行上设置上下文菜单,如下所示。然后您可以将适当的模型发送到命令。在下面的示例中,我使用了一个静态定位器类,它也让我的 VM 绑定了实际命令。

<Style TargetType="{x:Type xcdg:DataCell}">
    <Setter Property="ContextMenu" Value="{StaticResource CellContextMenu}">
        <Setter.Value>
            <ContextMenu>
                <MenuItem Header="Execute Command" 
                          CommandParameter="{Binding}"
                          Command="{Binding Source={StaticResource Locator} Path=ViewModel.SampleCommand}" />
            </ContextMenu>
        </Setter.Value>
    </Setter>
</Style>
于 2013-11-11T12:24:39.450 回答