简体   繁体   中英

How to navigate in WPF using PRISM and bind DataContext

There must be a lot of questions surrounding this area but I couldn't find anything to help in my instance.

The problem I'm experiencing is getting my ViewModel, and specifically a property within ViewModel, to be updated to my View. Below is my implementation. I think I understand where I'm going wrong but not sure how to resolve it.

I have a Module that has a list and edit view. Quite simply lists domain objects and then ability to edit a domain object.

My xaml binds the DataContent to a ViewModel property in my View.

I then use the INavigationAware.NavigateTo method to navigate to my ViewModel and this is where I load the domain object.

The problem is that obviously this is not reflected back to the View. The view already has an instance of the ViewModel. This method worked fine when the ViewModel was using a list of objects using ObservableCollection. However, this did not work when using a simple object or even an ObservableObject.

Could someone please help my understanding or point me to some links with a better implementation of what I am trying to achieve?

MyModule

public class MyModule : IModule
{
    private readonly IRegionManager _regionManager;

    public MyModule(IRegionManager regionManager)
    {
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        _regionManager.RegisterViewWithRegion(Constants.MainRegionName, typeof(MyListView));
        _regionManager.RegisterViewWithRegion(Constants.MainRegionName, typeof(MyEditView));
    }
}

XAML

<UserControl
    DataContext="ViewModel">
    ...
    <TextBlock Text="{Binding Path=MyDomainObject.AProperty}" />
    ...

View

public partial class MyEditView
{
    public readonly static string ViewName = "MyEditView";

    public MyEditView(MyEditViewModel viewModel)
    {
        InitializeComponent();
        ViewModel = viewModel;
    }

    public MyEditViewModel ViewModel
    {
        get { return DataContext as MyEditViewModel; }
        private set { DataContext = value; }
    }
}

ViewModel

public class MyViewModel : INavigationAware
{
    private readonly IRegionManager _regionManager;

    public MyDomainObject MyDomainObject { get; set; }

    public void Load(ViewModelKey key)
    {
        // get domain object
        // this method worked when MyDomainObject was 
        // ObservableCollection<T> as just adding elements to list
        // where this is creating a new instance of MyDomainObject
        var id = parameter from navigationContext;
        MyDomainObejct = server.GetDomainObject(id);
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        var key = key from navigationContext;
        Load(key);
    }
}

SOLUTION

public class MyEditViewModel : INavigationAware
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private MyDomainObject _myDomainObject;
    public MyDomainObject MyDomainObject
    {
        get
        {
            return _myDomainObject;
        }
        set
        {
            if (value != _myDomainObject)
            {
                _myDomainObject = value;
                NotifyPropertyChanged();
            }
        }
    }

View

public partial class MyEditView
{
    public MyEditView(MyEditViewModel viewModel)
    {
        InitializeComponent();
        ViewModel = viewModel;

        ViewModel.PropertyChanged += ViewModel_PropertyChanged;
    }

    public MyEditViewModel ViewModel
    {
        get { return DataContext as MyEditViewModel; }
        private set { DataContext = value; }
    }

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (!(sender is MyEditViewModel))
            return;

        ViewModel = (MyEditViewModel)sender;
    }
}

For your binding to update you need to implement INotifyPropertyChanged and raise PropertyChanged Event on the set accessor of your domain object.

public event PropertyChangedEventHandler PropertyChanged = delegate {};


public MyDomainObject MyDomainObject
{
    get
    {
        return myDomainObject;
    }
    set
    {
        if(value != myDomainObject)
        {
            myDomainObject = value;
            RaisePropertyChanged("MyDomainObject");
        }
    }
}

private void RaisePropertyChanged(String p)
{
    PropertyChanged(this, new PropertyChangedEventArgs(p));
}

Or as in the Prism book, inherit NotificationObject and call RaisePropertyChanged(()=> PropertyName) which is refactoring-safe

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM