0

I've spent the day looking at other questions on this topic and cannot get this particular code to work.

Background: We're using Prism to auto-register viewsmodels for views and inject services using Prism's DI container. The app is structured on reusable user controls. So a specific user control might appear on different windows (or, obviously, multiple instances of the same window). For example, we have a Company window with five different user controls. Each user control's viewmodel must get the CompanyId from the parent window's viewmodel so it can use that CompanyId to call various services, etc.

I'm tried setting up a simple example without Prism but with the Community MVVM Toolkit and haven't had much luck.

The window XAML:

<Window
    x:Class="BindingDependencyProperties.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:BindingDependencyProperties"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0">
            <Run Text="{Binding CompanyIdFromWin}" />
        </TextBlock>
        <local:CompanyUserControl Grid.Row="1" CompanyId="{Binding CompanyIdFromWin}" />
        <local:CompanyUserControl Grid.Row="2" CompanyId="hardcoded" />
    </Grid>
</Window>

Window code-behind:

namespace BindingDependencyProperties
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

Window VM:

namespace BindingDependencyProperties
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        private string _companyIdFromWin;

        public MainWindowViewModel()
        {
            CompanyIdFromWin  = "test123";
        }
    }
}

User Control XAML:

<UserControl x:Class="BindingDependencyProperties.CompanyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:BindingDependencyProperties"
                 d:DataContext="{d:DesignInstance Type=local:CompanyUserControlViewModel, IsDesignTimeCreatable=True}"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBlock>
            <Run Text="Company ID: " />
            <Run Text="{Binding CompanyId}" />
        </TextBlock>
    </Grid>
</UserControl>

User Control code behind:

namespace BindingDependencyProperties
{
    public partial class CompanyUserControl : UserControl
    {
        private CompanyUserControlViewModel _viewModel;

        public string CompanyId
        {
            get { return (string)GetValue(CompanyIdProperty); }
            set { SetValue(CompanyIdProperty, value); }
        }

        public static readonly DependencyProperty CompanyIdProperty =
            DependencyProperty.Register("CompanyId", typeof(string), typeof(CompanyUserControl),
                new PropertyMetadata(string.Empty));

        public CompanyUserControl()
        {
            InitializeComponent();
            _viewModel = new CompanyUserControlViewModel();
            this.DataContext = _viewModel;
        }
    }
}

User Control VM:

    public partial class CompanyUserControlViewModel : ObservableObject
    {
        [ObservableProperty]
        private string _companyId = string.Empty;

    }

When I run the app I see:

App output

The CompanyId's aren't flowing into the usercontrol VM. Please note I did try without using the MVVM Toolkit, manually implementing INotifyPropertyChanged and the backing properties and handler with the same result.

So,

  1. What am I doing wrong here?
  2. Is this the best way of doing this, given that eventually Prism will be initializing the viewmodels (not CompanyUserControl's constructor) and we don't want tight coupling between windows and user controls.

2 Answers 2

0

What am I doing wrong here?

public CompanyUserControl()
{
    InitializeComponent();
    _viewModel = new CompanyUserControlViewModel();
    this.DataContext = _viewModel;
}

That's not a reusable control, it is very specific and specialized. That's fine, all applications have that, mostly for top level views or pages. Those also make use of Prism's view model locator.

In your situation, though, you want the control to receive its view model from someone else, e.g. by binding an ItemsControl's ItemsSource to a collection property on the parent view model and creating your control through ItemTemplate.

Sign up to request clarification or add additional context in comments.

1 Comment

I don't understand why you don't consider this a reusable user control or why I would want the viewmodel to come from somewhere else. We're trying to do something similar to youtube.com/watch?v=d1PqVmmFMSQ Also, the user control's view model will have all the retrieve, validate, save, etc. logic for the user control. The parent view model will not know how to do any of this.
0

According to the video tutorial you have mentioned in the comment (youtube video at 5 minute and 15 seconds, https://youtu.be/d1PqVmmFMSQ?t=315), you have forgotten to use x:Name="root" in the User Control XAML. And then in Text binding to use ElementName=root. This worked for me and this should do the trick...

<UserControl x:Class="WpfApp1.Components.CompanyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:BindingDependencyProperties"
         mc:Ignorable="d" 
         d:DesignHeight="450" d:DesignWidth="800"
         d:DataContext="{d:DesignInstance Type=local:CompanyUserControlViewModel, IsDesignTimeCreatable=True}"
         x:Name="root"
         Background="White">
<Grid>
    <TextBlock>
        <Run Text="Company ID: " />
        <Run Text="{Binding CompanyId, ElementName=root}" />
    </TextBlock>
</Grid>

after fixing showing "hardcoded" in Window I encountered the missing value of the first Company ID: and fix it by adding RelativeSource in the Window XAML:

<local:CompanyUserControl Grid.Row="1" CompanyId="{Binding DataContext.CompanyIdFromWin, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

Hopefully this should help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.