ContentControl Content Property not changing with hosted content
up vote
4
down vote
favorite
I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:
In the main window where this drawer is, I have a ContentControl
where I set its content with a Binding.
<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>
This window's binding is set to a view model.
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
and here is the ViewModel:
MainWindowViewModel.cs
public class MainWindowViewModel: ViewModelBase
{
private object _content;
public object WindowContent
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(nameof(WindowContent));
}
}
public ICommand SetWindowContent { get; set; }
public MainWindowViewModel()
{
SetWindowContent = new ChangeWindowContentCommand(this);
}
}
So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:
RecoveryOperationsView.xaml
In "RecoveryOperationsView.xaml" (which is a UserControl
) I also reference the view model from above like so..
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
and have a button to call the command to change the Content property of the ContentControl
from the main window..
<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >
In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so
ChangeWindowContentCommand.cs
public class ChangeWindowContentCommand : ICommand
{
private MainWindowViewModel viewModel;
public ChangeWindowContentCommand(MainWindowViewModel vm)
{
this.viewModel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
switch (parameter)
{
case "Home":
viewModel.WindowContent = new HomeView();
break;
case "RecoveryOps":
viewModel.WindowContent = new RecoveryOperationsView();
break;
case "DatabaseRecovery":
viewModel.WindowContent = new DatabaseRestoreView();
break;
}
}
}
However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl
Content property doesnt change to the new UserControl
I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl
nothing changes. What am I missing?
c# wpf mvvm material-design-in-xaml
add a comment |
up vote
4
down vote
favorite
I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:
In the main window where this drawer is, I have a ContentControl
where I set its content with a Binding.
<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>
This window's binding is set to a view model.
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
and here is the ViewModel:
MainWindowViewModel.cs
public class MainWindowViewModel: ViewModelBase
{
private object _content;
public object WindowContent
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(nameof(WindowContent));
}
}
public ICommand SetWindowContent { get; set; }
public MainWindowViewModel()
{
SetWindowContent = new ChangeWindowContentCommand(this);
}
}
So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:
RecoveryOperationsView.xaml
In "RecoveryOperationsView.xaml" (which is a UserControl
) I also reference the view model from above like so..
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
and have a button to call the command to change the Content property of the ContentControl
from the main window..
<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >
In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so
ChangeWindowContentCommand.cs
public class ChangeWindowContentCommand : ICommand
{
private MainWindowViewModel viewModel;
public ChangeWindowContentCommand(MainWindowViewModel vm)
{
this.viewModel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
switch (parameter)
{
case "Home":
viewModel.WindowContent = new HomeView();
break;
case "RecoveryOps":
viewModel.WindowContent = new RecoveryOperationsView();
break;
case "DatabaseRecovery":
viewModel.WindowContent = new DatabaseRestoreView();
break;
}
}
}
However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl
Content property doesnt change to the new UserControl
I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl
nothing changes. What am I missing?
c# wpf mvvm material-design-in-xaml
1
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21
add a comment |
up vote
4
down vote
favorite
up vote
4
down vote
favorite
I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:
In the main window where this drawer is, I have a ContentControl
where I set its content with a Binding.
<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>
This window's binding is set to a view model.
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
and here is the ViewModel:
MainWindowViewModel.cs
public class MainWindowViewModel: ViewModelBase
{
private object _content;
public object WindowContent
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(nameof(WindowContent));
}
}
public ICommand SetWindowContent { get; set; }
public MainWindowViewModel()
{
SetWindowContent = new ChangeWindowContentCommand(this);
}
}
So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:
RecoveryOperationsView.xaml
In "RecoveryOperationsView.xaml" (which is a UserControl
) I also reference the view model from above like so..
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
and have a button to call the command to change the Content property of the ContentControl
from the main window..
<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >
In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so
ChangeWindowContentCommand.cs
public class ChangeWindowContentCommand : ICommand
{
private MainWindowViewModel viewModel;
public ChangeWindowContentCommand(MainWindowViewModel vm)
{
this.viewModel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
switch (parameter)
{
case "Home":
viewModel.WindowContent = new HomeView();
break;
case "RecoveryOps":
viewModel.WindowContent = new RecoveryOperationsView();
break;
case "DatabaseRecovery":
viewModel.WindowContent = new DatabaseRestoreView();
break;
}
}
}
However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl
Content property doesnt change to the new UserControl
I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl
nothing changes. What am I missing?
c# wpf mvvm material-design-in-xaml
I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:
In the main window where this drawer is, I have a ContentControl
where I set its content with a Binding.
<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>
This window's binding is set to a view model.
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
and here is the ViewModel:
MainWindowViewModel.cs
public class MainWindowViewModel: ViewModelBase
{
private object _content;
public object WindowContent
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(nameof(WindowContent));
}
}
public ICommand SetWindowContent { get; set; }
public MainWindowViewModel()
{
SetWindowContent = new ChangeWindowContentCommand(this);
}
}
So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:
RecoveryOperationsView.xaml
In "RecoveryOperationsView.xaml" (which is a UserControl
) I also reference the view model from above like so..
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
and have a button to call the command to change the Content property of the ContentControl
from the main window..
<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >
In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so
ChangeWindowContentCommand.cs
public class ChangeWindowContentCommand : ICommand
{
private MainWindowViewModel viewModel;
public ChangeWindowContentCommand(MainWindowViewModel vm)
{
this.viewModel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
switch (parameter)
{
case "Home":
viewModel.WindowContent = new HomeView();
break;
case "RecoveryOps":
viewModel.WindowContent = new RecoveryOperationsView();
break;
case "DatabaseRecovery":
viewModel.WindowContent = new DatabaseRestoreView();
break;
}
}
}
However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl
Content property doesnt change to the new UserControl
I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl
nothing changes. What am I missing?
c# wpf mvvm material-design-in-xaml
c# wpf mvvm material-design-in-xaml
edited Nov 20 at 0:19
asked Nov 19 at 23:00
tastydew
504514
504514
1
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21
add a comment |
1
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21
1
1
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21
add a comment |
1 Answer
1
active
oldest
votes
up vote
3
down vote
accepted
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl
and your MainWindow
use different instances of the MainWindowViewModel
. You do not need to instantiate the VM for the user control, as it will inherit the DataContext
from the MainWindow
. The way it works in WPF is that if any given UIElement
does not have theDataContext
assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand
and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl
, or if you want to use a command, you can have a single ContentControl
bound to a property of MainWindowViewModel
that is of type ViewModelBase
. Let's call it CurrentViewModel
. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel
property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand
to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed propertypublic ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting theCurrentViewModel
changes the context for the view?
– tastydew
Nov 20 at 21:09
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from theContentControl
due to the binding. ThisDataContext
will now beRecoveryOperationsViewModel
rather thanMainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to useRelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.
– Nik
Nov 20 at 21:18
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally useRelativeSource
binding (among other techniques).
– Nik
Nov 20 at 21:21
|
show 3 more comments
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
3
down vote
accepted
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl
and your MainWindow
use different instances of the MainWindowViewModel
. You do not need to instantiate the VM for the user control, as it will inherit the DataContext
from the MainWindow
. The way it works in WPF is that if any given UIElement
does not have theDataContext
assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand
and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl
, or if you want to use a command, you can have a single ContentControl
bound to a property of MainWindowViewModel
that is of type ViewModelBase
. Let's call it CurrentViewModel
. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel
property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand
to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed propertypublic ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting theCurrentViewModel
changes the context for the view?
– tastydew
Nov 20 at 21:09
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from theContentControl
due to the binding. ThisDataContext
will now beRecoveryOperationsViewModel
rather thanMainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to useRelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.
– Nik
Nov 20 at 21:18
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally useRelativeSource
binding (among other techniques).
– Nik
Nov 20 at 21:21
|
show 3 more comments
up vote
3
down vote
accepted
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl
and your MainWindow
use different instances of the MainWindowViewModel
. You do not need to instantiate the VM for the user control, as it will inherit the DataContext
from the MainWindow
. The way it works in WPF is that if any given UIElement
does not have theDataContext
assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand
and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl
, or if you want to use a command, you can have a single ContentControl
bound to a property of MainWindowViewModel
that is of type ViewModelBase
. Let's call it CurrentViewModel
. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel
property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand
to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed propertypublic ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting theCurrentViewModel
changes the context for the view?
– tastydew
Nov 20 at 21:09
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from theContentControl
due to the binding. ThisDataContext
will now beRecoveryOperationsViewModel
rather thanMainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to useRelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.
– Nik
Nov 20 at 21:18
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally useRelativeSource
binding (among other techniques).
– Nik
Nov 20 at 21:21
|
show 3 more comments
up vote
3
down vote
accepted
up vote
3
down vote
accepted
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl
and your MainWindow
use different instances of the MainWindowViewModel
. You do not need to instantiate the VM for the user control, as it will inherit the DataContext
from the MainWindow
. The way it works in WPF is that if any given UIElement
does not have theDataContext
assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand
and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl
, or if you want to use a command, you can have a single ContentControl
bound to a property of MainWindowViewModel
that is of type ViewModelBase
. Let's call it CurrentViewModel
. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel
property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand
to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl
and your MainWindow
use different instances of the MainWindowViewModel
. You do not need to instantiate the VM for the user control, as it will inherit the DataContext
from the MainWindow
. The way it works in WPF is that if any given UIElement
does not have theDataContext
assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand
and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl
, or if you want to use a command, you can have a single ContentControl
bound to a property of MainWindowViewModel
that is of type ViewModelBase
. Let's call it CurrentViewModel
. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel
property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand
to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
edited Nov 20 at 15:09
answered Nov 20 at 3:36
Nik
1,2571619
1,2571619
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed propertypublic ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting theCurrentViewModel
changes the context for the view?
– tastydew
Nov 20 at 21:09
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from theContentControl
due to the binding. ThisDataContext
will now beRecoveryOperationsViewModel
rather thanMainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to useRelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.
– Nik
Nov 20 at 21:18
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally useRelativeSource
binding (among other techniques).
– Nik
Nov 20 at 21:21
|
show 3 more comments
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed propertypublic ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting theCurrentViewModel
changes the context for the view?
– tastydew
Nov 20 at 21:09
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from theContentControl
due to the binding. ThisDataContext
will now beRecoveryOperationsViewModel
rather thanMainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to useRelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.
– Nik
Nov 20 at 21:18
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally useRelativeSource
binding (among other techniques).
– Nik
Nov 20 at 21:21
1
1
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
This was absolutely the issue! I really appreciate the help and the excellent explanation of de-coupling my viewmodels from my views! My approach was much like the one you suggested of the two where I create a ViewModel for every user control. I guess its the implementation of switching between them that i need to work on. Thanks again!
– tastydew
Nov 20 at 4:02
1
1
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
My pleasure. Glad it helped. It looks like you're almost there, just a small change in concept. You should use the implicit data templates as above and move the switching code into your MainViewModel (rather than ICommand class), while using the generic implementation of RelayCommand above. That will perfectly adhere to the MVVM pattern!
– Nik
Nov 20 at 5:03
So I implemented the DataTemplate method you provided using an exposed property
public ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting the CurrentViewModel
changes the context for the view?– tastydew
Nov 20 at 21:09
So I implemented the DataTemplate method you provided using an exposed property
public ViewModelBase CurrentViewModel
to change the current view, however, when I try to click a button in one of the views that appear in the UserControl, it appears as though the inherited DataContext is lost up the VisualTree.It looks like the output console logs the following: ` BindingExpression path error: 'ChangeCurrentViewModel' property not found on 'object' ''RecoveryOperationsViewModel' (HashCode=58033516)'. Is this because setting the CurrentViewModel
changes the context for the view?– tastydew
Nov 20 at 21:09
1
1
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from the
ContentControl
due to the binding. This DataContext
will now be RecoveryOperationsViewModel
rather than MainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to use RelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.– Nik
Nov 20 at 21:18
So the way that will work, is the user controls will no longer inherit the data context from the window, but rather from the
ContentControl
due to the binding. This DataContext
will now be RecoveryOperationsViewModel
rather than MainWindowViewModel
, where the command is defined. Hence the binding error. You should be able to use RelativeSource
binding to fire the command. Or move the command to the child view model, and have the child communicate with parent VM. Not sure how much this makes sense, but I can flesh this out in my answer for you after work today.– Nik
Nov 20 at 21:18
1
1
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally use
RelativeSource
binding (among other techniques).– Nik
Nov 20 at 21:21
One simple concept that helped me A LOT when I was first learning WPF is: Everything is a tree! And everything inherits from up the logic tree. This applies to styles, data templates, and any other dependency property. To skip some levels in the logical tree (i.e. to select the correct datacontext), you would generally use
RelativeSource
binding (among other techniques).– Nik
Nov 20 at 21:21
|
show 3 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53383868%2fcontentcontrol-content-property-not-changing-with-hosted-content%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
"I can see the property being changed but the actual window content doesn't". Changed to what? Are you perhaps by accident creating a duplicate MainWindowViewModel somewhere? "but if I try to use anything in the hosted content of the ContentControl, nothing works" What is that "anything"? I have no idea what you mean with this sentence. Please edit and improve your question to clarify this.
– elgonzo
Nov 19 at 23:16
@elgonzo I've updated my question with as much clarification I could get. I don't think I am making multiple references to the MainWindowViewModel anywhere unless adding the datacontext in XAML does exactly that... In the breakpoint, when I click the "Restore Database" button, I can see the WindowContent property changing to an instance of DatabaseRestoreView.
– tastydew
Nov 20 at 0:21