Search code examples
c#wpfmvvm

WPF Pass data from View to ViewModel in UserControl


I have ListBox with list of notes (NoteList) that represented by the Note Model (subject, text, id):

<ListBox x:Name="NotesListBox" ItemsSource="{Binding NoteList}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <UserControls:NoteHeadControl/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

ListItem I created in separate UserControl (NoteHeadControl). Was decided to pass Id by Tag property. I`m not sure that it is the best way of doing this, but I don't know how to do it other way.

    <Button Name="OpenButton" Tag="{Binding id}">
        <DockPanel>
            <Label Content="{Binding subject}"/>
            <Button Command="{Binding DeleteNoteCommand}" Name="DeleteNoteButton" Content="Delete"/>
        </DockPanel>
    </Button>

For NoteHeadControl I created separate ViewModel (NoteHeadControlViewModel ) where my commands and properties should be. For example, I want to create a DELETE button on note to delete it from my database. For this Id parameter is required in NoteHeadControlViewModel.

The problem happens when I try to pass Id data from View to ViewModel:

    public NoteHeadControl()
    {
        InitializeComponent();

        NoteHeadControlViewModel noteHeadControlViewModel = new NoteHeadControlViewModel((int)this.OpenButton.Tag);
        DataContext = noteHeadControlViewModel;
    }

It seems that UserControl is not completely initialized at the moment when (int)this.OpenButton.Tag is run and OpenButton is just not found (null).

The question is, how can I pass data (Id in my case) from Window -> UserControl -> UserControl`s ViewModel ?

I tried to set Tag property not in the Button, but in UserControl controller. UserControl is initialized, but the property is still null.

This task has so many steps and so many of my decision looks kind of strange (I`m still learning and most of them I found here or on other resources) so I don't know where I have done the biggest mistake or what I don't know to resolve the issue by myself.


Solution

  • For NoteHeadControl I created separate ViewModel (NoteHeadControlViewModel ) where my commands and properties should be.

    This is wrong. The UserControl should not have its own view model. It should inherit the DataContext from the parent element.

    What you can do is to add an ICommand dependency property to the NoteHeadControl and bind this one to a command of the parent view model that should be responsible for deleting the item:

    <UserControls:NoteHeadControl DeleteCommand="{Binding DataContext.DeleteNoteCommand, 
        RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}" />
    

    In the control, you then bind the Command property of the Button to the dependency property:

    <Button Command="{Binding DeleteCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" 
            Content="Delete"/>
    

    But don't break the DataContext inheritance by explicitly setting the DataContext of the control like this:

    NoteHeadControlViewModel noteHeadControlViewModel = new NoteHeadControlViewModel((int)this.OpenButton.Tag);
    DataContext = noteHeadControlViewModel;