1

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:

  1. (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 call RaisePropertyChanged("SelectedProjectName")?

  2. 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.

4

1 回答 1

0

从您的选择中,我不喜欢 #1,因为它可能会引入大量难以跟踪和维护的布线。

选项#2 可能没问题,但最终可能会出现与#1 相同的接线混乱,但由于消息传递,它会不太明显。

我会考虑一种不同的方法:

  • 在没有打开或选择文件时,在 MainWindow 中放置一个菜单,该菜单负责处理命令。
  • 当文档在视图中打开并具有焦点时:将当前菜单替换为文档特定菜单。(就像 MDI 应用程序在 WinForms 中工作)

这样,您可以自定义每个文档(类型)的菜单,它不需要事件依赖项。

于 2013-08-27T08:48:27.143 回答