Refresh combobox list from other window, MVVM

2019-02-26 09:47发布

问题:

I'm working on some application and I have one problem. I have two windows (Bookings - parent and Guests - child). In the parent window, I have one combo box with list of guests and one button for adding new guest. When I click on that button Guests window (child window) opens. In the child window I am adding new guest into database and that works fine. My question is: How to refresh/update combo box list in parent window after adding a new guest in the child window? I know that changes in the property should be reflected in the view without retrieving data from database (thanks to binding).

Bookings.xaml

    <ComboBox ItemsSource="{Binding Path=Guests}" SelectedItem="{Binding Path=Guest}" Height="25" HorizontalAlignment="Left" IsEditable="True" IsTextSearchEnabled="True" Margin="119,10,0,0" Name="cbGuest" Padding="3,1,1,1"  TextSearch.TextPath="Name" VerticalAlignment="Top" VerticalContentAlignment="Center" Width="141" FontFamily="Times New Roman" FontWeight="Bold" FontSize="14">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock DataContext="{Binding}" Text="{MultiBinding StringFormat='\{0\} ', Bindings={Binding Path=Name}}" />
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    <Button BorderBrush="Black" Command="{Binding Path=btnAddGuest}" Content="Novi Gost" FontFamily="Times New Roman" FontWeight="Bold" Height="25" HorizontalAlignment="Left" IsDefault="True" Margin="266,10,0,0" Name="btnNewGuest" VerticalAlignment="Top" Width="62" />

BookingsViewModel.cs

    private tblGuest guest;
    public tblGuest Guest    // Selected guest from combo box
    {
        get
        {
            return guest;
        }
        set
        {
            guest = value;
            OnPropertyChanged("Guest");
        }
    }

    private ObservableCollection<tblGuest> guests;
    public ObservableCollection<tblGuest> Guests    // Guests list in the combo box
    {
        get
        {
            return guests;
        }
        set
        {
            guests = value;
            OnPropertyChanged("Guests");
        }
    }

    public ICommand _btnAddGuest;
    public ICommand btnAddGuest    // Command for opening child window
    {
        get
        {
            if (_btnAddGuest == null)
            {
                _btnAddGuest = new DelegateCommand(delegate()
                {
                    try
                    {
                        Guests guest = new Guests();
                        guest.ShowDialog();
                    }
                    catch
                    {
                        Trace.WriteLine("working...", "MyApp");
                    }
                });
            }
            return _btnAddGuest;
        }
    }

Guests.xaml

    <Button Command="{Binding Path= btnAddGuest}" Content="Dodaj" FontFamily="Times New Roman" FontWeight="Bold" Height="36" HorizontalAlignment="Left" Margin="12,402,0,0" Name="btnAddGuest" VerticalAlignment="Top" Width="62" IsDefault="True" />

This button (in Guest.xaml window) adds a new guest into database.

GuestViewModel.cs

    private tblGuest guest;
    public tblGuest Guest    // Guest to be added into database
    {
        get 
        {
            return guest;
        }
        set 
        {
            guest = value;
            OnPropertyChanged("Guest");
        }
    }

    public ICommand _btnAddGuest;
    public ICommand btnAddGuest    // Command for adding new guest
    {
        get
        {
            if (_btnAddGuest == null)
            {
                _btnAddGuest = new DelegateCommand(delegate()
                {
                    try
                    {
                        Service1Client wcf = new Service1Client();                           
                        wcf.AddGuest(Guest);    // "AddGuest()" WCF method adds new guest to database
                        wcf.Close();
                    }
                    catch
                    {
                        Trace.WriteLine("working...", "MyApp");
                    }
                });
            }
            return _btnAddGuest;
        }
    }

How to solve this problem? Is there any easy way? Can you, please, explain in detail your solution because I'm new in WPF, WCF and MVVM...

Best regards, Vladimir

回答1:

Just use your already existent connection to your GuestViewModel from your BookingsViewModel.

The following suggestion is not tested but you will get the idea

public ICommand btnAddGuest    // Command for opening child window
    {
        get
        {
            if (_btnAddGuest == null)
            {
                _btnAddGuest = new DelegateCommand(delegate()
                {
                    try
                    {
                        Guests guest = new Guests();
                        guest.ShowDialog();

                        // Add some Logic here and an is save check property to your GuestVM
                        // sample solution
                        // var vm = guest.DataContext as GuestViewModel;
                        // if(vm != null)
                        //     if(vm.IsSaved)
                        //     {
                        //         var model = vm.Guest as tblGuest;
                        //         Guests.Add(model);                // will add him to your list
                        //         Guest = model                     // will add him at your selected Guest
                        //     }
                    }
                    catch
                    {
                        Trace.WriteLine("working...", "MyApp");
                    }
                });
            }
            return _btnAddGuest;
        }
    }


回答2:

Hold BookingsViewModel instance in GuestViewModel class and call BookingsViewModel.OnPropertyChanged("Guest") when adding a new Guest (after wcf.AddGuest(Guest); line).



回答3:

If your creating your child window from your main window as well as your child view model, you could either look at your window closed event or if you need to know before the window gets closed you could also subscribe to the property changed event of the child window when it is created an when a property changes on the child view model you can check to see if its the property you want. If so update your list.

ChildViewModel n = new ChildViewModel()
n.PropertyChanged += new PropertyChangedEventHandler(n_PropertyChanged);
void n_PropertyChanged(object sender, PropertyChangedEventArgs e)
{ if(e.PropertyName == "myChangingObject")
//reload list
}


回答4:

If I understand problem correctly - you want to get a new guest from child window and update your collection in parent window - then there are many ways to do it.

For example:

  1. Add event "OnSuccessful" to your GuestsViewModel and then attach BookingsViewModel.

  2. Implement EventAggregator pattern (subscriber - listener)

  3. Just refresh guests after closing child window (show it as a modal dialog).



回答5:

A simple callback interface can help you. The client should subscribe and if server pushes a notification then either populate the combobox or refresh.

Here is a tutorial: http://idunno.org/archive/2008/05/29/wcf-callbacks-a-beginners-guide.aspx

I used to created a "login" method and store every client callback on server side. Then whenever I needed to use push notification I simply used this stored client-callback. On the client side the you can handle the recieved message / event as you want.



回答6:

OK, I added this lines in my "GuestsViewModel.cs":

    private BookingsViewModel _BookingsViewModel;
    public BookingsViewModel BookViewModel    // Property
    {
        get
        {
            return _BookingsViewModel;
        }
        set
        {
            _BookingsViewModel = value;
            OnPropertyChanged("BookViewModel");
        }
    }

    public GuestsViewModel(BookingsViewModel bvm)    // Constructor with one parameter
    {
        BookViewModel = bvm;
    }

    public ICommand _btnAddGuest;
    public ICommand btnAddGuest 
    {
        get
        {
            if (_btnAddGuest == null)
            {
                _btnAddGuest = new DelegateCommand(delegate()
                {
                    try
                    {
                        Service1Client wcf = new Service1Client();                           

                        wcf.AddGuest(Guest);
                        BookingsViewModel.OnPropertyChanged("Guests");    // You said to add this
                        wcf.Close();
                    }
                    catch
                    {
                        Trace.WriteLine("working...", "MyApp");
                    }
                });
            }
            return _btnAddGuest;
        }
    }

Did you mean something like this above? This don't work... I suppose I should add one more thing? Sorry, maybe this questions are stupid but I don't have idea how to solve this problem...

Regards, Vladimir