Change Tabs from ViewModel

Apr 9, 2013 at 7:01 PM
I have been playing with mui and I really like it. There is one thing I have come across that is tripping me up. In the app, I have several pages one of which contains a ModernTab with three links. I have it working so I can navigate from my view model by setting the content property on the ModernFrame that hosts the views. The view in question is Promotion.xaml and it contains the ModernTab control and hosts 3 links, PromotionPrep.xaml, PromotionMonitor.xaml, and PromotionSummary.xaml. From another view model, I can navigate to Promotion.xaml and everything works great, the default tab of PromotionPrep.xaml is selected just as I want it to me. There are cases when I do not want have PromotionPrep.xaml to be the SelectedSource. Sometimes i want it to be PromotionMonitor.xaml instead. In this case I set the Source property of the ModernFrame to PromotionMonitor.xaml. That view is displayed, but not as the tab being selected in the Promotion.xaml file like I want it to, instead the PromotionMonitor.xaml view is shown all by itself, essentially taking the place of Promotion.xaml. How can I specify what tab is shown from a view model?

Thanks,

Jay
Coordinator
Apr 9, 2013 at 10:38 PM
You should not set the ModernFrame.Content directly. Set the Source instead and let the ContentLoader work its magic to set the Content. This may be the cause of your issue? If not, could you share some code?
Apr 15, 2013 at 8:44 PM
Sorry it took so long to respond, things have been a bit crazy around here. I put together a quick sample of the kind of thing I was trying to do and put it on SkyDrive. I want to change the selected tab in various view models, but in the sample it is done in the constructor for demonstration purposes. In the real project there are view models behind each tab in the samples views and they try to set the current source at different points in the program. If you want a more elaborate example just let me know and I will work on it.

In this sample, the view model tries to set the source to be the second tab with limited success. The Sample1.xaml file is displayed, but it looses context as it does not display any tabs nor does it know it should have selected Sample in the main navigation. If you comment out the code in the constructor in SampleVM you can see what it should be looking like with the exception that I wanted the second tab to be selected in this case.

Thanks,

jay
Coordinator
Apr 16, 2013 at 12:56 PM
Not sure what the problem is, your sample seem to run just fine.

What I would suggest though, is to not use this static NavigationHelper. Just add a SelectedSource property to your VM in bind it (two-way) to the SelectedSource property of the ModernTab. So your VM looks like this:
public class SampleVM : NotifyPropertyChanged
{
  private Uri selectedSource = new Uri("/Content/Sample0.xaml", UriKind.Relative);

  public Uri SelectedSource
  {
    get { return this.selectedSource; }
    set
    {
      if (this.selectedSource != value) {
        this.selectedSource = value;
        OnPropertyChanged("SelectedSource");
      }
    }
  }
}
And your Sample.xaml looks like this;
<Grid DataContext="{StaticResource VM}" Style="{StaticResource ContentRoot}">
  <mui:ModernTab SelectedSource="{Binding SelectedSource, Mode=TwoWay}" >
    <mui:ModernTab.Links>
      <mui:Link DisplayName="Sample 0" Source="/Content/Sample0.xaml" />
      <mui:Link DisplayName="Sample 1" Source="/Content/Sample1.xaml" />
      <mui:Link DisplayName="Sample 2" Source="/Content/Sample2.xaml" />
    </mui:ModernTab.Links>
  </mui:ModernTab>
</Grid>
Apr 16, 2013 at 3:37 PM
Edited Apr 16, 2013 at 3:40 PM
I actually did this exact thing in the real application in the and it works great for selecting the initial tab. I have an issue changing active tabs from another tabs view model now. I will try give some better code examples this time.

The "Promotion" view- this is the container for 3 tabs that offer different views during a promotion.
<Grid Style="{StaticResource ContentRoot}" DataContext="{StaticResource VM}">
    <mui:ModernTab Layout="Tab" SelectedSource="{Binding CurrentTabSource}">
        <mui:ModernTab.Links>
            <mui:Link DisplayName="Non-CherryPicked Info" Source="/Views/Promotion/NonCherryPickedView.xaml" />
            <mui:Link DisplayName="Progress Monitor" Source="/Views/Promotion/MonitorProgressView.xaml" />
            <mui:Link DisplayName="Summary" Source="/Views/Promotion/SummaryView.xaml" />
        </mui:ModernTab.Links>
    </mui:ModernTab>
</Grid>
And here is the view model for the Promotion view where I choose which tab should be selected by default based on the promotion objects state, (I don't like how this is done right now either, but that will be cleaned up in the next round of changes.)
        const String NON_CHERRY_PICKED_VIEW_PATH = "/Views/Promotion/NonCherryPickedView.xaml";
        const String MONITOR_PROGRESS_VIEW_PATH = "/Views/Promotion/MonitorProgressView.xaml";
        const String SUMMARY_VIEW_PATH = "/Views/Promotion/SummaryView.xaml";

        private String _currentTabSource;
        public String CurrentTabSource
        {
            get { return _currentTabSource; }
            set
            {
                if (_currentTabSource == value) return;
                _currentTabSource = value;
                PropertyHasChanged();
            }
        }

        public PromotionVM()
        {
            var promotion = ObjectRepository.GetInstance<Promotion>();
            if (promotion.TraditionalBranches.Count == 0 && promotion.NonPromotables.Count == 0 && !promotion.IsPromotionComplete)
                this.CurrentTabSource = MONITOR_PROGRESS_VIEW_PATH;
            else if (promotion.IsPromotionComplete)
                this.CurrentTabSource = SUMMARY_VIEW_PATH;
            else
                this.CurrentTabSource = NON_CHERRY_PICKED_VIEW_PATH;
        }
That works great. You can see in the screenshot that the tab that I wanted was selected in the view.
Screenshot

So lets just say we go to the MonitorProgressView and it does its thing. In the MonitorProgressViewModel I want to change the SelectedSource on the Promotion view to SummaryView once the process that the MonitorProgressView is monitoring is complete. Here is some of the code from the MonitorProgressViewModel
private void OnPromotionComplete()
{
    if (this.Promotion.IsPromotionComplete)
    {
        this.ProgressMessage = "The promotion is complete, you can do your happy dance now.";
                
                // Here is where I can't figure out how to change the SelectedSource back in the Promotion view.
        NavigationHelper.NavigateTo("/Views/Promotion/SummaryView.xaml");
    }
    else if (this.Promotion.CurrentConflicts.Count == 0)
    {
        // Do some other stuff here
    }

}
The result of this is we get the proper view to show, but it looses the tabs. Screenshot I am assuming this is because I didn't set the SelectedSource property, but I am not sure how to get at that being this is in a view model of on of the specific tabs in the view.

Thanks again,

Jay
Apr 16, 2013 at 4:04 PM
Edited Apr 16, 2013 at 4:04 PM
This doesn't really have anything do to with mui, but an approach to do this is to send a message from MonitorProgressViewModel to the PromotionVM. Are you using an MVVM library/toolkit/framework? Most of them do provide messaging capabilities so that your VMs can talk in a de-coupled manner.

I use MVVMLight and I do this (sending messages between VMs) all of the time.
Apr 16, 2013 at 5:59 PM
I am not using any framework at all at the moment. I will take a look at MVVMLight. Thanks for the recommendation.

Thanks,

Jay
Coordinator
Apr 17, 2013 at 12:16 PM
Edited Apr 17, 2013 at 12:19 PM
There are two ModernFrame instances in place when using a tab. The root ModernFrame hosts the PromotionView, which in turn contains a ModernTab having a ModernFrame hosting the ProgressMonitor and Summary content. The problem is that your NavigationHelper is tight to the root ModernFrame. When invoking NavigateTo you load the summary in the root ModernFrame instead of in the ModernFrame instance of the ModernTab.

My suggestion is to get rid of this NavigationHelper and use the PromotionViewModel.SelectedSource binding. When you need to initiate a navigation from within a child view model, you can either use messaging (as flyte is suggesting) or make sure you have a reference to the parent PromotionViewModel and set the SelectedSource property.

Makes sense?
Apr 17, 2013 at 4:38 PM
That makes perfect sense. I am looking into adding messaging to my app to support this functionality. Thanks for all the assistance. I really love MUI and what you have created.

Thanks,

Jay
Dec 28, 2013 at 6:14 AM
hello sir..
can u please mail or share your this tabchange application on this forum...cz i need this type of app or any example related to this same i.e. tabchange..
kindly do a reply...thank u..