I
I
Ivan Vyrov2018-10-18 14:28:34
WPF
Ivan Vyrov, 2018-10-18 14:28:34

How to properly implement "complex" data binding in wpf?

Please advise the beginner how to correctly implement the binding through the XAML of the following class:

Binding class
using System.ComponentModel;



namespace TestBinding
{
    public class Service : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private int id;
        private InfoService info;
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public int Id
        {
            get { return id; }
            set
            {
                id = value;
                RaisePropertyChanged("Id");
            }
        }
        public InfoService Info
        {
            get { return info; }
            set
            {
                info = value;
                RaisePropertyChanged("Info");
            }
        }
    }

    public class InfoService : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private int id;
        private ServiceMain main;
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        public int Id
        {
            get { return id; }
            set
            {
                id = value;
                RaisePropertyChanged("Id");
            }
        }
        public ServiceMain Main
        {
            get { return main; }
            set
            {
                main = value;
                RaisePropertyChanged("Main");
            }
        }
    }

    public class ServiceMain : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private int id;
        private string name;
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public int Id
        {
            get { return id; }
            set
            {
                id = value;
                RaisePropertyChanged("Id");
            }
        }
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                RaisePropertyChanged("Name");
            }
        }
    }
}
Filled out the class
using System.Windows;

namespace TestBinding
{
    public partial class MainWindow : Window
    {
        public Service service;
        public MainWindow()
        {
            InitializeComponent();
            service = 
                new Service{ Id = 0, Info = 
                new InfoService{ Id = 1, Main = 
                new ServiceMain { Id = 2, Name="Тест"} } };
        }
    }
}
Interface Implementation
<Window x:Class="TestBinding.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:TestBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel DataContext="{Binding Source=service}">
        <TextBlock Text="Service"/>
        <TextBox Text="{Binding Path=Id}"/>
        <TextBlock Text="InfoService"/>
        <TextBox Text="{Binding Path=Id}"/>
        <TextBlock Text="ServiceMain"/>
        <TextBox Text="{Binding Path=Id}"/>
    </StackPanel>
</Window>


The expected result is to see the following values ​​in the fields:
0
1
2
But something went wrong...

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Meloman19, 2018-10-18
@Meloman19

Well, you immediately have 2 errors here.
First, you cannot set the DataContext in the StackPanel in this way, because the DataContext of the window itself is empty. In general, your View does not intersect with the ViewModel anywhere. Though at least one point of intersection should be. This is usually done in App.xaml.
Open App.xaml and delete StartupUri. In the same place, drive in Startup and add a handler to this event in the code.

private void Application_Startup(object sender, StartupEventArgs e)
{
   MainWindow = new MainWindow
   {
      DataContext = new Service
      {
         Id = 0,
         Info = new InfoService
         {
            Id = 1,
            Main = new ServiceMain { Id = 2, Name = "Тест"}
         }
      }
   };
   MainWindow.Show();
}

Second: you have nested properties. Therefore, you need to do nested TextBoxes. The code is attached taking into account what you did, as in the previous paragraph.
<StackPanel>
      <TextBlock Text="Service"/>
      <TextBox Text="{Binding Path=Id}"/>
      <StackPanel DataContext={Binding Info}>
            <TextBlock Text="InfoService"/>
            <TextBox Text="{Binding Path=Id}"/>
            <StackPanel DataContext={Binding Main}>
                  <TextBlock Text="ServiceMain"/>
                  <TextBox Text="{Binding Path=Id}"/>
            </StackPanel>
      </StackPanel>
</StackPanel>

Well, just a note for the future. It makes sense to separate PropertyChanged and the RaisePropertyChanged method into a separate class, from which all your ViewModels (Service, InfoService, ServiceMain) will further inherit.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question