简体   繁体   中英

WPF Entity Framework refresh one context entity

I have a simple MVVM WPF app with database-first EF dbContext (which is Global.Database in my app), which is long-living in my application. I have a window with a listbox with ItemsSource binded to viewmodel property called Clients which is an ObservableCollection of my dbmodel Client .

The SelectedItem of this listbox is binded to a viewmodel property called SelectedClient .

In Client entity class there is a field called last_status which is a simple int from my database.

So, in my view, when i select client from listbox, a label binded to SelectedClient's last_status should show the value of last_status .

I added a button and a refresh command into my viewmodel. All i want is: when i manually change last_status for the client in my database and press refresh button in my view, the content of label should change. But i have absolutely no idea how to achieve this. Here is the part of my viewmodel code (i use Catel, but it doesn't matter for this case):

public ClientManagerWindowViewModel()
    {

       RefreshClientInfoCommand = new Command(OnRefreshClientInfoCommandExecute);

       Clients = new ObservableCollection<Client>();
       RefreshClients();
    }

public ObservableCollection<Client> Clients
    {
        get { return GetValue<ObservableCollection<Client>>(ClientsProperty); }
        set { SetValue(ClientsProperty, value); }
    }

    public static readonly PropertyData ClientsProperty = RegisterProperty("Clients", typeof(ObservableCollection<Client>));

public Client SelectedClient
    {
        get
        {return GetValue<Client>(SelectedClientProperty);}
        set
        {
            SetValue(SelectedClientProperty, value);
        }
    }

    public static readonly PropertyData SelectedClientProperty = RegisterProperty("SelectedClient", typeof(Client));


//here is my refresh button command handler:

 public Command RefreshClientInfoCommand { get; private set; }

 private void OnRefreshClientInfoCommandExecute()
 {
     RefreshClientInfo(SelectedClient);
 }

//and here is my "logic" for working with dbcontext:

private void RefreshClients()
    {
        var qry = (from c in Global.Database.Clients where c.client_id != 1 select c).ToList();
        Clients = new ObservableCollection<Client>(qry);
    }

private void RefreshClientInfo(Client client)
    {
        Global.Database.Entry(client).Reload();
    }

My XAML for a listbox:

<ListBox
                    x:Name="ClientsListBox"
                    Grid.Row="1"
                    Margin="5"
                    DisplayMemberPath="fullDomainName"
                    IsSynchronizedWithCurrentItem="True"
                    ItemsSource="{Binding Clients}"
                    SelectedItem="{Binding SelectedClient}" />

My XAML for a label:

<Label Margin="5" Content="{Binding SelectedClient.last_status}" />

And for a button:

<Button Command="{Binding RefreshClientInfoCommand}" Content="↻"/>

For now, when i change a client's last_status value manually in database and press refresh button nothing happens. But when i select another client in a listbox and then return to needed client - label content updates correctly. I know, maybe i miss something very stupid and simple, but i cant' figure out what exactly. Maybe i need to force change SelectedClient in my button command handler, or call SelectedClient s setter somehow... Please, help me. Thanks a lot.

Well, i figured out what was wrong with my code.

My databindig was set to SelectedClient.last_status . For some reason it didn't work as i expected. So i created a new viewmodel property called LastStatus and modified my RefreshClientInfo:

 private void RefreshClientInfo(Client client)
    {
        Global.Database.Entry(client).Reload();
        LastStatus = client.last_status;
        SetValue(SelectedClientProperty, client);
    }

and binded label to this new property. Now everything works correct.

You need to set the SelectedClient property to the new Client object that you get back from the EF query.

You could do this by storing the client_id of the currently selected client before you query the database and then select the new Client object with this particular client_id .

Try this:

private void RefreshClients()
{
    int? currentlySelectedClientId = (SelectedClient != null) ? SelectedClient.client_id : default(int?);
    var qry = (from c in Global.Database.Clients where c.client_id != 1 select c).ToList();
    Clients = new ObservableCollection<Client>(qry);
    if (currentlySelectedClientId.HasValue)
        SelectedClient = Clients.FirstOrDefault(x => x.client_id = currentlySelectedClientId.Value);
}

Edit: Make sure that you fetch the updated records from the DB:

private void RefreshClientInfo(Client client)
{
    var newClient = (from c in Global.Database.Clients where c.client_id == client.client_id select c).ToList();
    SetValue(SelectedClientProperty, newClient[0]);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM