Search code examples
wpfmvvmdata-bindingioc-containerstructuremap

How can I bind a command in a user control?


Introduction

I am trying to cover my C# WPF application into an MVVM pattern using Structuremap IoC container for dependency injection.

My code is works well until I try to use the same command binding in UserControls as Windows.

I tried the following

If I try to bind a command in a certain UserControl, I got the following error: System.Windows.Data Error: 40 : BindingExpression path error: 'HelloWorldCommand' property not found on 'object' ''MainWindowViewModel' (HashCode=7304143)'. BindingExpression:Path=HelloWorldCommand; DataItem='MainWindowViewModel' (HashCode=7304143); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')

So the problem is that my HelloWorldCommand is it in my CustomUserControlViewModel which contained and binded by MainWindowViewModel.

My custom code snippet

My code is 90% same to the following tutorial: Part 1 Part 2

Only the ObjectFactory method is different, which can be seen below:

public sealed class ObjectFactory
    {
        public static IContainer Container { get; private set; }

        private static Action<ConfigurationExpression> _initialiseMethod;
        private static readonly Lazy<IContainer> _containerBuilder =
            new Lazy<IContainer>(CreateContainer, LazyThreadSafetyMode.ExecutionAndPublication);

        public static void Initialise()
        {
            Container = _containerBuilder.Value;
        }

        private static IContainer CreateContainer()
        {
            return new Container(config =>
            {
                #region services
                config.For<IFileDisplayerService>().Singleton().Use<FileDisplayerService>();
                config.For<IWatermarkService>().Singleton().Use<WatermarkService>();
                #endregion

                #region windows
                config.For<IWindow>().Use<MainWindow>();
                config.For<IWatermarkWindow>().Use<WatermarkSettingsWindow>();

                config.For<IMainWindow>().Singleton().Use<MainWindow>();
                config.For<IMainWindowViewModel>().Singleton().Use<MainWindowViewModel>();

                config.For<IWatermarkSettingsWindow>().Singleton().Use<WatermarkSettingsWindow>();
                config.For<IWatermarkSettingsWindowViewModel>().Singleton().Use<WatermarkSettingsWindowViewModel>();
                #endregion

                #region views
                config.For<IFileListView>().Use<FileListView>();
                config.For<IFileListViewModel>().Use<FileListViewModel>()
                    .Ctor<IView>().Is<FileListView>();

                config.For<IFileDisplayerView>().Use<FileDisplayerView>();
                config.For<IFileDisplayerViewModel>().Use<FileDisplayerViewModel>()
                    .Ctor<IView>().Is<FileDisplayerView>();
                #endregion
            });
        }
    }

My question

My concrete question is that how can I bind a command to a user control which has an own- and a parent ViewModel? This is not shown in the example above.

I think the parent ViewModel should contain the command as well, but I don't know how can I pass it to the parent ViewModel from child ViewModel.


Solution

  • Thank you mm8 I solved my problem like the following mode:

    MainWindowViewModel:

    public IViewModel FileListViewModel { get; set; }
    public IViewModel FileDisplayerViewModel { get; set; }
    
    public IView FileListView { get; set; }
    public IView FileDisplayerView { get; set; }
    
    public MainWindowViewModel(IWindow window, IContainer container,
            IFileDisplayerViewModel fileDisplayerViewModel, IFileListViewModel fileListViewModel) : base(window, container)
            {
                FileListViewModel = fileListViewModel;
                FileListView = FileListViewModel.View;
    
                FileDisplayerViewModel = fileDisplayerViewModel;
                FileDisplayerView = FileDisplayerViewModel.View;
            }
    

    Now I can bind my UserControl's ViewModel into UserControlView:

    <Button Command="{Binding FileListViewModel.HelloWorldCommand}" Width="100" Height="20" Content="Push" Background="White"></Button>
    

    This is not entirely what I wanted but it saves me from catastrophic spaghetti code. I think it is feasible that I have a child view collection which is used to automatically bind a command from them if it is not available in the parent view. But this is sufficient now to proceed the project. Thank you!