【问题标题】:Populate DataGridComboBoxColumn using integer fields and an enum as it's display name使用整数字段和枚举作为显示名称填充 DataGridComboBoxColumn
【发布时间】:2021-02-27 10:31:08
【问题描述】:

我从包含用户数组的数据库中收到一个查询,在用户类中我有一个名为 Role 的字节字段。我希望我的DataGridComboBoxColumn 有 2 个项目。如果Role == 0"Member",如果Role == 1"Moderator"

User.cs

public enum UserRole
{
    Member,
    Moderator
}

public class User
{
    [JsonConstructor]
    public User(int userId, string email, string password, string token, string nickname, byte role, uint coins, int power1, int power2, int power3, int power4, DateTime createTime, DateTime? lastLoginTime)
    {
        this.UserId = userId;
        this.Email = email;
        this.Password = password;
        this.Token = token;
        this.Nickname = nickname;
        this.Role = role;
        this.Coins = coins;
        this.Power1 = power1;
        this.Power2 = power2;
        this.Power3 = power3;
        this.Power4 = power4;
        this.CreateTime = createTime;
        this.LastLoginTime = lastLoginTime;
        this.UserRole = (UserRole)role;
    }

    [JsonPropertyName("userId")]
    public int UserId { get; set; }

    [JsonPropertyName("email")]
    public string Email { get; set; }

    [JsonPropertyName("password")]
    public string Password { get; set; }

    [JsonPropertyName("token")]
    public string Token { get; set; }

    [JsonPropertyName("nickname")]
    public string Nickname { get; set; }

    [JsonPropertyName("role")]
    public byte Role { get; set; }

    [JsonPropertyName("coins")]
    public uint Coins { get; set; }

    [JsonPropertyName("power1")]
    public int Power1 { get; set; }

    [JsonPropertyName("power2")]
    public int Power2 { get; set; }

    [JsonPropertyName("power3")]
    public int Power3 { get; set; }

    [JsonPropertyName("power4")]
    public int Power4 { get; set; }

    [JsonPropertyName("createTime")]
    public DateTime CreateTime { get; set; }

    [JsonPropertyName("lastLoginTime")]
    public DateTime? LastLoginTime { get; set; }

    [JsonIgnore]
    public UserRole UserRole { get; set; }
}

MainWindow.xaml

<materialDesign:DataGridComboBoxColumn
  Header="Role"
  Width="100">
    <materialDesign:DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="ComboBox" BasedOn="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type ComboBox}, ResourceId=MaterialDataGridComboBoxColumnEditingStyle}}" >
            <Setter Property="IsEditable" Value="True" />
            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Role}"/>
            <Setter Property="DisplayMemberPath" Value="UserRole"/>
        </Style>
    </materialDesign:DataGridComboBoxColumn.EditingElementStyle
</materialDesign:DataGridComboBoxColumn>

【问题讨论】:

    标签: c# wpf xaml datagrid material-design-in-xaml


    【解决方案1】:

    您的代码和 XAML 中有多个问题需要解决。

    • 数据网格列不是可视树的一部分,这意味着您的绑定 DataContext.Role 等相对源和元素名称绑定将不起作用。
    • 您的UserRole 类型是enum,但您希望在组合框列中将其常量名称显示为可供选择的项目。 ItemsSource 属性需要一个可枚举类型,而枚举不是。
    • 我假设您将Users 的数组或集合绑定到您的数据网格。这意味着您的列的数据上下文是 User 实例。在这种情况下,您的组合框 ItemsSource 绑定没有意义,因为您正在尝试绑定不包含当前用户的数据网格数据上下文,即使它包含,Role 属性也是 byte ,而不是可枚举的。
    • 您将DisplayMemberPath 设置为UserRole,但它不起作用,因为您想将您的组合框绑定到不包含属性UserRoleUserRole 枚举的常量。
    • 您的User 类型包含byte 类型的Role,以及UserRole 类型的UserRole。目前尚不清楚您要使用两者的哪个属性以及另一个用于什么。
    • 您的枚举类型具有underlying type int, which is the default。如果您在内部使用值作为 byte 无论如何,您可以考虑将其更改为 byte
    • 你只设置了编辑样式,没有设置非编辑样式,进入编辑模式时会弹出值,离开编辑模式时会隐藏。
    • 您设置了DisplayMemberPath,但我认为您真正想做的是将Role 属性绑定为选定项。

    在下文中,我假设您希望使用 byte 类型的 Role 属性进行绑定,并且您希望在组合框列中显示 UserRole 枚举的常量名称。

    首先,您必须创建一个枚举常量。您可以在代码中提供一个可以绑定的集合,但由于它永远不会更改并且当前仅在数据网格中使用,您可以像这样create it entirely in XAML using an ObjectDataProvider

    <ObjectDataProvider x:Key="UserRoles" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
       <ObjectDataProvider.MethodParameters>
          <x:Type TypeName="local:UserRole"/>
       </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    

    接下来,我们需要创建一个转换器,将byte 转换为UserRole 并返回。

    public class UserRoleToEnumConverter : IValueConverter
    {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
          return (UserRole)System.Convert.ToByte(value);
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
          return (byte)(int)value;
       }
    }
    

    现在让我们将它们放在一个数据网格中并绑定到Role 属性。

    <DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False">
       <DataGrid.Resources>
          <local:UserRoleToEnumConverter x:Key="UserRoleToEnumConverter"/>
          <ObjectDataProvider x:Key="UserRoles" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
             <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:UserRole"/>
             </ObjectDataProvider.MethodParameters>
          </ObjectDataProvider>
       </DataGrid.Resources>
       <DataGrid.Columns>
          <materialDesign:DataGridComboBoxColumn Header="Role"
                                                 IsEditable="True"
                                                 Width="100">
             <materialDesign:DataGridComboBoxColumn.EditingElementStyle>
                <Style TargetType="ComboBox" BasedOn="{StaticResource {ComponentResourceKey TypeInTargetAssembly={x:Type ComboBox}, ResourceId=MaterialDataGridComboBoxColumnEditingStyle}}" >
                   <Setter Property="IsEditable" Value="True" />
                   <Setter Property="ItemsSource" Value="{Binding Source={StaticResource UserRoles}}"/>
                   <Setter Property="SelectedItem" Value="{Binding Role, Converter={StaticResource UserRoleToEnumConverter}}"/>
                </Style>
             </materialDesign:DataGridComboBoxColumn.EditingElementStyle>
          </materialDesign:DataGridComboBoxColumn>
       </DataGrid.Columns>
    </DataGrid>
    

    正如我上面已经提到的,由于您只调整编辑样式,您的列可能不会像您预期的那样表现。我建议您根本不要编辑样式,而是在列上设置绑定:

    <materialDesign:DataGridComboBoxColumn Header="Role"
                                           IsEditable="True"
                                           Width="100"
                                           ItemsSource="{Binding Source={StaticResource UserRoles}}"
                                           SelectedItemBinding="{Binding Role, Converter={StaticResource UserRoleToEnumConverter}}"/>
    

    这种方法可以满足您的要求,但是使用枚举常量直接显示 UI 文本是不好的。考虑本地化和具有多个作品的项目。您不会显示驼峰式文本。展望未来,也许您应该考虑使用更灵活、更强大的解决方案。

    【讨论】:

    • 做那个人一定感觉很好
    【解决方案2】:

    因为你已经有一个用于从数据库中转换字节值的枚举,你只需要一个专用的转换:

    public class UserRoleToStringConverter : IValueConverter
    {
        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var enumValue = UserRole.Member;
    
            if (value is UserRole)
            {
                enumValue = (UserRole)value;
            }
    
            switch (enumValue)
            {
                case UserRole.Member: return "Member";
                case UserRole.Role: return "Moderator";
            }
        }
    
        /// <summary>
        /// Converts a value.
        /// </summary>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="culture">The culture to use in the converter.</param>
        /// <returns>
        /// A converted value. If the method returns null, the valid null value is used.
        /// </returns>
        /// <exception cref="NotImplementedException"></exception>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    请注意,通过查看CultureInfo 转换器参数,如果需要,您可能会本地化您的字符串。

    另一种方法可能是绑定 ViewModel 的 string 属性,以便 XAML 并在 ViewModel 中添加字符串转换。

    【讨论】:

    • 感谢您的回答,如何在我的 xaml 代码中使用它?
    • 请关注stackoverflow.com/questions/9448862/… 此处的帖子,该帖子让您描述在使用您使用的 DisplayMemberPath 方法时如何使用转换器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 2022-01-16
    • 2018-06-21
    相关资源
    最近更新 更多