Refresh View databindings using UpdateLayout or so

2019-07-22 06:19发布

问题:

I have an interface IScreenViewModel, and to simplify the problem I can

RedScreenViewModel : IScreenViewModel
GreenScreenViewModel : IScreenViewModel

etc.. This means I have a RedScreenView.xaml which makes a RedScreenViewModel instance, likewise for all subsequent color screens.

IScreenViewModel has some properties that you must implement e.g. 
interface IScreenViewModel
{
   public Color ScreenColor{get;set;}
}

I have a ViewmodelWrapper class which holds all viewmodels instances. ScreenViewModels, MenuViewModels etc... Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet. So in main for example. I can't have

ScreenLabel.DataContext  = viewModelWrapper.ScreenViewModel

I would have to do in main:

DataContext  = viewModelWrapper;

This way the parent Window can see all child elements.

In a RedScreenView.xaml I can have something like this:

<Label Background="ScreenViewModel.ScreenColor"/>

And hopefully the data binding should look in the ViewModelWrapper find the IScreenViewModel.ScreenViewModel object and use the correct ScreenColor object using dynamic binding/polymorphism.

There are cases where a screen can have more properties, so let say in GreenScreenViewModel along with ScreenColor property inherited, it can have its own property maybe DifferentProperty.

The problem is: I have a Factory that returns a screen object depending on what screen the user wants. It returns the correct screen object, but when it notifies the View to update itself it looks at the new object but using the wrong XAML. If that makes any sense. I do something like this in a ViewModelWrapper method.

MainGui.ScreenWrapper.LayoutRoot.Clear() ;
MainGui.ScreenWrapper.Items.Clear() ;
MainGui.ScreenWrapper.LayoutRoot.Add(screenFactory.GetSelectedScreen("RedScreen").GetLayoutRoot()
    MainGui.UpdateLayout() ;
    ScreenViewModel = screenFactory.GetSelectedScreen("RedScreen").GetViewModel() ;

Ignore the fact that I called factory twice... ScreenWrapper is LayoutGroup that holds the screens. When I swap the Views (screens) using that code I am hoping that it would use the correct bindings. So let's say I swap from GreenScreenViewModel to RedScreenViewModel, remember GreenScreenViewModel one more property than RedScreenViewModel and in the GreenScreenView I had something like this:

<Label Content="ScreenViewModel.DifferentProperty"/> 

When the swap is done and ScreenViewModel notifies that is now pointing to RedScreenViewModel it throws an exception. I am strongly assuming this is because Layout isn't being refreshed and it is still using the wrong view. The output error in debug mode is "Cannot find property DifferentProperty in viewModelWrapper.ScreenModel" Which isn't right because I have already deleted THAT GreenScreenView, I updated the layout, I know there is a LayoutChanged event or something like that, so that could have been raised as well so why is it still seeing the wrong View? How can I update ScreenWrapper.LayoutRoot to "see" the new View with a different binding code. Heavens, I hope that was clear. EDIT: At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. I am sure I didn't make myself clear but maybe the question should be: When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem. UpdateLayout() doesn't work

EDIT: At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. It throws the exception when I call OnPropertyChanged, and yeah the handler isn't null! I am sure I didn't make myself clear but maybe the question should be: When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem. UpdateLayout() doesn't work.

回答1:

Firstly, you say

Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet.

Express should not be the problem here. You need something to bind to that will return the appropriate ViewModel. Have a look here for discussion on this subject.

Secondly, you say the error is

Cannot find property DifferentProperty in viewModelWrapper.ScreenModel

This is not necessarily a problem, and does not cause an exception. When you change bindings dynamically, INotifyPropertyChanged events fly off all over the place and there may be a period of 'uncertainty'. I am assuming your ViewModels implement INotifyPropertyChanged.

I think the key is probably looking closer at the exception, if there is one (because "Cannot find property" is a debug message, not an exception). To get some clarity you might want to turn off the binding message as described here. If there is an actual exception, please edit with the details.