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

时间:2018-09-05 03:50:31

标签: c# wpf

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

以下是获取children元素的代码:

Time datatype

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

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

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

  

序列中没有匹配的元素

与我在文本框中使用的相同方法一样,我也可以显示值并编辑值。但是对于我的组合框,它不能按预期的方式工作。有人可以帮我吗谢谢。

这是我的xaml代码:

 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;


    }

谢谢

2 个答案:

答案 0 :(得分: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 -> MainViewModel.cs)创建一个ViewModel:

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

现在,由于我只是设计了此测试应用程序来查看在这种情况下角色的变化数据,因此我将其设计为只能在输出窗口上查看。更改角色并单击“更新”按钮时,请检查输出窗口。

enter image description here

答案 1 :(得分:0)

如果您只想在ComboBox中填充RowDetailsTemplate,则可以处理其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>