Hello. I am implementing an app based on the MVVM pattern. One of my controls has a dockmanager with a tabgrouppane. I need to be able to change the active tab based on a property in my viewmodel. Here's what the xaml looks like:
ObjectDataProvider def:
<Window.Resources> <ResourceDictionary>
<ObjectDataProvider x:Key="odpWizard" ObjectType="{x:Type ViewModel:WizardViewModel}" IsAsynchronous="True" />...
TabGroupPane def:
<igDock:TabGroupPane x:Name="tabGroup1" SelectionChanged="tabGroup1_SelectionChanged" DataContext="{Binding Source={StaticResource odpWizard}}" SelectedIndex="{Binding ActiveTab}">...
ViewModel def;
private int _activeTab;
public int ActiveTab { get { return _activeTab; } set { _activeTab = value; OnPropertyChanged("ActiveTab"); } }
What I'm missing is how to change the active tab from the ViewModel. Thanks for the help.
If you are binding the SelectedIndex to your ActiveTab property then in theory you can just set your ActiveTab property and the SelectedIndex should be updated. That being said, you may not want to go this route. When an end user drags a pane, unpins a pane, floats a pane, etc. that pane will no longer be within that TabGroupPane. It may go back there depending on the interaction - e.g. when it is pinned it will be back in the container from which it was unpinned but if it is dragged out then it will be within whatever parent they dragged the pane into.
If you want to bring an element into view then you can use the BringIntoView method of an element within the pane you want to bring into view. If it happens to be within a TabGroupPane then it will be made the selected item of that tabgroup. Note active and selected are not necessarily the same thing since you can have multiple tabgroups each with a selected item but only 1 or even none may be active. The ActivePane is the one that has keyboard focus so if you want to make something the active pane then you would call Focus on it (note it would need to be brought into view in order for the WPF framework to allow you to give it focus so you might need to call BringIntoView, UpdateLayout and then Focus).
Thanks for the response, Andrew. The way the tabs are within my DockManager, I am not allowing for removal of the tabs. That said, how do I set the binding between the viewmodel and the tabgrouppane on the view? Just setting the ActiveTab property from my viewmodel does not seem to do the trick. My tabgrouppane's selectedindex property is bound to my ActiveTab property. Is that correct?
Andrew,
You asked: "I'm assuming that the DataContext of the tabgroup (directly or because it is inherited from an ancestor) is your viewmodel?"
The answer is yes. I've tested that I can set the active tab via the SelectedIndex property in the XAML of the tabgrouppane definition as well as from another user control. What I'm still having trouble with is setting the ActiveTab from the viewmodel. Here's my viewmodel code:
public class WizardViewModel : INotifyPropertyChanged { #region Private Members private int _activeTab; private ICommand _setActiveTabCommand; #endregion #region Public Properties public int ActiveTab { get { return _activeTab; } set { _activeTab = value; OnPropertyChanged("ActiveTab"); } } public ICommand SetActiveTabCommand { get { return _setActiveTabCommand; } } #endregion #region Ctor public WizardViewModel(int selectedTab) { // Wire up command _setActiveTabCommand = new RelayCommand { CanExecuteDelegate = x => true, ExecuteDelegate = x => SetActiveTab(selectedTab) }; } #endregion #region Private Members public void SetActiveTab(int tab) { ActiveTab = tab; } #endregion #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion // INotifyPropertyChanged Members
So the SetActiveTab method doesn't result in the selection changing? Again it could be that if focus is within the previously selected tab then we would keep that as the selected item. I'm not sure what you mean by changing it from another user control but if that means that focus was elsewhere then that could be why it worked. I would probably need to see a sample to actually understand what you are trying and why it is failing. One thing that I can say is that the relaycommand seems incorrect. It looks like your SetActiveTabCommand is always going to use whatever was the selectedTab integer passed into the constructor which I'm assuming is not what you want. I might have thought that you would use the CommandParameter as the index that you want to activate unless you're exposing one command per tab/pane.
Thanks again for the quick response. Allow me to briefly describe what I'm trying to do. I have a tabgrouppane in a user control with 6 tabs(let's call this my wizard control). A user entering the app goes to the first tab and enters data (companyname and companydesc) and then clicks "Next". If the validation for the fields passes, the tab to the right of the current active tab should become focused. I have a validator class for each tab because each tab corresponds to a separate viewmodel. Each viewmodel has an instance of its validator and vice versa. The validator also has access to the wizard control via a wizardviewmodel. I am able to set the ActiveTab property form the validator below, but that change isn't reflected in the tabgrouppane. Here is the validator code:
public class CompanyValidator { private CompanyViewModel companyViewModel { get; set; } private WizardViewModel wizardViewModel { get; set; } public CompanyValidator(CompanyViewModel cvm) { this.companyViewModel = cvm; } #region Public Methods public void Validate() { ObservableCollection<ValidationFailure> localValidationErrors = new ObservableCollection<ValidationFailure>(); #region Validate Company if (companyViewModel.Name == String.Empty) { localValidationErrors.Add(new ValidationFailure("CompanyName", Properties.Resources.EmptyCompanyNameString)); } if (companyViewModel.Description == String.Empty) { localValidationErrors.Add(new ValidationFailure("CompanyDesc", Properties.Resources.EmptyCompanyDescString)); } #endregion companyViewModel.ValidationErrors = localValidationErrors; if (localValidationErrors.Count > 0) { StringBuilder sb = new StringBuilder(); foreach (var error in localValidationErrors) { sb.AppendLine(error.Description); } MessageBox.Show(sb.ToString()); } else { // Save the company. companyViewModel.Save(); wizardViewModel.SetActiveTab(1); // Hard-coded to the next tab for a test. } } #endregion }
Then its probably because you have keyboard focus in the first pane and simply changing the selected index will not take focus from that pane and we need to keep the active pane (i.e.the one with the keyboard focus) in view. If I may ask, why do you want to use a TabGroupPane for this? It would seem to me that you would want this to be treated as a single entity and not as indvidual pages. As we discussed earlier you wouldn't want to be able to unpin/float/close an individual pane. Also if this is a wizard then you wouldn't want the user to see each pane in the tab group - e.g. when they for example press Ctrl-Tab and see the pane navigator they will see each contentpane in the control which in this case will mean they will see each step/page/tab of the wizard or when they press Alt-F7 which cycles through the contentpanes. If you want the whole wizard to be one pane so that it may be floated/unpinned/closed as a unit that is fine - then I would say you should use a TabControl or something else as the wizard and put that by itself into a ContentPane but I'm not sure I understand why you want to use separate contentpanes as the steps/pages/tabs of the wizard.
Hmmm... Maybe I don't want to use this control given your last comment. I wasn't aware of the "CTRL" navigation. Does the igWindows:XamTabControl not allow for this? I don't really need to show all the tabs because I am controlling the flow of the "wizard". Do you recommend only adding tabs to the igWindows:XamTabControl as the user completes each section? On the other hand, if I use only one tab in the igWindows:XamTabControl how do you recommend I represent the "wizard" flow?
byronking said:Hmmm... Maybe I don't want to use this control given your last comment. I wasn't aware of the "CTRL" navigation. Does the igWindows:XamTabControl not allow for this?
byronking said: I don't really need to show all the tabs because I am controlling the flow of the "wizard". Do you recommend only adding tabs to the igWindows:XamTabControl as the user completes each section? On the other hand, if I use only one tab in the igWindows:XamTabControl how do you recommend I represent the "wizard" flow?