Skip to main content

Microsoft Silverlight

Answered Question DataForm: Problem Binding to nested objectsRSS Feed

(0)

Leonid Andruhin
Leonid A...

Member

Member

636 points

137 Posts

DataForm: Problem Binding to nested objects

Hi, I've got a problem using DataForm. When binding to nested object and setting CommandButtonsVisibility to "Commit,Cancel", the commit button is always Disabled when changing SubEntity, and becomes Enabled when changing some property of entity. Both, entity and subentity realise INotifipropertyChanged event. I can send sample which demonstrates this issue.

BaseEntity:

using System.ComponentModel;

namespace DataFormTest

{

public abstract class BaseEntity : INotifyPropertyChanged

{

protected void OnPropertyChanged(string sPropertyName)

{

if (PropertyChanged != null)

PropertyChanged(this, new PropertyChangedEventArgs(sPropertyName));

}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion

}

}

Here is SubEntity 

namespace DataFormTest

{

public class SubEntity : BaseEntity

{

private string _sProperty;public string sProperty

{

get

{

return _sProperty;

}

set

{

if (_sProperty == value)

return;

_sProperty = value;base.OnPropertyChanged("sProperty");

}

}

}

}

MainEntity:

using System.ComponentModel;

using System.Windows;

namespace DataFormTest

{

public class Entity : BaseEntity, IEditableObject

{

private string _sMainProperty;public string sMainProperty

{

get

{

return _sMainProperty;

}

set

{

if (_sMainProperty == value)

return;

_sMainProperty = value;base.OnPropertyChanged("sMainProperty");

}

}

private SubEntity _oSubEntity;public SubEntity oSubEntity

{

get

{

return _oSubEntity;

}

set

{

if (_oSubEntity == value)

return;

_oSubEntity = value;base.OnPropertyChanged("oSubEntity");

}

}

#region IEditableObject Memberspublic void BeginEdit()

{

MessageBox.Show("BeginEdit");

}

public void CancelEdit()

{

MessageBox.Show("CancelEdit");

}

public void EndEdit()

{

MessageBox.Show("EndEdit");

}

#endregion

}

}

MainPage.Xaml:

<UserControl xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit" x:Class="DataFormTest.MainPage"

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"

mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

<Grid x:Name="LayoutRoot">

<dataFormToolkit:DataForm CurrentItem="{Binding}" CommandButtonsVisibility="Commit,Cancel">

<dataFormToolkit:DataForm.EditTemplate>

<DataTemplate>

<StackPanel>

<dataFormToolkit:DataField>

<TextBox Text="{Binding sMainProperty, Mode=TwoWay}"/>

</dataFormToolkit:DataField>

<dataFormToolkit:DataField>

<TextBox Text="{Binding oSubEntity.sProperty, Mode=TwoWay}"/>

</dataFormToolkit:DataField>

</StackPanel>

</DataTemplate>

</dataFormToolkit:DataForm.EditTemplate>

</dataFormToolkit:DataForm>

</Grid>

</UserControl>

and finally change app.startup:

private void Application_Startup(object sender, StartupEventArgs e)

{

Entity oEntity = new Entity();
MainPage mp = new MainPage();

mp.DataContext = oEntity;

this.RootVisual = mp;

}

Leonid Andruhin
Leonid A...

Member

Member

636 points

137 Posts

Re: DataForm: Problem Binding to nested objects

And 2 extra issues:

If I change the SubEntity property and press TAB button - cancelEdit fires, but if I get focus on MainPropery using mouse or Shift + TAB - behavior is normal

And second: error handlig issue.

Change Code of SubEntity:

public class SubEntity : BaseEntity

{

private string _sProperty;public string sProperty

{

get

{

return _sProperty;

}

set

{

if (string.IsNullOrEmpty(value))

throw new ArgumentNullException("value");

if (_sProperty == value)

return;

_sProperty = value;base.OnPropertyChanged("sProperty");

}

}

}

Change SubEntity property 2 times - first enter valid string (just not empty) and then clear value - you'll see Validation summary, but double click on error will not focus on control with incorrect input, it will also fire CancelEdit.

Is it right forum?

Leonid Andruhin
Leonid A...

Member

Member

636 points

137 Posts

Re: DataForm: Problem Binding to nested objects

Hi, is it anybody alive here? Or I need to post bug reports for codeplex?

hectorgar
hectorgar

Member

Member

203 points

87 Posts

Re: DataForm: Problem Binding to nested objects

 Hey Leonid, I having the same problem than you. Are you found the solution?

Im really need to bind nested objects

hectorgar
hectorgar

Member

Member

203 points

87 Posts

Re: DataForm: Problem Binding to nested objects

I try to bind the data programmatically (as an intent of workaround) in the datyaform's contentLoaded event, and the result is the same, the commit button is disabled.

private void editCompanyForm_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
        {
            ((TextBox)editCompanyForm.FindNameInContent("txtCompanyName")).Text = "name";

        }

 

Regis Brid
Regis Brid

Member

Member

42 points

6 Posts

Answered Question

Re: DataForm: Problem Binding to nested objects

Unfortunately this release of the DataForm control does not support editing nested objects out of the box. This is a functionality that is on our radar for a future release. I spent some time trying to find a solution that might accommodate you.

First, I modified the entity classes as follows:

    public class SubEntity : BaseEntity
    {
        private string _sProperty;
        private bool _bProperty;

        public SubEntity()
        {
        }

        internal SubEntity(SubEntity se)
        {
            _sProperty = se.sProperty;
            _bProperty = se.bProperty;
        }

        public bool bProperty
        {
            get
            {
                return _bProperty;
            }
            set
            {
                if (_bProperty == value)
                    return;
                _bProperty = value;
                base.OnPropertyChanged("bProperty");
            }
        }

        public string sProperty
        {
            get
            {
                return _sProperty;
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("value");
                if (_sProperty == value)
                    return;
                _sProperty = value;
                base.OnPropertyChanged("sProperty");
            }
        }

        internal void RefreshProperty(string propertyName, object propertyValue)
        {
            if (propertyName == "sProperty")
                _sProperty = propertyValue as string;
            else
                _bProperty = (bool) propertyValue;
        }
    }

    public class Entity : BaseEntity, IEditableObject
    {
        struct EntityData
        {
            internal string _sMainProperty;
            internal bool _bMainProperty;
            internal SubEntity _oSubEntity;
        }

        private EntityData _backupData, _data;
        private bool _bIsEditing;

        public Entity()
        {
            _data = new EntityData();
            _data._oSubEntity = new SubEntity();
        }

        public bool bMainProperty
        {
            get
            {
                return _data._bMainProperty;
            }
            set
            {
                if (_data._bMainProperty == value)
                    return;
                _data._bMainProperty = value;
                base.OnPropertyChanged("bMainProperty");
            }
        }

        public string sMainProperty
        {
            get
            {
                return _data._sMainProperty;
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("value");
                if (_data._sMainProperty == value)
                    return;
                _data._sMainProperty = value;
                base.OnPropertyChanged("sMainProperty");
            }
        }
       
        public SubEntity oSubEntity
        {
            get
            {
                return _data._oSubEntity;
            }
            set
            {
                if (_data._oSubEntity == value)
                    return;
                _data._oSubEntity = value;
                base.OnPropertyChanged("oSubEntity");
            }
        }

        public void RefreshSubEntity(string propertyName, object propertyValue)
        {
            SubEntity se = new SubEntity(this.oSubEntity);
            se.RefreshProperty(propertyName, propertyValue);
            this.oSubEntity = se;
        }

        #region IEditableObject Members

        public void BeginEdit()
        {
            if (!this._bIsEditing)
            {
                this._backupData = this._data;
                this._backupData._oSubEntity = new SubEntity(this._data._oSubEntity);
                this._bIsEditing = true;
            }
        }

        public void CancelEdit()
        {
            if (this._bIsEditing)
            {
                this._data = this._backupData;
                this._bIsEditing = false;
            }
        }

        public void EndEdit()
        {
            if (this._bIsEditing)
            {
                this._backupData = new EntityData();
                this._backupData._oSubEntity = new SubEntity();
                this._bIsEditing = false;
            }
        }

        #endregion
    }
}

My XAML looks like this:

  <Grid x:Name="LayoutRoot">
        <dataFormToolkit:DataForm x:Name="df" CurrentItem="{Binding}" CommandButtonsVisibility="Commit,Cancel">
            <dataFormToolkit:DataForm.EditTemplate>
                <DataTemplate>
                    <StackPanel>
                        <dataFormToolkit:DataField>
                            <TextBox x:Name="txtMainProperty" Text="{Binding sMainProperty, Mode=TwoWay}"/>
                        </dataFormToolkit:DataField>
                        <dataFormToolkit:DataField>
                            <CheckBox x:Name="chkMainProperty" IsChecked="{Binding bMainProperty, Mode=TwoWay}"/>
                        </dataFormToolkit:DataField>
                        <dataFormToolkit:DataField>
                            <TextBox x:Name="txtProperty" Text="{Binding oSubEntity.sProperty, Mode=TwoWay}"/>
                        </dataFormToolkit:DataField>
                        <dataFormToolkit:DataField>
                            <CheckBox x:Name="chkProperty" IsChecked="{Binding oSubEntity.bProperty, Mode=TwoWay}"/>
                        </dataFormToolkit:DataField>
                    </StackPanel>
                </DataTemplate>
            </dataFormToolkit:DataForm.EditTemplate>
        </dataFormToolkit:DataForm>
    </Grid>

And finally, the code behind looks like this:

    public partial class MainPage : UserControl
    {
        TextBox txtProperty;
        CheckBox chkProperty;

        public MainPage()
        {
            InitializeComponent();
            this.df.ContentLoaded += new System.EventHandler(df_ContentLoaded);
        }

        private void df_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
        {
            if (txtProperty != null)
            {
                txtProperty.TextChanged -= new TextChangedEventHandler(txtProperty_TextChanged);
            }
            txtProperty = this.df.FindNameInContent("txtProperty") as TextBox;
            if (txtProperty != null)
            {
                txtProperty.TextChanged += new TextChangedEventHandler(txtProperty_TextChanged);
            }

            if (chkProperty != null)
            {
                chkProperty.Click -= new System.Windows.RoutedEventHandler(chkProperty_Click);
            }
            chkProperty = this.df.FindNameInContent("chkProperty") as CheckBox;
            if (chkProperty != null)
            {
                chkProperty.Click += new System.Windows.RoutedEventHandler(chkProperty_Click);
            }
        }

        private void chkProperty_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            Entity entity = this.chkProperty.DataContext as Entity;
            entity.RefreshSubEntity("bProperty", chkProperty.IsChecked);
        }

        private void txtProperty_TextChanged(object sender, TextChangedEventArgs e)
        {
            Entity entity = this.txtProperty.DataContext as Entity;
            entity.RefreshSubEntity("sProperty", txtProperty.Text);
        }
    }

This seems to work fine, but it did require the addition of a public method Entity.RefreshSubEntity(...), and new application code. I tried to treat the inner class as if it were a simple type rather than a complex object.  It is far from being ideal. No doubt there are improvements we could make in the DataForm to support this scenario out of the box, especially if the inner sub-class implemented IEditableObject as well, or a richer interface. In the meantime, this is the best I could come up with for now.

Hope it helps.

-Regis [MSFT]

Leonid Andruhin
Leonid A...

Member

Member

636 points

137 Posts

Re: DataForm: Problem Binding to nested objects

Regis Brid, thank for your solution, it semms to work fine.

But I don't like codebehind in my view, so i changed dataform to autoEdit, removed all buttons, and added my own Buttons, using attached property, which enables ICommand interface. So, validation works fine, properties of viewmodel change and commit/cancel I've wrote myself

steyoung
steyoung

Member

Member

56 points

32 Posts

Re: DataForm: Problem Binding to nested objects

I've hit this issue too. Is there any update on a possible fix ?

Thanks, Steve

  • Unanswered Question
  • Answered Question
  • Announcement
Microsoft Communities