0

Trying to get DataContext in UserControl.

My structure

I have the model Car

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace AutoShop.MVVM.Model
{
    class Car : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string prop = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }


        private string _Model;
        public string Model
        {
            get
            {
                return _Model;
            }
            set
            {
                _Model = value;
                OnPropertyChanged();
            }
        }

        private string _Mark;
        public string Mark
        {
            get
            {
                return _Mark;
            }
            set
            {
                _Mark = value;
                OnPropertyChanged();
            }
        }

        private float _Volume;
        public float Volume
        {
            get
            {
                return _Volume;
            }
            set
            {
                _Volume = value;
                OnPropertyChanged();
            }
        }

        private int _DateOfIssue;
        public int DateOfIssue
        {
            get
            {
                return _DateOfIssue;
            }
            set
            {
                _DateOfIssue = value;
                OnPropertyChanged();
            }
        }

        public enum EngineTypes { 
            DISEL,
            PETROL
        };

        private EngineTypes _EngineType;
        public EngineTypes EngineType
        {
            get
            {
                return _EngineType;
            }
            set
            {
                _EngineType = value;
                OnPropertyChanged();
            }
        }

        private string _ImageUrl;

        public string ImageUrl
        {
            get
            {
                return _ImageUrl;
            }
            set
            {
                _ImageUrl = value;
                OnPropertyChanged();
            }
        }

        public Car()
        {

        }
    }
}

And I have main view model

using AutoShop.MVVM.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace AutoShop.MVVM.ViewModel
{
    class MainViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName] string prop = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }

        public HomeViewModel HomeVM; 

        private object _CurrentPage;
        public object CurrentPage
        {
            get
            {
                return _CurrentPage;
            }
            set
            {
                _CurrentPage = value;
                OnPropertyChanged();
            }
        }

        private List<Car> _Cars;

        public List<Car> Cars
        {
            get
            {
                return _Cars;
            }
            set
            {
                _Cars = value;
                OnPropertyChanged();
            }
        }

        public MainViewModel()
        {
            Cars = new List<Car>() {
                new Car
                {
                    Mark = "Audi",
                    Model = "asdf",
                    Volume = 1.4F,
                    DateOfIssue = 2019,
                    EngineType = Car.EngineTypes.DISEL,
                    ImageUrl = "img/img"
                },
                new Car
                {
                    Mark = "Moto",
                    Model = "asdf",
                    Volume = 1.4F,
                    DateOfIssue = 2019,
                    EngineType = Car.EngineTypes.DISEL,
                    ImageUrl = "img/img"
                },
                new Car
                {
                    Mark = "Some",
                    Model = "asdf",
                    Volume = 1.4F,
                    DateOfIssue = 2019,
                    EngineType = Car.EngineTypes.DISEL,
                    ImageUrl = "img/img"
                }
            };


            HomeVM = new HomeViewModel();
            CurrentPage = HomeVM;

        }
    }
}

I need to display cars on the ProductPage.xaml and I do it by the next way

<UserControl x:Class="AutoShop.MVVM.View.ProductPage"
             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:AutoShop.MVVM.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             >
    <StackPanel Background="#fff">
        <WrapPanel>
            <Grid Width="200px" Margin="30 0 0 0">
                <TextBox x:Name="field4" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
                <TextBlock IsHitTestVisible="False" Text="Марка" Padding="20 10 0 0">
                    <TextBlock.Style>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="Visibility" Value="Collapsed"/>
                            <Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
                            <Setter Property="Foreground" Value="#ccc" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Text, ElementName=field4}" Value="">
                                    <Setter Property="Visibility" Value="Visible" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>
            </Grid>
            <Grid Width="200px" Margin="30 0 0 0">
                <TextBox x:Name="field7" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
                <TextBlock IsHitTestVisible="False" Text="username" Padding="20 10 0 0">
                    <TextBlock.Style>
                        <Style TargetType="{x:Type TextBlock}">
                            <Setter Property="Visibility" Value="Collapsed"/>
                            <Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
                            <Setter Property="Foreground" Value="#ccc" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Text, ElementName=field7}" Value="">
                                    <Setter Property="Visibility" Value="Visible" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>
            </Grid>
            <Grid Width="200px" Margin="30 0 0 0">
                <ComboBox Style="{StaticResource ComboBoxTheme}" SelectedIndex="0">
                    <ComboBoxItem>
                        <TextBlock Text="asdasdasd" />
                    </ComboBoxItem>
                    <ComboBoxItem>
                        <TextBlock Text="fsdfsd" />
                    </ComboBoxItem>
                </ComboBox>
            </Grid>
        </WrapPanel>
        <Grid Height="400">
            <ScrollViewer VerticalScrollBarVisibility="Auto">
                <ItemsControl x:Name="ItemsWrapper" ItemsSource="{Binding Cars}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border Margin="10" BorderBrush="Black" BorderThickness="2" Height="279">
                                <Grid Height="279" Width="200">
                                    <Image Source="{Binding Path=Image}" Width="100" Height="100" VerticalAlignment="Top" Margin="0 10 0 0"  />
                                    <TextBlock Text="{Binding Path=Name, StringFormat='Name: {0}'}" VerticalAlignment="Top" Margin="0,120,0,0"/>
                                    <TextBlock Text="{Binding Path=Mark, StringFormat='Rating: {0}' }" VerticalAlignment="Top" Margin="0 180 0 0" />
                                    <TextBlock Text="{Binding Path=Model, StringFormat='Category: {0}'}" VerticalAlignment="Top" Margin="0,200,0,0" />
                                    <TextBlock Text="{Binding Path=Volume, StringFormat='Price: {0}'}" VerticalAlignment="Top" Margin="0,160,0,0" />
                                    <TextBlock Text="{Binding Path=DateOfIssue, StringFormat='Description: {0}'}" VerticalAlignment="Top" Margin="0,140,0,0" />
                                </Grid>
                            </Border>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
        </Grid>
    </StackPanel>
</UserControl>

And it the MainForm.xaml I added ProductPage.xaml

<ContentControl Margin="10" Grid.Column="1" Content="{Binding CurrentPage}"/>

The problem is that nothing is being outputted, I think it might be due to the loss of context. How do I properly pass the context to UserControl (ProductPage.xaml)? ProductPage.xaml

Update: I set DataContext for MainWindow

And DataContext working becouse {Binding CurrentPage} is working, but binding on Cars field is not working

  • 1
    are you getting any errors in the output window? – default Apr 26 '21 at 11:43
  • @Default No, I expect items on the my app, but there are nothing https://prnt.sc/123as04 DataContext is working, becouse my View is loaded (ProductPage.xaml) – dotsqlcode Apr 26 '21 at 11:47
  • Sorry, I meant [the output window](https://learn.microsoft.com/en-us/visualstudio/ide/reference/output-window?view=vs-2019) in Visual Studio, showing status messages. WPF is fairly good at showing a lot of information there. you can search in that window after "ERROR". – default Apr 26 '21 at 11:53
  • hm.. I think I see your problem. see if [this thread](https://stackoverflow.com/questions/16985382/binding-to-usercontrol-dependencyproperty) would help you – default Apr 26 '21 at 12:32
  • 1
    You have `MainViewModel` in the DataContext of the Window. It is not clear from your code how UserControl is used in the Window layout. Please show this, as it may be important in understanding the cause of the problem. – EldHasp Apr 26 '21 at 15:03
  • To understand why, add `` to the StackPanel as the first element (before the WrapPanel). And let us know what this TextBlock will display. – EldHasp Apr 26 '21 at 15:06

2 Answers2

0

1.find the MainForm.xaml.cs

2.find ctor

public MainForm()
{
    InitializeComponent();
    //set the MainForm DataContext
    DataContext = new MainViewModel();
}

3.you should add ContentControl.DataTemplate to show your HomeViewModel

----20210427 update

Here demo i make, project name is WpfApp4

MainWindow.xaml

<Window x:Class="WpfApp4.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        xmlns:views="clr-namespace:WpfApp4.Views"
        xmlns:viewModels="clr-namespace:WpfApp4.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <viewModels:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox Grid.Column="0" ItemsSource="{Binding ItemsSource, Mode=OneWay}" SelectedItem="{Binding SelectedCarViewModel, Mode=TwoWay}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Grid Grid.Column="1">
            <ContentControl Content="{Binding SelectedCarViewModel}">
                <ContentControl.ContentTemplate>
                    <!--Style your product page here-->
                    <DataTemplate DataType="{x:Type viewModels:CarViewModel}">
                        <UniformGrid Columns="2">
                            <TextBlock Text="Name"/>
                            <TextBlock Text="{Binding Name}"/>
                            <TextBlock Text="Volume"/>
                            <TextBlock Text="{Binding Volume}"/>
                            <TextBlock Text="Price"/>
                            <TextBlock Text="{Binding Price}"/>
                        </UniformGrid>
                    </DataTemplate>
                </ContentControl.ContentTemplate>
            </ContentControl>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace WpfApp4
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

MainWindowViewModel.cs

using System.Collections.ObjectModel;

namespace WpfApp4.ViewModels
{
    class MainWindowViewModel : ViewModelBase
    {
        public ObservableCollection<CarViewModel> ItemsSource { get; } = new ObservableCollection<CarViewModel>();

        private CarViewModel selectedCarViewModel;

        public CarViewModel SelectedCarViewModel
        {
            get { return this.selectedCarViewModel; }
            set { SetProperty(ref this.selectedCarViewModel, value); }
        }

        public MainWindowViewModel()
        {
            ItemsSource.Add(new CarViewModel() { Name = "BMW", Volume = 10, Price = 100 });
            ItemsSource.Add(new CarViewModel() { Name = "Toyota", Volume = 5, Price = 80 });
            ItemsSource.Add(new CarViewModel() { Name = "Benz", Volume = 20, Price = 150 });

            // don't let view binding null value, so here initialze property "SelectedCarViewModel"
            this.selectedCarViewModel = ItemsSource[0];
        }
    }
}

CarViewModel.cs

namespace WpfApp4.ViewModels
{
    class CarViewModel : ViewModelBase
    {
        private string name;

        public string Name
        {
            get { return this.name; }
            set { SetProperty(ref this.name, value); }
        }

        private float volume;

        public float Volume
        {
            get { return this.volume; }
            set { SetProperty(ref this.volume, value); }
        }

        private decimal price;

        public decimal Price
        {
            get { return this.price; }
            set { SetProperty(ref this.price, value); }
        }
    }
}

ViewModelBase.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;

namespace WpfApp4.ViewModels
{
    public class ViewModelBase : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        
        protected bool SetProperty<T>(ref T t, T value, [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(t, value))
            {
                return false;
            }
            else
            {
                t = value;
                RaisePropertyChanged(propertyName);
                return true;
            }
        }
    }
}
Allen Hu
  • 141
  • 6
0

Add this to the Window.Resources. Of course DataContext must be set for the MainForm.

<Window>
    <Window.Resources>
        <DataTemplate DataType="{x:Type xmlnsForVewModel:MainViewModel}">
            <ProductPage/>
        </DataTemplate>
    </Window.Resources>
</Window>

and if for ProductPage HomeViewModel supposed to be a DataContext, then HomeViewModel has to have Cars property, not MainViewModel.

Rekshino
  • 6,954
  • 2
  • 19
  • 44
  • Sorry, I forgot mention than page changing is working. I edited the queshion. Check please. CurrentPage binding for ContentControl is working, but bundings not work in the ProductPage.xaml although in theory they have the same context – dotsqlcode Apr 26 '21 at 11:36
  • @dotsqlcode Will `ProductPage` also be shown? Are you sure you don't set `DataContext` in code behind of `UserControl`? – Rekshino Apr 26 '21 at 11:42