0

I have one View that has another one inside. I want to make ViewModel for both of them. But apparently Binding in Child View is not working properly or I have done wrong binding, perhaps.

I have debugged that Child ViewModel is recreated every time I have selected different row in Parent ViewModel. But UI, doesn't refresh, despite UpdateSourceTrigger=PropertyChanged.

If I edit Binding in XAML while running app then it gets refreshed (as Binding probably is regenerated).

I could set UpdateSourceTrigger=Explicit, but I can't call UpdateSource from none of ViewModels.

PARENT VIEW:

<UserControl ... DataContext="{Binding ProjectsViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
        <poc:AdvancedListView ItemsSource="{Binding Projects}" SelectedObject="{Binding SelectedProject, Mode=TwoWay}"/>
        ...
        <ScrollViewer>
            <StackPanel Orientation="Vertical">
                ...
                <poc:Section SectionName="ATTACHMENTS">
                    <poc:AttachmentsControl DataContext="{Binding AttachmentsViewModel, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" /> // THIS IS BINDING BETWEEN VM
                </poc:Section>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</UserControl>

PARENT VIEWMODEL:

public class ProjectsViewModel : BaseViewModel
{
    public ProjectsViewModel(ObservableCollection<Project> projects)
    {
        this.Projects = projects;
    }

    public ObservableCollection<Project> Projects { get; }

    private Project selectedProject;
    public Project SelectedProject
    {
        get { return selectedProject; }
        set
        {
            SetPropertyAndNotify(ref selectedProject, value);
            AttachmentsViewModel = new AttachmentsViewModel(selectedProject.Attachments); // THIS IS CREATION OF CHILD VM
        }
    }

    public AttachmentsViewModel AttachmentsViewModel { get; set; }
}

CHILD VIEW:

<UserControl ... x:Name="attachmentControl">
    <Grid x:Name="mainGrid">
        ...
        <ListView x:Name="attachmentsListView" ItemsSource="{Binding Attachments, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" SelectionMode="Single"> // THIS IS BINDING TO LIST THAT IS NOT WORKING
            <ListView.View>
                <GridView>
                    ...
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

CHILD VIEWMODEL:

public class AttachmentsViewModel : BaseViewModel
{
    public ObservableCollection<Attachment> Attachments { get; set; }

    public AttachmentsViewModel(ObservableCollection<Attachment> attachments)
    {
        Attachments = attachments;
    }

}

What I do wrong or what concept I have understood wrong?

jaydopartu
  • 63
  • 1
  • 8

1 Answers1

0
public class ProjectsViewModel : BaseViewModel
{
    public ProjectsViewModel(ObservableCollection<Project> projects)
    {
        this.Projects = projects;
    }

    public ObservableCollection<Project> Projects { get; }

    private Project selectedProject;
    public Project SelectedProject
    {
        get { return selectedProject; }
        set
        {
            SetPropertyAndNotify(ref selectedProject, value);

            // THIS IS CREATION OF CHILD VM
            AttachmentsViewModel = new AttachmentsViewModel(selectedProject.Attachments);         
        }
    }

    private AttachmentsViewModel _attachmentsViewModel;
    public AttachmentsViewModel AttachmentsViewModel
    {
       get => _attachmentsViewModel;
       set => SetPropertyAndNotify(_attachmentsViewModel, value);
    }
}
public class AttachmentsViewModel : BaseViewModel
{
    // This should be a Read Only property
    public ObservableCollection<Attachment> Attachments { get; /* set; */}

    public AttachmentsViewModel(ObservableCollection<Attachment> attachments)
    {
        Attachments = attachments;
    }
}

Additional recommendation: Adding extra logic to the property setter - is bad. Surely in the BaseViewModel implementation there is an opportunity to set the dependence of properties on each other in a different way.

EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • Thanks a lot. Summarizing - I forget to make child ViewModel instance to work with INotifyPropertyChanged. For now is working for me. So thank you very much. I would rethink this ViewModel design as I finish. My implementation of BaseViewModel is quite simple, just 2 methods for INotifyPropertyChanged. Have a nice day sir! – jaydopartu May 02 '21 at 11:16
  • У меня есть простая реализация. Воспользуйтесь ею: [BaseInpc](https://stackoverflow.com/questions/67183351/wpf-two-way-binding-does-not-work-for-checkboxes-inside-a-combobox/67184113#67184113) – EldHasp May 02 '21 at 11:19
  • @EldHasp, could you explain why adding extra logic to property setter is bad? I often do it, for example when I change a ComboBox.SelectedItem, and this has effects on other variables. I know this is not the original question, but interesting to understand too. – Siegfried.V May 02 '21 at 15:34
  • This confuses the code. Makes it hard to read. Some bugs are possible in case of errors. The property has been assigned one value, and displayed in the View another. It is better to knit the base already used in WPF implementation. See how DependecyProperty is implemented. After changing the property, the method is called in which the changed value is being processed. In my class, I made a simplified analogue. – EldHasp May 02 '21 at 16:56
  • Also, in large tasks, the phased creation of the ViewModel can be used. Properties can be declared in the base class. The derived class adds its own properties. And we must ask them to depend on each other. Property setters simply cannot do this. These difficulties are not visible on simple tasks. But it's better to immediately get used to doing it right. – EldHasp May 02 '21 at 17:00