Marcel Grüger
Marcel Grüger

Reputation: 934

Bindingerror in XAML and MVVM in UWP (with C#)

I have a problem with correctly implementing a data binding with MVVM and a ComboBox. I have a simple Model:

public class RuleSet
{
    public List<RuleMainSystem> RuleMainSystems { get; set; }

    public RuleSet()
    {
        RuleMainSystems = new List<RuleMainSystem>();
    }
}

And therein it looks like this:

public class RuleMainSystem
{
    public string Name { get; set; }
    public List<RuleSubSystem> RuleSubSystems { get; set; }

    public RuleMainSystem(string name)
    {
        Name = name;
        RuleSubSystems = new List<RuleSubSystem>();
    }
}

I want now a simple binding from a page where a ComboBox is part of and the ItemSource of this ComboBox to be set to this RuleSet. I tried to bind directly to the ViewModel, but this one is deleted from the RAM as soon as I navigate to another site and I want to avoid as much static's as possible, so I created an instance of this ViewModel on the first site and give it to the second page as a parameter. That works well so far, but now I would have to bind the ComboBox to this._editRulesViewModel.RuleSet.RuleMainSystems and that's where I don't get the right code. Maybe I approach this from the wrong side? A suggestion how I could access the data from one site to another without giving the whole instances from A to B to C and so on?
This doesn't work:
View:

public sealed partial class EditRules
{
    private EditRulesViewModel _editRulesViewModel;

    public EditRules()
    {
        DataContext = this;
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        _editRulesViewModel = (EditRulesViewModel)e.Parameter;
    }
}

And in the XAML:

<ComboBox x:Name="MainSystemComboBox" DisplayMemberPath="Name" ItemsSource="_editRulesViewModel.RuleSet.RuleMainSystems" />

I mean, yes, the data arrives at the second page, but the Binding is wrong. Oh and I also tried to set the DataContext in the XAML Page definition:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

Upvotes: 0

Views: 40

Answers (2)

Martin Zikmund
Martin Zikmund

Reputation: 39072

First, {Binding} does not work with private fields. Instead, you must expose them as public properties. The newer {x:Bind} extension does work with public fields, but still not with private.

Next - you need to use {Binding} syntax in all places that do data-binding - so for ItemsSource as well, it is not enough to write the variable name directly.

Finally, to make it all working, you will also need to implement INotifyPropertyChanged on the page, because during OnNavigatedTo the data-binding has already run, so it will not bind the newly assigned value otherwise.

public sealed partial class EditRules : INotifyPropertyChanged
{ 
    public EditRulesViewModel EditRulesViewModel {get; private set;}

    public EditRules()
    {
        DataContext = this;
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        EditRulesViewModel = (EditRulesViewModel)e.Parameter;
        OnPropertyChanged(nameof(this.EditRulesViewModel));
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Now in XAML do:

<ComboBox x:Name="MainSystemComboBox" DisplayMemberPath="Name"
    ItemsSource="{Binding EditRulesViewModel.RuleSet.RuleMainSystems}" />

Upvotes: 1

thezapper
thezapper

Reputation: 496

you could try to implement the INotifyPropertyChanged-Event.

Or add Elements to a list like so:

public sealed partial class EditRules
{
private List<object> _editRulesViewModel = new List<object>();

public EditRules()
{
    DataContext = this;
    InitializeComponent();
}

//Add nullchecking, etc.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    _editRulesViewModel.Clear();
    foreach (item in ((EditRulesViewModel)e.Parameter)RuleSet.RuleMainSystems)
    {        
        _editRulesViewModel.Add(item);
    }
}
}

<ComboBox x:Name="MainSystemComboBox" DisplayMemberPath="Name" ItemsSource="_editRulesViewModel" />

Upvotes: 1

Related Questions