简体   繁体   English

选择的Datagrid行显示错误“序列不包含匹配元素”

[英]Datagrid row selected showing error “Sequence contains no matching element”

I have a datagrid which holds the User information. 我有一个保存用户信息的数据网格。 Now once, I click on the selected row, I want to display the user information such as their roles and allow the user to edit the user roles by clicking on the combobox. 现在,一次,我单击所选的行,我想显示用户信息,例如他们的角色,并允许用户通过单击组合框来编辑用户角色。 Under my data template I have the combobox in my xaml. 在我的数据模板下,我的xaml中有组合框。 Since using the datatemplate, the combobox name couldnt be found, I am using the following method below to get the children from the grid. 由于使用数据模板,因此无法找到组合框名称,因此我在下面使用以下方法从网格中获取子级。

Here is the code to get the children element: 这是获取children元素的代码:

private List<FrameworkElement> GetChildren(DependencyObject parent)
    {
        List<FrameworkElement> controls = new List<FrameworkElement>();

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); ++i)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            if (child is FrameworkElement)
            {
                controls.Add(child as FrameworkElement);
            }
            controls.AddRange(GetChildren(child));
        }

        return controls;
    }

I created the selection changed event for the datagrid: 我为数据网格创建了选择更改事件:

 private void userDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var userRolesList = new User().getUserRoles();
        ComboBox cbUserRole = (ComboBox)GetChildren(userDataGrid).First(x => x.Name == "cbUserRole");
        cbUserRole.ItemsSource = userRolesList;


    }

Now when I run this code, I am being shown error message 现在,当我运行此代码时,系统将显示错误消息

Sequence contains no matching element 序列不包含匹配元素

The same method I use for my textboxes, I am able to display the values and edit the values too. 与我在文本框中使用的相同方法一样,我也可以显示值并编辑值。 But for my combobox its not working the way it supposed to. 但是对于我的组合框,它不能按预期的方式工作。 Can someone please help me on this. 有人可以帮我吗 Thanks. 谢谢。

This is my xaml code: 这是我的xaml代码:

<DataGrid AutoGenerateColumns="False" Grid.Row="2" Grid.ColumnSpan="4" Grid.RowSpan="3" x:Name="userDataGrid" Margin="70,0.2,70,0" ItemsSource="{Binding}" SelectionChanged="userDataGrid_SelectionChanged">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding UserId}"/>
            <DataGridTextColumn Header="Username" Binding="{Binding UserName}"/>
            <DataGridTextColumn Header="Email" Binding="{Binding UserEmail}"/>
            <DataGridTextColumn Header="User Role" Binding="{Binding UserRole}"/>
            <DataGridTextColumn Header="Created Date" Binding="{Binding UserCreatedDate}"/>
        </DataGrid.Columns>
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <Border BorderThickness="0" Background="BlanchedAlmond" Padding="10">
                    <StackPanel Orientation="Vertical">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock FontSize="12" Text="User ID: " VerticalAlignment="Center" />
                            <TextBlock x:Name="txtBlockId" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserId, Mode=TwoWay}" VerticalAlignment="Center" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock FontSize="12" Text="First Name: " VerticalAlignment="Center" />
                            <TextBox x:Name="txtFirstName" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserFirstName, Mode=TwoWay}" VerticalAlignment="Center" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock FontSize="12" Text="Last Name: " VerticalAlignment="Center" />
                            <TextBox x:Name="txtLastName" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserLastName}" VerticalAlignment="Center" />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock FontSize="12" Text="User Role: " VerticalAlignment="Center" />
                            <ComboBox x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" SelectionChanged="cbUserRole_Click"/>
                        </StackPanel>
                        <StackPanel>
                            <Button x:Name="btnUpdate" Content="Update" VerticalAlignment="Center" HorizontalAlignment="Right" Click="btnUpdate_Click"/>
                        </StackPanel>
                    </StackPanel>
                </Border>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>

Thanks 谢谢

I've been seeing you asking around how to work with this, let me show you one way, hope this helps, but I recommend you read about MVVM patterns and frameworks like MVVMLight for WPF. 我一直在看到您询问如何使用它,让我向您展示一种方法,希望对您有所帮助,但是我建议您阅读有关MVVM模式和框架(例如WPF的MVVMLight)的信息。

Well, for this, first you need to install Install-Package MvvmLight -Version 5.4.1 好吧,为此,首先您需要安装Install-Package MvvmLight -Version 5.4.1

Then you may need to fix one reference issue, in the ViewModelLocator, remove all the usings and replace with: 然后,您可能需要修复一个参考问题,在ViewModelLocator中,删除所有使用方法并替换为:

using GalaSoft.MvvmLight.Ioc;
using CommonServiceLocator;

Now, your MainWindowView.xaml , it should like: 现在,您的MainWindowView.xaml应该像:

<Window x:Class="WpfApp2.MainWindow"
        x:Name="root"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:WpfApp2.ViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <vm:MainViewModel x:Name="Model"/>
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <DataGrid AutoGenerateColumns="False" x:Name="userDataGrid" Margin="70,0.2,70,0" ItemsSource="{Binding Users}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding UserId}"/>
                <DataGridTextColumn Header="Username" Binding="{Binding UserName}"/>
                <DataGridTextColumn Header="Email" Binding="{Binding UserEmail}"/>
                <DataGridTextColumn Header="User Role" Binding="{Binding UserRole}"/>
                <DataGridTextColumn Header="Created Date" Binding="{Binding UserCreatedDate}"/>
            </DataGrid.Columns>
            <DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <Border BorderThickness="0" Background="BlanchedAlmond" Padding="10">
                        <StackPanel Orientation="Vertical">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock FontSize="12" Text="User ID: " VerticalAlignment="Center" />
                                <TextBlock x:Name="txtBlockId" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserId, Mode=TwoWay}" VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock FontSize="12" Text="First Name: " VerticalAlignment="Center" />
                                <TextBox x:Name="txtFirstName" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserFirstName, Mode=TwoWay}" VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock FontSize="12" Text="Last Name: " VerticalAlignment="Center" />
                                <TextBox x:Name="txtLastName" FontSize="16" Foreground="MidnightBlue" Text="{Binding UserLastName}" VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock FontSize="12" Text="User Role: " VerticalAlignment="Center" />
                                <ComboBox ItemsSource="{Binding Path=DataContext.UserRoles, ElementName=root}" SelectionChanged='CbUserRole_OnSelectionChanged' SelectedItem="{Binding UserRole}" x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" />
                            </StackPanel>
                            <StackPanel>
                                <Button x:Name="btnUpdate" Content="Update" VerticalAlignment="Center" HorizontalAlignment="Right" Command="{Binding UpdateCommand, ElementName=Model}" CommandParameter="{Binding}" />
                            </StackPanel>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </DataGrid.RowDetailsTemplate>
        </DataGrid>
    </Grid>
</Window>

Then in your code-behind , there's a little event handling that needs to be done, when changing the roles, 然后,在您code-behind ,在更改角色时,需要完成一些事件处理,

using System.Windows;
using System.Windows.Controls;
using WpfApp2.ViewModel;

namespace WpfApp2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public MainViewModel ViewModel => (MainViewModel) DataContext;

        private void CbUserRole_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ComboBox cb = (ComboBox)sender;

            if (cb != null)
            {
                ViewModel.SelectedUserRole = (UserRole)cb.SelectedItem;
            }
        }
    }
}

Then you should create a ViewModel like so ( ViewModel -> MainViewModel.cs ): 然后,您应该像这样创建一个ViewModel( ViewModel -> MainViewModel.cs ):

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using GalaSoft.MvvmLight.Command;
using WpfApp2.Data;

namespace WpfApp2.ViewModel
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            PopulateUserTestData();
            UpdateCommand = new RelayCommand<User>(UpdateUser);
        }

        private ObservableCollection<User> _users;

        public ObservableCollection<User> Users
        {
            get => _users;
            set
            {
                if (_users != value)
                {
                    _users = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private UserRole _userRole;
        public UserRole SelectedUserRole
        {
            get => _userRole;
            set
            {
                if (_userRole != value)
                {
                    _userRole = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public RelayCommand<User> UpdateCommand { get; }

        public IEnumerable<UserRole> UserRoles => Enum.GetValues(typeof(UserRole)).Cast<UserRole>();

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void UpdateUser(User user)
        {
            Users.Single(u => u.UserId == user.UserId).UserRole = SelectedUserRole;

            // Do updates on your context (or in-memory).

            PrintUsersOnDebug();
        }

        #region Test data and diagnostics support

        private void PrintUsersOnDebug()
        {
            foreach (User user in Users)
            {
                Debug.WriteLine("Username: " + user.UserName + " Role: " + user.UserRole);
            }
        }

        private void PopulateUserTestData()
        {
            Users = new ObservableCollection<User>
            {
                new User
                {
                    UserId = 1,
                    UserCreatedDate = DateTime.Now,
                    UserEmail = "johndoe1@email.com",
                    UserFirstName = "John",
                    UserLastName = "Doe",
                    UserName = "johnd",
                    UserRole = UserRole.Administrator
                },
                new User
                {
                    UserId = 2,
                    UserCreatedDate = DateTime.Now,
                    UserEmail = "billgordon@email.com",
                    UserFirstName = "Bill",
                    UserLastName = "Gordon",
                    UserName = "billg",
                    UserRole = UserRole.SuperUser
                }
            };

            PrintUsersOnDebug();
        }

        #endregion
    }
}

Other related classes: 其他相关类别:

Data->User.cs

using System;

namespace WpfApp2.Data
{
    public class User
    {
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string UserEmail { get; set; }
        public UserRole UserRole { get; set; }
        public DateTime UserCreatedDate { get; set; }
        public string UserFirstName { get; set; }
        public string UserLastName { get; set; }

    }
}

UserRole.cs

namespace WpfApp2
{
    public enum UserRole
    {
        Administrator,
        User,
        SuperUser
    }
}

Now since I just designed this test app to view the changing data in this case roles, I designed this to be just viewable on output window. 现在,由于我只是设计了这个测试应用程序来查看在这种情况下角色中不断变化的数据,因此我将其设计为只能在输出窗口上查看。 As you change the roles and click the update button, inspect the output window. 更改角色并单击更新按钮时,请检查输出窗口。

在此处输入图片说明

If you simply want to populate the ComboBox in the RowDetailsTemplate , you could handle its Loaded event: 如果只想在RowDetailsTemplate填充ComboBox ,则可以处理其Loaded事件:

private void cbUserRole_Loaded(object sender, RoutedEventArgs e)
{
    ComboBox cbUserRole = (ComboBox)sender;
    if (cbUserRole.ItemsSource == null)
        cbUserRole.ItemsSource = new User().getUserRoles();
}

XAML: XAML:

<StackPanel Orientation="Horizontal">
    <TextBlock FontSize="12" Text="User Role: " VerticalAlignment="Center" />
    <ComboBox x:Name="cbUserRole"
              Loaded="cbUserRole_Loaded"
              FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch"
              VerticalAlignment="Center" 
              SelectionChanged="cbUserRole_Click"/>
</StackPanel>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM