WPF MVVM 绑定命令到 Datagrid 内的 Datacontext

WPF MVVM binding command to Datacontext inside Datagrid

我正在创建一个包含 2 个 windows 的 WPF 模具应用程序:带有 DataGrid 的 MainWindow 和允许 Add/Edit 模具的 AddEditWindow。

我有一个位于 DataGrid 的 TemplateColumn 中的 EditButton:

<DataGridTemplateColumn Width="Auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                                <Button Width="150" 
                                    Height="40" 
                                    BorderThickness="2" 
                                    BorderBrush="DarkRed"
                                    Background="Red" 
                                    Foreground="White" 
                                    Content="Edit" 
                                    Name="BtnEdit"                                   
                                    CommandParameter="{Binding}"
                                    Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.AddEditWindowCommand}">
                                </Button>
                            </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

添加编辑窗口命令:

public ICommand AddEditWindowCommand { get; }

        private bool CanAddEditWindowCommandExecute(object SelectedRow) => true;

        private void OnAddEditWindowCommandExecuted(object SelectedRow)
        {
            
            AddEditWindow window = new AddEditWindow();
            window.Show();
        }

我想将 DataContext 传递给 AddEditWindowViewModel。在代码隐藏中,我可以这样做:

private void BtnEdit_Click(object sender, RoutedEventArgs e)
        {
            AddEditWindow addEditWindow = new AddEditWindow((sender as Button).DataContext as Molds);
            addEditWindow.Show();
        }

然后像这样检索它的 AddEditWindow:

private Molds _currentMold = new Molds();
        public GamesEdit(Molds selectedMold)
        {
            InitializeComponent();
            if (selectedMold != null)
            {
                _currentMold = selectedMold;
                              
            }

            DataContext = _currentMold;

但在 MVVM 中我不能。那么,有没有办法在不破坏 MVVM 模式的情况下做到这一点?

p.s。由于我是 MVVM 的新手,非常感谢详细的解释。

更新:

MainWindowViewModel:

internal class MainWindowViewModel : ViewModel
    {
      
        #region Variables
       
        #region Textblocks for search

        private Molds newMolds { get; set; } = new Molds();
        public string TxtType
        {
            get => newMolds.Type;
            set => newMolds.Type = value;
        }

        public string TxtName
        {
            get => newMolds.Name;
            set => newMolds.Name = value;
        }

        public string TxtKus
        {
            get => newMolds.Kus;
            set => newMolds.Kus = value;
        }

        #endregion

        #region AllMolds

        private ObservableCollection<Molds> allMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
        public ObservableCollection<Molds> AllMolds
        {
            get => allMolds; 
            set => allMolds = value;           
        }

        #endregion

        #region FilteredMolds

        private ObservableCollection<Molds> filteredMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
        public ObservableCollection<Molds> FilteredMolds
        {
            get
            {               
                filteredMolds = AllMolds;              
                var currentfilteredmolds = new List<Molds>(filteredMolds);
                if (TxtName != null)
                    currentfilteredmolds = currentfilteredmolds.Where(p => p.Name.ToLower().Contains(TxtName.ToLower())).ToList();
                if (TxtType != null)
                    currentfilteredmolds = currentfilteredmolds.Where(p => p.Type.ToLower().Contains(TxtType.ToLower())).ToList();
                if (TxtKus != null)
                    currentfilteredmolds = currentfilteredmolds.Where(p => p.Kus.ToLower().Contains(TxtKus.ToLower())).ToList();
                return new ObservableCollection<Molds>(currentfilteredmolds);
            }
            
            set => filteredMolds = value;
        }

        #endregion

        
        #endregion

        #region Commands

        #region CloseApplicationCommand
        public ICommand CloseApplicationCommand { get; }

        private bool CanCloseApplicationCommandExecute(object p) => true;

        private void OnCloseApplicationCommandExecuted(object p)
        {
            Application.Current.Shutdown();
        }
        #endregion

        #region SearchCommand

        public ICommand SearchCommand { get; }

        private bool CanSearchCommandExecute(object p) => true;

        private void OnSearchCommandExecuted(object p)
        {            
            OnPropertyChanged("FilteredMolds");
        }

        #endregion

        #region Open AddEditWindowCommand
        public ICommand AddEditWindowCommand { get; }

        private bool CanAddEditWindowCommandExecute(object SelectedRow) => true;

        private void OnAddEditWindowCommandExecuted(object SelectedRow)
        {
            
            AddEditWindow window = new AddEditWindow();
            window.Show();
        }
        #endregion

        #region DeleteMoldCommand

        public ICommand DeleteMoldCommand { get; }

        private bool CanDeleteMoldCommandExecute(object SelectedItems)
        {
            if (SelectedItems != null) return true; else return false;           
        }
       
        private void OnDeleteMoldCommandExecuted(object SelectedItems)
        {
            System.Collections.IList items = (System.Collections.IList)SelectedItems;
            var moldsforRemoving = items?.Cast<Molds>().ToList();

            if (MessageBox.Show($"You want to remove the following {moldsforRemoving.Count()} molds?", "Attention",
                MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
            {
                try
                {
                    ApplicationContext.GetContext().Molds.RemoveRange(moldsforRemoving);
                    ApplicationContext.GetContext().SaveChanges();
                    MessageBox.Show("Data deleted successfully.", "Data deletion",
         MessageBoxButton.OK, MessageBoxImage.Information);
                    AllMolds = new ObservableCollection<Molds>(ApplicationContext.GetContext().Molds.ToList());
                    OnPropertyChanged("FilteredMolds");
                }

                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message.ToString(), "Error",
         MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        #endregion

        #region DragMoveCommand

        public ICommand DragMoveCommand { get; }

        private bool CanDragMoveCommandExecute(object p) => true;

        private void OnDragMoveCommandExecuted(object p)
        {
            Application.Current.MainWindow.DragMove();
        }

        #endregion

        #endregion

        public MainWindowViewModel()
        {
            #region Command Samples

            CloseApplicationCommand = new LamdaCommand(OnCloseApplicationCommandExecuted, CanCloseApplicationCommandExecute);
            SearchCommand = new LamdaCommand(OnSearchCommandExecuted, CanSearchCommandExecute);
            AddEditWindowCommand = new LamdaCommand(OnAddEditWindowCommandExecuted, CanAddEditWindowCommandExecute);
            DeleteMoldCommand = new LamdaCommand(OnDeleteMoldCommandExecuted, CanDeleteMoldCommandExecute);
            DragMoveCommand = new LamdaCommand(OnDragMoveCommandExecuted, CanDragMoveCommandExecute);


            #endregion

            #region Variable Samples for searching

            TxtName = null;
            TxtKus = null;
            TxtType = null;

            #endregion
          
        }
    }

添加编辑窗口视图模型

internal class AddEditWindowViewModel : ViewModel
    {
        #region Variables     


        private Molds _currentMold = new Molds();
    

        #endregion

        #region Commands

        #region CloseWindowCommand
        public ICommand CloseWindowCommand { get; }

        private bool CanCloseWindowCommandExecute(object p) => true;

        private void OnCloseWindowCommandExecuted(object p)
        {
            Application.Current.Windows[1].Close();           
        }
        #endregion

        #region DragMoveAddEditWindowCommand

        public ICommand DragMoveAddEditWindowCommand { get; }

        private bool CanDragMoveAddEditWindowCommandExecute(object p) => true;

        private void OnDragMoveAddEditWindowCommandExecuted(object p)
        {
            Application.Current.Windows[1].DragMove();
        }

        #endregion

        #endregion

        public AddEditWindowViewModel()
        {
            
            #region Command samples

            CloseWindowCommand = new LamdaCommand(OnCloseWindowCommandExecuted, CanCloseWindowCommandExecute);
            DragMoveAddEditWindowCommand = new LamdaCommand(OnDragMoveAddEditWindowCommandExecuted, CanDragMoveAddEditWindowCommandExecute);

            #endregion

        }
    }

我使用 :

将它们连接到 window
<Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>

AddEditWindowViewModel 也是如此。

DataGrid 绑定:

<DataGrid x:Name="DGridMolds" 
                  AutoGenerateColumns="False" 
                  IsReadOnly="True" 
                  Foreground="White" 
                  BorderBrush="White" 
                  Background="#2b2a38"
                  Grid.Column="1"
                  Grid.Row="1"
                  ItemsSource="{Binding Path=FilteredMolds}"
                  >

AddEditWindow.Xaml:

<Window.DataContext>
        <vm:AddEditWindowViewModel/>
    </Window.DataContext>

    <Border Background="#2f2e3c" 
            CornerRadius="10">

        <Border.InputBindings>
            <MouseBinding Command="{Binding DragMoveAddEditWindowCommand}" MouseAction="LeftClick"/>
        </Border.InputBindings>

        <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30"/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition Width="30"/>
        </Grid.ColumnDefinitions>

            <StackPanel Grid.Column="1" Grid.Row="1" Orientation="Vertical">
                <TextBlock Text="Add" FontSize="22" Foreground="White" HorizontalAlignment="Center" Margin="0,30,0,0"/>
                <TextBox Foreground="White" Text="{Binding Type, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5, 35, 5, 5" materialDesign:HintAssist.Hint="Type"/>
                <TextBox Foreground="White" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5" materialDesign:HintAssist.Hint="Name"/>
                <TextBox Foreground="White" Text="{Binding Kus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5" materialDesign:HintAssist.Hint="Kus"/>

In a Code-Behind, I could done something like this:

仔细看看你的 XAML。
您在 CommanParameter 属性.
中设置了绑定 绑定实例为空 - 这意味着绑定发生在指定它的元素的 DataContext 中。
因此,在命令参数中,您将获得此数据上下文。

        private void OnAddEditWindowCommandExecuted(object SelectedRow)
        {
            AddEditWindow window = new AddEditWindow()
                    {DataContext = SelectedRow};
            window.Show();
        }

So, is there a way to do it without breaking MVVM pattern?

您已经破坏了 MVVM。
ViewModel 不允许使用 UI 个元素。
ViewModel 甚至不需要知道使用它的视图类型:WPF、Forms 或 Console。
然后你创建 Window!
对于 MVVM 概念,这是不可接受的

关于在主要问题中显示更详细代码的补充:

我看不懂你的代码逻辑。 所以,我会写在我理解你的意图的措施中。

AddEditWindowViewModel class - 专为编辑 and/or 添加项目的逻辑而设计。 但是他必须得到这个元素并在他的 属性 中提供它,这样他才能创建一个 GUI 进行编辑。

应该是这样的:

namespace MoldsApp
{
    public class AddEditWindowViewModel : ViewModel
    {
        public Molds CurrentMold { get; }

        // Constructor called to edit an entity
        public AddEditWindowViewModel(Molds currentMold)
        {
            CurrentMold = currentMold;
        }

        //Constructor called to create and edit an entity
        public AddEditWindowViewModel()
            : this(new Molds())
        {
        }
    }
}

此外,您的 DataContext 设置不正确。
通过在 XAML 中创建它,您无法为该 ViewModel 实例设置数据。
因此,在 XAML 中,您可以实例化仅在设计时使用的 ViewModel。
对于设计时 DataContext,使用 d: 前缀。

    <d:Window.DataContext>
        <vm:MainWindowViewModel/>
    </d:Window.DataContext>

经过这些更改,项目编辑命令应如下所示:

    private void OnAddEditWindowCommandExecuted(object SelectedRow)
    {
        AddEditWindow window = new AddEditWindow()
        {
             DataContext =  new AddEditWindowViewModel((Molds)SelectedRow)
        };
        window.Show();
    }