I receive a query from database containing an array of users and in the user class I have a byte field named Role
. I want my DataGridComboBoxColumn
to have 2 items. if Role == 0
it's "Member"
and if Role == 1
it's "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>
There are multiple issues in your code and XAML that you should resolve.
DataContext.Role
will not work.UserRole
type is an enum
, but you want to display its constant names in a combo box column as items to select from. The ItemsSource
property expects an enumerable type which an enum is not.User
s to your data grid. That means the data context of your column is a User
instance. In this case your combo box ItemsSource
binding does not make sense, since you are trying to bind the data grid data context, which does not contain the current user and even if it did, the Role
property is a byte
, not an enumerable.DisplayMemberPath
to UserRole
, but it will not work, because you want to bind your combobox to constants of the UserRole
enum, which does not contain a property UserRole
.User
type contains both Role
of type byte
, as well as UserRole
of type UserRole
. It is unclear which property of both you want to use and what the other one is used for.int
, which is the default. If you use values internally as byte
anyway, you might consider changing it to byte
.DisplayMemberPath
, but I think what you really want to do is bind the Role
property as selected item.In the following I assume that you want to use the Role
property of type byte
for binding and that you want to display the constant names of the UserRole
enum in your combo box column.
First, you have to create an enumerable of your enum constants. You could provide a collection in code, that you could bind to, but since it does never change and is currently only used in the data grid, you can create it entirely in XAML using an ObjectDataProvider
like this:
<ObjectDataProvider x:Key="UserRoles" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:UserRole"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
Next, we need to create a converter that converts a byte
to a UserRole
and back.
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;
}
}
Now let us put it all together in a data grid and bind to the Role
property.
<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>
As I already mentioned above, since you only adapt the editing style, you column might not behave like you expect. I recommend you to not edit the style at all, but rather set the bindings on the column:
<materialDesign:DataGridComboBoxColumn Header="Role"
IsEditable="True"
Width="100"
ItemsSource="{Binding Source={StaticResource UserRoles}}"
SelectedItemBinding="{Binding Role, Converter={StaticResource UserRoleToEnumConverter}}"/>
This approach will work for your requirements, but using enum constants to display UI texts directly is bad. Think about localization and items with more than a single work. You would not display camel cased text. Going forward, maybe you should consider using a more flexible and robust solution.