WPF绑定窗口标题到ViewModel属性

9

我正在尝试将窗口标题绑定到具有Title属性的ViewModel。以下是MainWindow XAML:

<Window x:Class="MyProject.View.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
        Title="{Binding Path=Title}" Height="350" Width="525" DataContext="{Binding Source={StaticResource mainWindowViewModel}}">
    <Window.Resources>
        <vm:MainWindow x:Key="mainWindowViewModel"/>
    </Window.Resources>

...

</Window>

当我尝试运行此代码时,出现以下异常:"在 'System.Windows.StaticResourceExtension' 上提供值引发了异常。" 行号和位置指向 DataContext 属性,内部异常说明 "找不到名为 mainWindowViewModel 的资源。"
下面是视图模型的代码:
namespace MyProject.ViewModel
{
    public class MainWindow : INotifyPropertyChanged
    {
        #region Fields

        private const string TitlebarPrefixString = "My Project";
        private string title = TitlebarPrefixString;

        public string Title {
            get
            {
                return this.title;
            } // End getter
            set
            {    
                this.title = value;
                OnPropertyChanged("Title");
            } // End setter
        } // End property

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            } // End if
        } // End method


        public event PropertyChangedEventHandler PropertyChanged;

    } // End class
} // End namespace

我的理论是,在尝试将标题绑定到属性之后,资源被加载。当抛出异常时,窗口的资源属性为空。

是否唯一的解决方案是在 Code Behind 中设置 DataContext?我可以让它工作,但我更喜欢保持在 XAML 中。


2
如果适用的话,您可以将VM资源移动到app.xaml。另外,请将VM类命名为“SomethingViewModel”,而不仅仅是与View相同的名称,并使用命名空间来区分类。这样做只会让人感到奇怪和恐怖。 - Viv
Josh Smith有一个相关的例子,我会看看能否找到它。基本上,当在XAML中应用datatemplate时,标题也会被应用。 - Zach Leighton
2个回答

13

您可以尝试使用属性元素语法设置DataContext

<Window x:Class="MyProject.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
    Title="{Binding Path=Title}" Height="350" Width="525">
<Window.Resources>
    <vm:MainWindow x:Key="mainWindowViewModel"/>
</Window.Resources>
<Window.DataContext>
  <StaticResourceExtension ResourceKey="mainWindowViewModel"/>
</Window.DataContext>

因为XAML解析器会在资源字典设置完成后执行StaticResourceExtension,所以这应该能够正常工作。

但是我认为,直接设置DataContext可能会更好,而无需将其声明为资源:

<Window x:Class="MyProject.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
    Title="{Binding Path=Title}" Height="350" Width="525">
<Window.DataContext>
    <vm:MainWindow x:Key="mainWindowViewModel"/>
</Window.DataContext>

你的第一个建议会抛出相同的异常。有趣的是,当我使用 Break 时,窗口在本地视图中显示资源。第二个建议会给出三个编译时错误:““Key”属性只能用于包含在IDictionary中的元素”; “Key属性只能用于包含在IDictionary类型属性中的标记”和“Key属性只能用于包含在字典中的标记(例如ResourceDictionary)”。 - Tim
1
@Tim,我在 ResourceKey="mainWindowModel" 中犯了一个错误,它应该是 ResourceKey="mainWindowViewModel"。如果你只是复制粘贴,请尝试更正。但我并不确定这一点,我想我曾经让它像那样工作。 - Jurica Smircic
修复了,这是非常好的消息。我正在重新访问这个问题,因为我在引用程序声明的视图模型方面遇到了困难,因此再次将其变成纯XAML可能有助于我的另一个问题。 - Tim
@jure,你的建议帮助我解决了一个有些相关的问题。我想将窗口标题设置为具有路径和转换器的绑定。我在<Window.Resources>中定义了转换器,但是在<Window>中设置绑定会抛出异常,因为它还没有访问<Window.Resources>。按照你提出的方法设置窗口标题是一个简单的解决方法。谢谢。 - user2023861

0

可能有点晚了,但我这里有一个简单而完美的解决方案,以防万一还有人在寻找可能性:

<Window x:Class="Project.MainWindow"
        Title="{Binding DataContext.ApplicationTitle, ElementName=TheMainView}">

        <views:MainView x:Name="TheMainView"/>

</Window>

那么很简单,只需在您的MainViewModel中添加一个属性,它是MainView的DataContext,如下所示:

public string ApplicationTitle
        {
            get
            {
                var text = "Application Name";
                if (!string.IsNullOrEmpty(_currentFileLoaded))
                {
                    text += $" ({_currentFileLoaded})";
                }

                return text;
            }
        }

希望这有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接