【问题标题】:Datagrid row selected showing error "Sequence contains no matching element"选择的数据网格行显示错误“序列不包含匹配元素”
【发布时间】:2019-02-10 02:33:21
【问题描述】:

我有一个保存用户信息的数据网格。现在,我单击选定的行,我想显示用户信息,例如他们的角色,并允许用户通过单击组合框来编辑用户角色。在我的数据模板下,我的 xaml 中有组合框。由于使用数据模板,无法找到组合框名称,我使用下面的方法从网格中获取子项。

下面是获取子元素的代码:

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;
    }

我为数据网格创建了选择更改事件:

 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;


    }

现在,当我运行此代码时,会显示错误消息

序列不包含匹配元素

与我用于文本框的方法相同,我也可以显示值并编辑值。但是对于我的组合框来说,它并没有按预期的方式工作。有人可以帮我解决这个问题。谢谢。

这是我的 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>

谢谢

【问题讨论】:

  • GetChildren(userDataGrid) 不返回任何带有Name"cbUserRole" 的元素。使用Immediate Window 查看GetChildren(userDataGrid) 的结果并查看它正在返回什么。
  • 如果你使用MVVM模式会更容易,你是否受限于你目前的开发策略?
  • @mjwills 它的投掷Exception thrown: 'System.NullReferenceException'。但是如果在选择更改方法中我添加了一个带有虚拟消息的消息框,那么应用程序能够找到组合框...
  • 如果我在其中添加消息框,它可以工作.. 这很奇怪,但是当我从组合框中选择项目时,它没有选择项目

标签: c# wpf


【解决方案1】:

我一直看到您询问如何使用它,让我向您展示一种方法,希望这会有所帮助,但我建议您阅读 MVVM 模式和框架,例如用于 WPF 的 MVVMLight。

好吧,为此,首先你需要安装Install-Package MvvmLight -Version 5.4.1

那么您可能需要修复一个引用问题,在 ViewModelLocator 中,删除所有使用并替换为:

using GalaSoft.MvvmLight.Ioc;
using CommonServiceLocator;

现在,你的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>

然后在您的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;
            }
        }
    }
}

那么你应该像这样创建一个 ViewModel (ViewModel -&gt; 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
    }
}

其他相关类:

Data-&gt;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
    }
}

现在,由于我刚刚设计了这个测试应用程序来查看本案例角色中不断变化的数据,因此我将其设计为只能在输出窗口中查看。当您更改角色并单击更新按钮时,检查输出窗口。

【讨论】:

  • 我正在尝试使用 Mvvm 灯,但在我的 xaml 文件中,它显示的 MainViewModel 在命名空间中不存在...
  • 您必须确保命名空间是正确的,在示例中 MainViewModel 的命名空间是 WpfApp2.ViewModel。文件结构是 MainViewModel.cs 在名为 ViewModel 的文件夹中
  • 是的,命名空间是正确的。我在一个名为 WpfApp2 的新项目中完全遵循您的代码
  • Mac 我能够解决命名空间问题。我只是好奇,如果没有我有很多视图和模型如何使用 mvvmlight 来实现它?
  • 目前,一旦我使用 Nuget 包安装 MvvmLight,它会自动创建 ViewModel 文件夹,其中有两个类文件 MainViewModel.cs 和 ViewModelLocator.cs。根据您上面的解释“那么您应该像这样创建一个 ViewModel (ViewModel -> MainViewModel.cs):”.. 我应该创建一个新的文件夹视图模型和新的 Mainviewmodel cs 文件还是使用相同的现有文件?
【解决方案2】:

如果您只是想在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:

<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>

【讨论】:

  • 这真的帮助我解决了我的问题!再次感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-23
  • 2013-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多