C# WPF Problem with Viewmodels executing business logic twice. Once on Application Loading and then again when User Selects their View

scott thomson 66 Reputation points
2021-06-04T07:24:54.8+00:00

I have a problem trying to find a way to avoid my ViewModels executing their business logic eg methods calling to database to load tables etc twice. I can see that when I run my application (windows desktop) the ViewModels execute their methods during the startup process of the application and then for those that a user then clicks on a button to bring their View up they execute again. I suspect my issue is that in the ViewModels I have calls in their constructors which then go to a method and this populate the lists by calling to database. IF I remove these methods from the constructors no business logic methods execute on load, however then I cant get the business logic to execute on the button click.

The above duplication of calls causes a long delay for user as one of the database calls takes quite a while eg 10 secs to bring in 7,000 records

I cant think of a way to remove these calls from the constructor but then get the ViewModel to execute these calls when their View is activated via the button on the parent View. Ive given MVVMLight messenger a go but had issues with the sequence. the message would not fire off at right sequence.

I will set out below what I think are the main pieces of code to try and give you an idea of how Ive wired things up. There must be an Industry norm for designing out this issue, but Im not a professional.

MainWindow View with several Child View/ViewModels:
<Window.Resources>
<DataTemplate DataType="{x:Type reports:ReportsTabViewModel}">
<reports:ReportsTabView />
</DataTemplate>
<DataTemplate DataType="{x:Type masterData:MasterDataTabViewModel}">
<masterData:MasterDataTabView />
</DataTemplate>
</Window.Resources>

MainWindow VIewModel is instantiated in the App.xaml.cs:
MainWindow app = new MainWindow();
MainWindowViewModel context = new MainWindowViewModel();
app.DataContext = context;
app.Show();

A series of buttons to bring into View the ViewModel/Views

A ContentControl where they bind when user clicks on their button:
<ContentControl x:Name="PageContentControl" Content="{Binding CurrentPageViewModel}" Grid.Row="1" Grid.RowSpan="2" Grid.Column="1"/>

Then the child View/Models (which are the CurrentPageViewModel):

Child View.xaml which is a UserControl:
<UserControl.DataContext>
<local:PeopleTabViewModel/>
</UserControl.DataContext>

Then the Child ViewModel constructor which fires twice (one call method shown that brings in data from database and is the step I need to avoid being done twice):

    public PeopleTabViewModel()
    {
        FillPeople();
        FillModules();
        SetPerson();
        SetCollectionViews(SelectedPerson.PersonID.ToString());
        ChangePerson();
    }

    private void FillPeople()
    {
        var q = (from a in db.People
                 where !a.Archived
                 orderby a.LastName
                 select a).ToList();
        _people = new List<Person>(q);
    }
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
809 questions
{count} votes

2 answers

Sort by: Most helpful
  1. scott thomson 66 Reputation points
    2021-06-08T06:45:07.057+00:00

    DaisyTan this editor will not allow me103240-viewmodelproblemscott.txt to insert many lines of code so all the code is in the attachment


  2. DaisyTian-1203 11,621 Reputation points
    2021-06-09T08:46:00.883+00:00

    I made a demo to initialise data on the button click as below:
    XAML code:

    <Window.DataContext>  
            <local:ViewModel></local:ViewModel>  
        </Window.DataContext>  
        <StackPanel>  
            <WrapPanel>  
                <DataGrid Width="200" Height="200" DataContext="{Binding Model1s,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding }"></DataGrid>  
                <Button Content="Load Data" Width="120" Height="38" Command="{Binding Btn1Cmd,Mode=TwoWay}" Margin="20 0 0 0"/>  
            </WrapPanel>  
      
            <WrapPanel Margin="0 30 0 0">  
                <DataGrid Width="200" Height="200" ItemsSource="{Binding Model2s,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></DataGrid>  
                <Button Content="Load Data" Width="120" Height="38" Command="{Binding Btn2Cmd}" Margin="20 0 0 0"/>  
            </WrapPanel>  
             
        </StackPanel>  
    

    C# code is:

      public class ViewModel: NotifyObject  
        {  
      
            public ObservableCollection<Model1> model1s;  
            public ObservableCollection<Model1> Model1s  
            {  
                get { return model1s; }  
                set  
                {  
                    this.model1s = value;  
                    OnPropertyChanged("Model1s");  
                }  
            }  
      
      
            public ObservableCollection<Model2> model2s;  
            public ObservableCollection<Model2> Model2s  
            {  
                get { return model2s; }  
                set  
                {  
                    this.model2s = value;  
                    OnPropertyChanged("Model2s");  
                }  
            }  
      
            public ViewModel()  
            {  
                
            }  
      
            private RelayCommand _btn1Cmd;  
            public RelayCommand Btn1Cmd  
            {  
                get  
                {  
                    if (_btn1Cmd == null) return new RelayCommand(() => LoadData1());  
                    return _btn1Cmd;  
                }  
      
                set  
                {  
                    _btn1Cmd = value;  
                }  
            }  
      
            private void LoadData1()  
            {  
                Model1s = new ObservableCollection<Model1>()  
                {  
                    new Model1(){Name="Jack",Sex="Gril"},  
                    new Model1(){Name="Daniel",Sex="Boy"},  
                };  
            }  
      
      
            private RelayCommand btn2Cmd;  
            public RelayCommand Btn2Cmd  
            {  
                get  
                {  
                    if (btn2Cmd == null) return new RelayCommand(() => LoadData2());  
                    return btn2Cmd;  
                }  
      
                set  
                {  
                    btn2Cmd = value;  
                }  
            }  
      
            private void LoadData2()  
            {  
                Model2s = new ObservableCollection<Model2>()  
                {  
                    new Model2(){ID="001",Age=25},  
                    new Model2(){ID="002",Age=20},  
                };  
            }  
        }  
      
        public class Model1  
        {  
            public string Name { get; set; }  
            public string Sex { get; set; }  
        }  
      
        public class Model2  
        {  
            public string ID { get; set; }  
            public int Age { get; set; }  
        }  
      
        public class NotifyObject : INotifyPropertyChanged  
        {  
            public event PropertyChangedEventHandler PropertyChanged;  
      
            protected void OnPropertyChanged(string propertyName)  
            {  
                if (PropertyChanged != null)  
                {  
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));  
                }  
            }  
      
        }  
    

    Result code is:
    103803-3.gif


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.