Motivation:
I'd like to have a 'File->Save As' MenuItem
that behaves just like in Visual Studio. When there is nothing opened it says "Save Selected Items as..."
and when a particular file (e.g. SomeFile.cs
) is opened in a tab, the MenuItem
reads "Save SomeFile.cs
as...".
My App architecture (MVVM
, using MVVM Light
):
MainWindow.xaml:
<MenuItem Header="{Binding SelectedProjectName}" HeaderStringFormat="Save {0} As..." />
MainWindowViewModel:
I hold a collection of opened tabs (opened files)
private ObservableCollection<BaseProjectViewModel> _projects;
I have a property returning a currently selected tab
public BaseProjectViewModel SelectedProject
{
get
{
return _selectedProject;
}
set
{
if (_selectedProject == value)
{
return;
}
_selectedProject = value;
RaisePropertyChanged("SelectedProject");
RaisePropertyChanged("SelectedProjectName");
}
}
I created a property returning the name of the file in the currently selected tab
public string SelectedProjectName
{
get
{
if (SelectedProject == null)
{
return "Selected Item";
}
return SelectedProject.SafeFileName;
}
}
BaseProjectViewModel
serves as a base class for various file types. Each file type has its own class derived from BaseProjectViewModel
. It has properties like for example
PaneHeader
that returns a string to be displayed in pane header,
SafeFileName
that returns just the file name of a path etc...
Question:
When I change the name of the file (thus changing properties of the BaseProjectViewModel
) how do I trigger RaisePropertyChanged
of the SelectedProjectName
in MainWindowViewModel
?
What is the cleanest way to do that?
My thoughts
I thought of two possible ways to do that, but i don't know if any of them is the correct way to do it:
(In short) Listening to
CollectionChanged
on_projects
. When there is add/remove -> subscribe/ubsubscribe an event handler that would look if the PropertyName is the one we are looking for and if yes subsequently callRaisePropertyChanged("SelectedProjectName")
?Use something like
MVVM Light Messaging
.
Question 2: If you don't suggest any other way and in fact you'd suggest one of these two - could you please elaborate on advantages and disadvantages?
EDIT
I created a very simple project to demonstrate the issue - LINK. When you run the project:
- 'New' adds a new TabItem. When text is edited, the TabHeader is decorated with an asterisk.
- 'Save {0}' menu item "saves" the selected TabItem (simulated by removing the asterisk). I didn't want to complicate the example and introduce a SaveFileDialog and such.
- 'Save As {0}' menu item simulates Save as in such a way that it adds 'X' character to the end od Tab header string.
When no TabItem is selected, the {0} resolves to "Selected Item".
When you have one tab selected, you click SaveAs() and open the menu, you'll notice that change has not been raised on SelectedProjectName property. When you click another tab and then select the first one back, the change is propagated.
Edit for Erno: What I fail to understand is this: Let's suppose I have a special menu for each document type. Let's suppose I have one particular tab selected (with it's own menu enabled/visible, the other collapsed). How is it going to help me propagate the PropertyChanged of PaneHeader property in BaseProjectViewMode to SelectedProjectName in MainWindowViewModel? If you have time could you please demonstrate it on the example? I also would like to ask you what would be an alternate way if I wanted/neede to do the wiring? Thank you in advance.