Wyswietlenie Comboboxa w DataGrid

0

Cześć,
Mam problem i proszę o pomoc bo motam się z tym strasznie
Mam takie dwie klasy:


namespace BK
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public partial class permission : INotifyPropertyChanged
    {
        private int ID;
        private string Name;

        public int id { get => ID; set { ID = value; OnPropertyChanged(nameof(id)); } }
        public string name { get => Name; set { Name = value; OnPropertyChanged(nameof(name)); } }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}



namespace BK
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public partial class user : INotifyPropertyChanged
    {
        private int ID;
        private string Login;
        private string Password;
        private string FirstName;
        private string LastName;
        private int? Permissions_id;
        private bool? Status;
        private DateTime? AddDate;
        private DateTime? ModifyDate;

        public int id { get => ID; set { ID = value; OnPropertyChanged(nameof(id)); } }
        public string login { get => Login; set { Login = value; OnPropertyChanged(nameof(login)); } }
        public string password { get => Password; set {Password = value; OnPropertyChanged(nameof(password)); } }
        public string firstName { get => FirstName; set { FirstName = value; OnPropertyChanged(nameof(firstName)); } }
        public string lastName { get => LastName; set { LastName = value; OnPropertyChanged(nameof(lastName)); } }
        public Nullable<int> permissions_id { get => Permissions_id; set{ Permissions_id = value; OnPropertyChanged(nameof(permissions_id)); } }
        public Nullable<bool> status { get => Status; set {Status = value; OnPropertyChanged(nameof(status)); } }
        public Nullable<System.DateTime> addDate { get => AddDate; set { AddDate = value; OnPropertyChanged(nameof(addDate)); } }
        public Nullable<System.DateTime> modifyDate { get => ModifyDate; set { ModifyDate = value; OnPropertyChanged(nameof(modifyDate)); } }

        public event PropertyChangedEventHandler PropertyChanged;

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

Mam również ViewModel ( mam nadzieje, że udało mi się to poprawnie stworzyć)


namespace BK.ViewModel
{
    class UserViewModel : INotifyPropertyChanged
    {
        private user _user;
        private permission _permission;
        public event PropertyChangedEventHandler PropertyChanged;
        bkEntities _db;
        public user user
        {
            get { return _user; }
            set
            {
                _user = value;
                OnPropertyChanged(nameof(user));
            }
        }
        public permission permission { get { return _permission; } set { _permission = value; OnPropertyChanged(nameof(permission)); } }
        private ObservableCollection<user> _lstUsers;
        private ObservableCollection<permission> _lstPermissions;

        public ObservableCollection<user> lstUsers
        {
            get { return _lstUsers; }
            set
            {
                _lstUsers = value;
                OnPropertyChanged(nameof(lstUsers));
            }
        }
        public ObservableCollection<permission> lstPermissions
        {
            get { return _lstPermissions; }
            set
            {
                _lstPermissions = value;
                OnPropertyChanged(nameof(lstPermissions));
            }
        }
        public UserViewModel()
        {
            _db = new bkEntities();
            LoadSetting();
        }
        private void LoadSetting()
        {
            lstUsers = new ObservableCollection<user>(_db.users);
            lstPermissions = new ObservableCollection<permission>(_db.permissions);
        }


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


    }
}

I widok w którym mam Datagrida

<UserControl x:Class="BK.UserControlUsers"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:local="clr-namespace:BK"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">

    <Grid>
<DataGrid x:Name="DBGrid" ItemsSource="{Binding lstUsers}" HorizontalAlignment="Stretch" Height="auto"  Margin="5,5,5,5" VerticalAlignment="Top" Width="auto" AutoGenerateColumns="False" MouseDoubleClick="DBGrid_MouseDoubleClick" SelectionMode="Single" SelectionUnit="FullRow" IsReadOnly="True">


                <DataGrid.Columns>
                    <DataGridTextColumn Header="ID" Binding="{Binding id}"/>
                    <DataGridTextColumn Header="Imię" Binding="{Binding firstName}"/>
                    <DataGridTextColumn Header="Nazwisko" Binding="{Binding lastName}"/>
                    <DataGridTextColumn Header="Login" Binding="{Binding login}"/>
                    <DataGridCheckBoxColumn Header="Status" Binding="{Binding status}"/>
                    <DataGridTemplateColumn  Header="Upr">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox x:Name="cbStatus" Style="{StaticResource MaterialDesignFloatingHintComboBox}" ItemsSource="{Binding lstPermissions}" DisplayMemberPath="name"></ComboBox>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                        <DataGridTextColumn Header="Data dodania" Binding="{Binding addDate,ConverterCulture=pl-PL}"/>
                    <DataGridTextColumn Header="Data modyfikacji" Binding="{Binding modifyDate,ConverterCulture=pl-PL}"/
                </DataGrid.Columns>
            </DataGrid>
        </DockPanel>
    </Grid>
</UserControl>

I niestety nie potrafię w kolumnie upr wyświetlić zamiast ID uprawnień z klasy user, nazwy z klasy permission (na podstawie id z klasy user). Czyli użytkownik ma pole permission_id które jest w relacji z id z klasy permission.
Jest już późno i trochę kombinowania z mną więc proszę o pomoc ewentualnie o wskazówkę jak ogarnąć tego comboxa. Bo jeszcze przede mną edycja w nowym oknie tego wiersza z ustawieniem comboboxa na odpowiedniej wartości.

0

Stworz multivalue converter, prześlij obie klasy jako argumenty i zwróć co chcesz

0

@gswidwa1:
Przypisałem jeszcze DataContext w taki sposób

    <UserControl.DataContext>
        <VM:UserViewModel/>
    </UserControl.DataContext>

Niestety w XAML Binding Failures otrzymuje taki błąd

Severity	Count	Data Context	Binding Path	Target	Target Type	Description	File	Line	Project
Error	1	null	lstPermissions	DataGridComboBoxColumn.ItemsSource	IEnumerable	Cannot find governing FrameworkElement or FrameworkContentElement for target element.			

Będę jeszcze czytał o multivalue.

0

W view modelu powinieneś posiadać listę modeli (klasy User), które już mają w sobie obiekt uprawnień. Wtedy nie byłoby problemu z comboxem.

Ale można też zrobić na piechotę, czyli z multi value converter, ale jak tym się nie bawiłeś to mogą wyjść po drodze inne problemy.

0

@Piotr.Net: Jeżeli dobrze rozumiem to muszę w klasie User bo listę modeli już mam (lstUser) dodać geta i seta który na podstawie permissions_id z klasy users zwróci mi nazwę uprawnienia ( pobierając ją z bazy) i to podstawić do widoku.

0

Mniej więcej tak, ale jak pobierasz klasę User z jakiejś warstwy danych do warstwy prezentacji to najlepiej jakby ta klasa miała w sobie obiekt uprawnień już ustawiony. Wtedy łatwo się do niego zbindujesz, np. do nazwy

0

@Piotr.Net:
Czy chodzi o takie coś w klasie user:


        public permission perm
        {
            get
            {


                permission Perm = _db.permissions.Where(u => u.id == permissions_id).Single();


                return Perm;
            }
        }
        public ObservableCollection<permission> lstPerm
        {
            get
            {
                LstPerms = new ObservableCollection<permission>( _db.permissions);
                return LstPerms;
            }
        }

Bo w przypadku pierwszego wpisu i dodania następnie do xaml

<DataGridTextColumn Header="Uprawnienia" Binding="{Binding perm.name}"/>

to uprawnienie wyświetla mi poprawnie.
Natomiast w przypadku

DataGridTemplateColumn  Header="Upr">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox x:Name="cbStatus" Style="{StaticResource MaterialDesignFloatingHintComboBox}" ItemsSource="{Binding lstPerm}" DisplayMemberPath="{Binding permissions_id}" >
                                    
                                </ComboBox>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>

Niestety mam tylko pustą listę.
Nie wiem czy w ogóle dobrze kombinuję.
Mam również pytanie jak powinny w takim wypadku wyglądać setty. (w jednym i drugim przykładzie).

0

A pod debugem zmienna LstPerms w getcie ma jakieś obiekty?

W xaml'u bindujesz się do lstPerm, czyli lista obiektów typu permission. W DisplayMemberPath nie możesz mieć permissions_id. Jedyne opcje to id lub name (czyli właściwości klasy permission, a nie user)

Co do set'ów, najlepiej mieć klasę bazodanową (gdzie masz własciwość permission_id) i klase kliencką (gdzie masz właściwość permission) i przy odczycie/zapisie zamieniać to w odpowiednią stronę.
Ale jak już tak lecisz na piechotę to właściwość perm w set może ustawiać permissions_id, które zapisujesz do bazy. W xaml dla ComboBox ustawiasz SelectedItem={Binding perm}, wtedy przy wybraniu z comboboxa obiektu ustawi właściwość permission_id (bo wcześniej ustawisz tam przypisanie id na secie)

0

@Piotr.Net:
W xamlu zbindowałem się do lstPerm i ustawiłem DisplayMemberPath = "name" i teraz wyświetla mi listę z nazwą uprawnienia. Mam jeszcze taki problem, że Combobox nie jest ustawiony domyślnie od razu na podstawie premission_id ( czyli muszę rozwinąć i dopiero wtedy widzę uprawnienia) a chciałbym aby wyświetlało się od razu domyślne uprawnienie z możliwością zmiany.
Czy mógłbyś mi podać link gdzie mógłbym zobaczyć na czym polega klasa bazodanowa i klasa kliencka.

0

Aby combobox miał ustawioną od razu jakąś wartość to musisz ustawić właściwość zbindowaną do SelectedItem={...}, pamietając o OnPropoertychanged()

Tu masz przykład aplikacji od samego MS, co prawda UWP, ale to bez znaczenia:
https://github.com/microsoft/InventorySample/tree/master

Znajdziesz tam na przykład klasy:
Order.cs - bazodanowa
OrderModel.cs - kliencka
OrderService.cs - tam jest zamieniana klasa bazodanowa na kliencką, gdy klient potrzebuje pobrać obiekty typu Order i odwrotnie gdy zaktualizujesz obiekt i chcesz zapisać do bazy

2

@Piotr.Net:
W końcu mi się udało to zrobić.
xaml'a mam następującego

<ComboBox x:Name="cbStatus" Style="{StaticResource MaterialDesignFloatingHintComboBox}" ItemsSource="{Binding lstPerm}" DisplayMemberPath="name" SelectedValuePath="id" SelectedValue="{Binding permissions_id}">

I teraz poprawnie wyświetla mi dane.

1 użytkowników online, w tym zalogowanych: 0, gości: 1