0

I have the following configuration:

MainWindow.xaml

<Window x:Class="MYNS.MainWindow"
        mc:Ignorable="d"
        x:Name="mainWindow"
        ...

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public Options Options { get; private set; }

    public MainWindow()
    {
        Options = new Options();
    }
}

Options.xaml

class Options 
{
    public bool QuietMode { get; set; }

    public Options()
    {
        QuietMode = true;
    }
}

Now I want to bind this boolean value to a CheckBox control of another window, so I have

OptionsWindows.xaml

<CheckBox 
  Content="Quiet Mode" 
  DataContext="{Binding ElementName=mainWindow, Path=Options}"
  IsChecked="{Binding Path=QuietMode}"
/>

which I though was going to do the trick but it doesn't work (I tried multiple similar approaches but none worked so far). Since I'm quite new with WPF, I think I'm clearly missing something here.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • 1
    Both windows could share the same DataContext. – Babbillumpa Jul 09 '18 at 14:15
  • 1
    It's clear that the `Options` property is a "model" property and needs to be in a view-model, not in the view class. – dymanoid Jul 09 '18 at 14:16
  • @dymanoid: Actually I didn't have the need to directly bind the Options object to anything in OptionsView so far (and such Options are used in MainWindow code although set from the other view) so that hasn't been an issue so far – Jack Jul 09 '18 at 14:18
  • "So far" means that now you need it. That's why you should refactor this data to a common place (a common Model or ViewModel that would be the DataContext of both Views), as suggested by previous comments. – heltonbiker Jul 09 '18 at 14:30
  • @heltonbiker: I see what you are saying but it's not clear where the actual instance of this Options (or the DataModel containing it) will be declared and kept. I'd like to have logical objects (like `Options`) clearly allocated somewhere, I don't see who will own `Options` if not as a static instance. – Jack Jul 09 '18 at 14:32
  • I'll update my answer to explain that. – heltonbiker Jul 09 '18 at 14:39
  • My answer is already updated! – heltonbiker Jul 09 '18 at 14:45
  • @Jack: So what's the relationship between an OptionsWindows and a MainWindow? How do they know each other? What does ElementName=mainWindow refer to in your example? – mm8 Jul 10 '18 at 14:51

1 Answers1

4

Bindings with ElementName only work inside the same UserControl. If the two Windows are completely separated, you have no option besides moving the Options property from MainWindow class to a proper Model or ViewModel class, which you should do anyway because that's how WPF/MVVM is supposed to be.

If you really want to bind from one view to the other, the different Window instances should at least share some common ancestor, so that you could use, instead of ElementName, the FindAncestor Binding parameter, but I doubt this would be an easy, or natural, or even possible solution. (I think it's not even possible because Window is a "top control" which doesn't have a parent in the Logical or Visual Trees.

The best solution by far is to take model data away from the View as soon as possible, you'll get a much easier environment to work with an improve upon.


UPDATE: Some MVVM context:

Usually in WPF apps you have some classes pertaining to the "View" layer (Windows, Controls, CheckBoxes) and others pertaining to the "[View]Model" layer, where you would put your Option class. You'd need to bootstrap your application in a way that, very soon in the startup execution, you have an instance of a View and an instance of a ViewModel, and the latter should be set as the DataContext of the former, so you can use DataBinding.

There is some religious wars around the "View-First" vs "Model-First" approach, but I don't use either: I always have a BootStrapper class that creates the MainView (usually a Window) and the MainViewModel, and let each class tree handle itself.

An exemple:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var window = new MainWindow();
        var vm = new MainViewModel();
        window.DataContext = vm;
        window.Show();
    }
}

But please have in mind that there is no "one right way" to do it. You'll have to decide what works best for you, unfortunately.

heltonbiker
  • 26,657
  • 28
  • 137
  • 252