9
votes

I am trying to bind a Window Title to the ViewModel which has a Title property. Below is the 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>

When I try to run this, I get the following exception "Provide value on 'System.Windows.StaticResourceExtension' threw an exception. The line number and position point to the DataContext property, and the inner exception states "Cannot find resource named mainWindowViewModel.

Below is the code for the View Model:

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

My theory is that the resources are loaded after the attempt to bind the title to the property. When the exception is thrown, the Resources property for the Window is empty.

Is the only solution to set the DataContext in the Code Behind? I can get this to work, but I would prefer to keep it in XAML.

2
you could always move your VM resource to app.xaml if that's applicable. On a sidenote please name VM classes as SomethingViewModel and not just the same name as the View and use namespace to differentiate classes. It's just really weird and freaky - Viv
Josh Smith had an example of this I will see if I can locate it, basically when the datatemplate was applied in the XAML the title was applied too.. - Zach Leighton

2 Answers

13
votes

You can try to set the DataContext using property element syntax:

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

That should work as the xaml parser will execute StaticResourceExtension after the resources dictionary is set.

But i think maybe even better would be to set the DataContext directly, without declaring it as resource:

<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>
0
votes

A little late but a simple and perfect solution that I am using just in case people are still searching for possibilities:

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

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

</Window>

Than simply enough, just add a Property to your MainViewModel that is the DataContext of the MainView like so:

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

                return text;
            }
        }

Hope it helps..