Reputation: 103
I am having an issue updating the interface of my windows phone 7 app I am working on.
I have a MainPage.xaml
and its MainViewModel.cs
which handle how a child view usercontrol ChildView1.xaml
appears in the MainPage. ChildView1 also has its ViewModel which is Child1ViewModel.cs
. Basically a myToggleButton
gets fired from the MainPage which will trigger a DataStateBehavior
to go into a show or hide state to respectively show or hide the ChildView1.xaml
. That DataStateBehavior is binded to the boolean CanShowView
while the myToggleButton
is binded to an Icommand
named ButtonChecked
which has a relay function SwitchVisibility
that will reverse the CanShowView
boolean to show or hide the view. All works well thanks to the RaisePropertyChanged
of the MVVM light I used.
However, the ChildView1.xaml
has a myCloseButton
which is supposed to call the SwitchVisibility
function from MainViewModel which would in turn revert the CanShowView
and the RaisePropertyChanged
in CanShowView
should fire to have ChildView1
close, but the issue is that the view stays visible and never closes for some reason! I noticed through the debugger that the RaisePropertyChanged
in CanShowView
seems to not fire. I suppose that's where the problem lies. What are my doing wrong? How can I force it to fire? I used multiple RaisePropertyChanged
but none seem to fire even though the degugger shows that the value in my function did change.
Here is my code. MainPage.xaml:
<phone:PhoneApplicationPage
...
>
<phone:PhoneApplicationPage.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}"/>
</phone:PhoneApplicationPage.DataContext>
...
<Grid x:Name="LayoutRoot">
...
<ToggleButton x:Name="myToggleButton" Command="{Binding Main.ButtonChecked, Source={StaticResource Locator}}" >
<local:ChildView1 x:Name="myChildView1" Grid.Row="0" RenderTransformOrigin="0.5,0.5">
<i:Interaction.Behaviors>
<ec:DataStateBehavior Binding="{Binding Main.CanShowView, Source={StaticResource Locator}}" Value="True" TrueState="showChildView1" FalseState="hideChildView1"/>
</i:Interaction.Behaviors>
<local:ChildView1.RenderTransform>
<CompositeTransform/>
</local:ChildView1.RenderTransform>
</local:ChildView1>
</Grid>
</phone:PhoneApplicationPage>
MainViewModel.cs :
public class MainViewModel : ViewModelBase
{
public ICommand ButtonChecked { get; private set; }
public MainViewModel()
{
ButtonChecked = new RelayCommand(() => SwitchVisibility());
}
public void SwitchVisibility()
{
CanShowView = !CanShowView;
RaisePropertyChanged("CanShowView");
}
bool _canShowView = true;
public bool CanShowView
{
get { return _canShowView; }
set
{
if (value != _canShowView)
{
_canShowView = value;
RaisePropertyChanged("CanShowView");
}
}
}
}
ChildView1.xaml :
<UserControl x:Class="myApp.Views.ChildView1"
....
DataContext="{Binding Child1VM, Source={StaticResource Locator}}"
>
<Grid x:Name="HomeGrid">
<Button x:Name=myCloseButton Command="{Binding Child1VM.CloseChildViewCmd, Source={StaticResource Locator}}"/>
...
</Grid>
</UserControl>
Child1ViewModel.cs :
namespace myApp
{
public class Child1ViewModel : ViewModelBase
{
public ICommand CloseChildViewCmd { get; private set; }
public Child1ViewModel()
{
CloseChildViewCmd = new RelayCommand(() => ExecuteCloseChildViewCmd());
}
public void ExecuteCloseChildViewCmd()
{
MainPage mainPage = new MainPage();
MainViewModel mainVM = new MainViewModel();
mainPage.DataContext = mainVM;
mainVM.SwitchVisibility();
}
}
}
Note: I am only a beginner programmer so the way I am tackling the issue or the program itself might be questionable, hence I am open to any suggestions, and code samples are very welcome. I have been stuck with this issue for many days now after doing a lot of research and experimenting many things that never seemed to work. I took the time to write the issue in detail and am really hopeful that I could find a solution here. Let me know if ever there are more details you need to know. Thanks in advance.
EDIT: I read creating a new instance of the views is not a solution. How else can I access fire the MainView
's functions and have properties raised?
EDIT: As suggested by OmegaMan, I am using static definitions to call my functions.
So the updated MainViewModel.cs
:
public class MainViewModel : ViewModelBase
{
public ICommand ButtonChecked { get; private set; }
private static MainViewModel Primary { get; set; }
public MainViewModel()
{
ButtonChecked = new RelayCommand(() => SwitchVisibility());
Primary = this;
}
public void SwitchVisibility()
{
CanShowView = !CanShowView;
RaisePropertyChanged("CanShowView");
}
public static void SwitchViewStatic()
{
Primary.SwitchVisibility();
}
bool _canShowView = true;
public bool CanShowView
{
get { return _canShowView; }
set
{
if (value != _canShowView)
{
_canShowView = value;
RaisePropertyChanged("CanShowView");
}
}
}
}
The updated Child1ViewModel.cs
:
namespace myApp
{
public class Child1ViewModel : ViewModelBase
{
public ICommand CloseChildViewCmd { get; private set; }
public Child1ViewModel()
{
CloseChildViewCmd = new RelayCommand(() => ExecuteCloseChildViewCmd());
}
public void ExecuteCloseChildViewCmd()
{
MainViewModel.SwitchViewStatic();
}
}
}
The desired function in MainViewModel gets called by myCloseButton from the child view, but CanShowView
still won't get raised to permit the DataStateBehavior listening to it, to update the UI... So I am still at an impass.
Upvotes: 2
Views: 1128
Reputation: 31606
There is nothing wrong with having the children hold a reference to the main view model, but why is the control creating a new MainViewModel? Pass in the MainViewModel and call SwitchVisibility off it...or make a static method on the MainVM which will access the actuall VM and call instance SwitchVisiblity.
--- Update to show the static method access from the MainVM
public class MainViewModel : ViewModelBase
{
private static MainViewModel Primary { get; set; }
public MainViewModel()
{
Primary = this;
}
public static void SwitchViewStatic()
{
Primary.SwitchVisibility();
}
public void SwitchVisibility()
{
...
}
}
Upvotes: 1
Reputation: 2861
The problem is that you're sending a message to a new MainPage object and view model, not the existing one.
What you need is either a delegate or an event. The trick is connecting the Main Page to the Child Page.
If you need to keep them separate, you need an object to send the event along. This called an Event Aggregator, or messenger. You main viewmodel would need to subscribe to a CloseChild event that the child view model would send when the command is executed. The event aggregator transfers the message.
Note: Most MVVM frameworks implement a messenger of some kind that your view models can use to send and receive messages / events.
If you want to implement one yourself, take a look here for a way to create one.
Upvotes: 1