WPF data binding to composite class templates?

This is the first time I am launching WPF, and I am struggling with how to bind controls to a class that is created using the composition of other objects. For example, if I have a Comp class that consists of two separate classes (note the various elements that were omitted for clarity):

class One { int _first; int _second; } class Two { string _third; string _fourth; } class Comp { int _int1; One _part1; Two _part2; } 

Now I understand that I can easily bind _int1 using the "get" defined in Comp. But how do I get attached to the _part1._first, _part1._second elements. Do I provide getters for them at the Comp class level? or can I expose them in composite classes and use a binding path that points to them? And how does this work with setting properties?

So is this a pattern?

 .... <TextBlock Name="txtBlock" Text="{Binding Path=Third}" /> .... class One { int _first; int _second; } class Two { string _third; string _fourth; } class Comp { int _int1; One _part1; Two _part2; int Int1 { get { return _int1; } set { _int1 = value; } } int First { get { return _part1._first; } set { _part1._first = value; } } int Second { get { return _part1._second; } set { _part1._second = value; } } string Third { get { return _part2._third; } set { _part2._third = value; } } string Fourth { get { return _part2.fourth; } set { _part2._fourth = value; } } } ... Comp oComp = new Comp(); txtBlock.DataContext = oComp; ... 

Or is it a template? (where I'm not sure what to put in the way)

 .... <TextBlock Name="txtBlock" Text="{Binding Path=_part2.Third}" /> .... class One { int _first; int _second; int First { get { return _first; } set { _first = value; } } int Second { get { return _second; } set { _second = value; } } } class Two { string _third; string _fourth; string Third { get { return _third; } set { _third = value; } } string Fourth { get { return _fourth; } set { _fourth = value; } } } class Comp { int _int1; One _part1; Two _part2; int Int1 { get { return _int1; } } } ... Comp oComp = new Comp(); txtBlock.DataContext = oComp; ... 

Or am I on my way to rethinking MV-VM (which I am gradually starting to understand)?

 .... <TextBlock Name="txtBlock" Text="{Binding Path=Third}" /> .... class One { int _first; int _second; } class Two { string _third; string _fourth; } class Comp { int _int1; One _part1; Two _part2; } class CompView { Comp _comp; CompView( Comp comp ) { _comp = comp; } int Int1 { get { return _comp._int1; } set { _comp._int1 = value; } } int First { get { return _comp._part1._first; } set { _comp._part1._first = value; } } int Second { get { return _comp._part1._second; } set { _comp._part1._second = value; } } string Third { get { return _comp._part2._third; } set { _comp._part2._third = value; } } string Fourth { get { return _comp._part2.fourth; } set { _comp._part2._fourth = value; } } } ... Comp oComp = new Comp(); CompView oCompView = new CompView( oComp ); txtBlock.DataContext = oCompView; ... 

So how do I do this? If this is the first or third template, then it seems that I took all my beautiful (disparate) hierarchical data and broke it into a flat configuration so that I can bind it to user interface elements. Is this how it should happen, or is there a better way (second template?)

Edit

I left the question that I really need a two-way binding. Thus, the owners of the properties really had to get and install.

Edit

Updated my pseudo code to show setters as well as getters

Edit

I followed the pattern presented by Marc and Julien, and implemented the setters and was pleased with the result. For some reason, I convinced myself that setting a property would not follow up to the ultimate entity.

+4
source share
2 answers

Data binding works through properties, so you will not use any of the member variables in your binding, for example:

 int _first public int First { get { return _first; } } 

you will use First, not _first for binding. I usually saw that each class provides its own properties for binding, in which case you can change your code to:

 class One : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } int _first = 1; int _second = 2; public int First { get { return _first; } set { _first = value; OnPropertyChanged("First"); } } public int Second { get { return _second; } set { _second = value; OnPropertyChanged("Second"); } } } class Two : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } string _third = "Third"; string _fourth = "Fourth"; public string Third { get { return _third; } set { _third = value; OnPropertyChanged("Third"); } } public string Fourth { get { return _fourth; } set { _fourth = value; OnPropertyChanged("Fourth"); } } } class Comp : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } int _int1 = 100; One _part1 = new One(); Two _part2 = new Two(); public One Part1 { get { return _part1; } set { _part1 = value; OnPropertyChanged("Part1"); } } public Two Part2 { get { return _part2; } set { _part2 = value; OnPropertyChanged("Part2"); } } public int Int1 { get { return _int1; } set { _int1 = value; OnPropertyChanged("Int1"); } } } 

Note that I made the properties public, keeping private fields private by default. If you assign the DataContext of the parent to the Comp instance:

 Comp comp = new Comp(); stack.DataContext = comp; 

Then you can bind the elements to xaml as follows:

 <Window x:Class="TestApp.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestApp" Title="Window1" Height="300" Width="300"> <StackPanel Name="stack"> <TextBlock Text="{Binding Int1}"/> <TextBlock Text="{Binding Part1.First}"/> <TextBlock Text="{Binding Part1.Second}"/> <TextBlock Text="{Binding Part2.Third}"/> <TextBlock Text="{Binding Part2.Fourth}"/> </StackPanel> </Window> 

here you will see that the StackPanel receives the Comp as a DataContext (and therefore all its children also have this DataContext, unless otherwise specified), and the text is bound to them by member classes.

EDIT: I also added to the setters, and also implemented INotifyPropertyChanged, which is a vital component of data binding. Thanks to this, you can bind your data to several controls and see the data update in all the controls during the update. You need to add: using System.ComponentModel;

+3
source

I think you should always bind to a property, so your classes should be:

 class One { int _first; int _second; int First { get { return _first; } } int Second { get { return _second; } } } class Two { string _third; string _fourth; string Third { get { return _third; } } string Fourth { get { return fourth; } } } class Comp { int _int1; One _part1; Two _part2; One Part1 { get { return _part1; } } Two Part2 { get { return _part2; } } } 

Then you can become attached to everything you want:

 .... <TextBlock Name="txtBlock" Text="{Binding Path=Part2.Third}" /> .... 
+1
source

All Articles