Click here to Skip to main content
15,890,336 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
Hi,

I am implementing one application in WPF MVVM.

I am having WPF application and DLL class library.

For example in application I am having 2 combobox's

I am binding database servers to the 1st combobox.

based on the 1st combobox selection then I am binding the 2nd combobox with list of all the databases.

my problem is here when we select the server from the 1st combobox it keep open state and Application gets hangs some time. I am unable to do other operations at that particular time.

My XAML code below :
<ComboBox Grid.Column="2" 
                  Grid.Row="1" Grid.ColumnSpan="3" MaxHeight="25" 
                  ItemsSource="{Binding Servers}"  
                  SelectedValue="{Binding SelectedServer}"
                  Name="cboServer" 
                  DataContext="{Binding Source={StaticResource vm}}" />

<ComboBox Grid.Column="2" 
             Grid.Row="2" Grid.ColumnSpan="3" MaxHeight="25" 
             ItemsSource="{Binding Databases}"
             SelectedValue="{Binding SelectedDatabase}"
             Name="cboDatabase" 
             DataContext="{Binding Source={StaticResource vm}}"/>

My view model code as follows:
/// <summary>
        /// For Servers Properties
        /// </summary>
        private ObservableCollection<string> _Servers;         
        private string _SelectedServer;
        public ObservableCollection<string> Servers        
        {
            get { return _Servers; }
            set
            {
                _Servers = value;
                OnPropertyChanged("Servers");
            }
        }
        public string SelectedServer
        {
            get { return _SelectedServer; }
            set
            {
               _SelectedServer = value;
                OnPropertyChanged("SelectedServer");

_extractInfo.ServerName = !string.IsNullOrEmpty(_SelectedServer) ? _SelectedServer : null;
         if (!string.IsNullOrEmpty(_extractInfo.ServerName))
              GetDatabases();
           else
              Databases = new ObservableCollection<string>();

}
}

         /// <summary>
        /// For Databases 
        /// </summary>
        private ObservableCollection<string> _DataBaseList; 
        private string _SelectedDatabase;
        public ObservableCollection<string> Databases         
        {
            get { return _DataBaseList; }
            set
            {
                _DataBaseList = value;
                           
                OnPropertyChanged("Databases");
            }
        }

//calling function GetDatabases(): ExtractHelper is located in my DAL Project
private ObservableCollection<string> GetDatabases()
        {
           try
            {
               
             Databases = new ObservableCollection<string>(ExtractHelper.RequestList(_extractInfo, ExtractHelper.RequestTypes.Databases).AsEnumerable().Select(x => x.DatabaseName));
                
                return Databases;
            }
            catch(Exception ex)
            { 
                MessageBox.Show(ex.Message);
            }
            return null;
        }

Kindly let me know is there any wrong with the code, if its wrong let me know how we achieve without freeze the application

What I have tried:

I have tried using Dispatcher like below.
When I am using below code, 1st combobox gets closed when I select the server, but application gets freezes some time then its displaying the databases list in the 2nd combobox.
public string SelectedServer
        {
            get { return _SelectedServer; }
            set
            {
               _SelectedServer = value;
                OnPropertyChanged("SelectedServer");

                _extractInfo.ServerName = !string.IsNullOrEmpty(_SelectedServer) ? _SelectedServer : null;
                
                if (!string.IsNullOrEmpty(_extractInfo.ServerName))
                    
                Application.Current.Dispatcher.BeginInvoke(
                    new Action(() =>
                    {

                     GetDatabases();
                        
                    }),
                    DispatcherPriority.Background,
                    null
                );
                    
                else
                    
                   Databases = new ObservableCollection<string>();
                 }
        }
Posted
Updated 15-Nov-17 1:23am
v9

Hi,
I think you take time to update the list of databases.
You can use a backgroundworker to update this list.
I will use this scenario:
- delete the list of databases after selection of server and disable the control of database
- launch the backgroundworker to get the new list that will be send as parameter in the completed event
- In the completed event handling set the new data base list and enable the control.

Best regards
 
Share this answer
 
Comments
D-Kishore 15-Nov-17 3:52am    
Hi, Do you have any sample application? If you have please post here. Thanks
Async...await TPL framework[^] is the answer. Spin a task off on another thread to release the UI thread and stop it from freezing.

Multithreaded program is something that needs a n investment in learning. Best to check out the following resources from the TPL gurus before doing any work:

* Async and Await Introduction[^] - Stephen Cleary (a lot or helpful information on his blog)

* Asynchronous Programming Demystified - YouTube[^] by Stephen Cleary

* Async Best Practices for C# and Visual Basic - YouTube[^] by Mads Torgersen[^] - Chief language architect at Microsoft. This uses a problem & solution approach - very helpful!
 
Share this answer
 
If you are using .NET 4.5 and above, I would recommend that you take advantage of the new features for moving the ObservableCollection off the dispatcher. Basically, you want to start off with something like this:
C#
public class MyViewModel
{
  private readonly object _lock = new object();
  public ObservableCollection<string> Servers { get; private set; }
  public MyViewModel()
  {
    _servers = new ObservableCollection<string>();
    BindingOperations.EnableCollectionSynchronization(Servers, _lock);
  }

  public async void UpdateServers()
  {
    await Task.Run(()=>
      {
        MySlowService slowService = new MySlowService();
        string[] servers = await slowService.GetSlowOperation();
        foreach (string server in servers)
        {
          Servers.Add(server);
        }
      });
  }
}
Please note, this is just an example to show you how to combine these techniques; you're going to have to do work to integrate this into your solution.
 
Share this answer
 
Comments
Graeme_Grant 15-Nov-17 7:32am    
Have you heard the proverb "Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime" [^]? I was trying to give him the tools to understand why. Now he will take a shortcut... You and I both know that Multithreading is not quite that simple...
Pete O'Hanlon 15-Nov-17 7:35am    
Without reading your links, he's not going to get the shortcut from this; this is far from a working sample.
Graeme_Grant 15-Nov-17 7:37am    
There's enough there to make him dangerous... Mads video has the answer that he needs ;)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900