Laden...

Warum wird DataSet.HasChanges() = true von DataBinding auf eigenem Property

Erstellt von citizen.ron vor 16 Jahren Letzter Beitrag vor 16 Jahren 1.701 Views
citizen.ron Themenstarter:in
432 Beiträge seit 2005
vor 16 Jahren
Warum wird DataSet.HasChanges() = true von DataBinding auf eigenem Property

Hallo zusammen,

Topic
ich habe ein eigenes datengebundenes Label erstellt, das mir (u.a.) erlauben soll, Werte einer Datenbindung formatiert darzustellen und die Datenbindung auf einen übergeordneten IDataContainer zusetzen, aus dessen ggf. mehreren BindingSources ich über die Properties "TableName" und "ColumnName" den genaue Datenbezug spezifizieren kann:


/*———————————————————————————————————————————————————————————————————————————————————————————————————————————————————*/
/* DBLabel                                                                                                           */
/*———————————————————————————————————————————————————————————————————————————————————————————————————————————————————*/
/* A Label inheritant providing data features for use with the Casa Application Framework (CAF)                      */
/*———————————————————————————————————————————————————————————————————————————————————————————————————————————————————*/
// Created  :  Sept. 2006, cr
// Modified :  
/*———————————————————————————————————————————————————————————————————————————————————————————————————————————————————*/
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace CCL.Common
{
// DBLabel
   /// <summary>
   /// A Label inheritant providing data features for use with the Casa Application Framework (CAF)
   /// </summary>
   [System.Drawing.ToolboxBitmap(typeof(DBLabel), "Resources.DBLabel.bmp")]
   public class DataLabel : Label
   {

   #region Members
      private IDataContainer     dataContainer;
   #endregion

   #region Property-bound members
      private string     _TableName     = string.Empty;
      private string     _ColumnName    = string.Empty;
      private string     _Format        = string.Empty;
   #endregion

   #region Properties

   // Tablename
      /// <summary>
      /// Gets or sets the data table name when this textbox is data bound 
      /// </summary>
      [Category("Daten")]
      [Description("Defines the underlying data table when this textbox is data bound")]
      public string TableName
      {
         get { return _TableName; }
         set { _TableName = (value == null) ? string.Empty : value;  }
      }

   // ColumnName
      /// <summary>
      /// Gets or sets the data column when this textbox is data bound 
      /// </summary>
      [Category("Daten")]
      [Description("Defines the underlying data column when this textbox is data bound")]
      public string ColumnName
      {
         get { return _ColumnName; }
         set 
         { 
            _ColumnName = (value == null) ? string.Empty : value; 
            if ((DesignMode) && (_ColumnName != null)) Text = "<" + _ColumnName + ">";
         }
      }

   // DBValue
      /// <summary>
      /// Gets or sets the data value of the control
      /// </summary>
      [Category("Daten")]
      [Bindable(true, BindingDirection.OneWay)]
      [Description("Defines the underlying data value of the control")]
      public object DBValue
     {
         get { return base.Text; }
         set 
         { 
				if (value == null || value == DBNull.Value)
               base.Text = string.Empty; 
            else
            {
               if (_Format != string.Empty) 
                  base.Text = String.Format("{0:" + _Format + "}", value);
               else
                  base.Text = value.ToString(); 
            }
         }
     }

   // Format
      /// <summary>
      /// Gets or sets the format string of the displayed value
      /// </summary>
      [Browsable(true)]
      [Category("Daten")]
      [Description("Gets or sets the format string of the displayed value")]
      public string Format
      {
         get { return _Format; }
         set { _Format = value; }
      }

   #endregion

   #region .ctor
      public DataLabel() : base()
      {
         BackColor = Color.Transparent;
         if ((DesignMode) && (_ColumnName != null)) Text = "<" + _ColumnName + ">";
      }
   #endregion

   #region Overrides
   // CreateHandle
      /// <summary>
      /// Creates the control´s handle and connects to microkernel in runtime mode
      /// </summary>
      protected override void CreateHandle()
      {
         base.CreateHandle();
         
         if (!DesignMode) 
         {
            dataContainer = FindDataContainer();
            if (dataContainer != null)
               dataContainer.DataBindingChanged += new EventHandler(OnDataBindingChanged);
         }
      }
   #endregion

   #region Event handling
      void OnDataBindingChanged(object sender, EventArgs e)
      {
      // Relink data bindings after business context has changed
         if ((dataContainer != null) && (_TableName != string.Empty) && (_ColumnName != string.Empty)) try
         { 
            DataBindings.Clear();
            DataBindings.Add(dataContainer.GetBinding("DBValue", _TableName, _ColumnName));
         }
         catch (Exception exc)
            { dataContainer.Throw(exc); }
      }
   #endregion

   #region Methods
      private IDataContainer FindDataContainer()
      {
         Control result = this;
         Form ownerForm = FindForm();
         while (result != ownerForm)
         {
            result = result.Parent;
            if (result is IDataContainer)
               break;
         }
         return result as IDataContainer;
      }
   #endregion
   }
}

Problem
Das Problem ist, dass durch die Bindung an das eigene Property "DBValue" offensichtlich eine Änderung der Daten stattfindet, denn bereits nach einer Navigation der BindingSource zum nächsten Datensatz liefert DataSet.HasChanges() = true.

Meine Tests:
Wenn ich die Bindung auf das Text Property setze, findet diese Änderung nicht statt:


void OnDataBindingChanged(object sender, EventArgs e)
{
// Relink data bindings after business context has changed
   if ((dataContainer != null) && (_TableName != string.Empty) && (_ColumnName != string.Empty)) try
   { 
      DataBindings.Clear();
      DataBindings.Add(dataContainer.GetBinding("Text", _TableName, _ColumnName));
   }
   catch (Exception exc)
      { dataContainer.Throw(exc); }
}

Witzigerweise gilt das auch dann, wenn ich das Property überschreibe und mit dem Code aus meinem eigenen Property "DBValue" fülle:


public override string Text
{
   get
   {
      if ((DesignMode) && (_ColumnName != null))
         return "<" + _ColumnName + ">";
      else
         return base.Text;
   }
   set 
   {
      if ((DesignMode) && (_ColumnName != null))
         base.Text = "<" + _ColumnName + ">";
      else
      { 
         if (_Format != string.Empty) 
            base.Text = String.Format("{0:" + _Format + "}", value);
         else
            base.Text = value.ToString(); 
      }
   }
}

De facto sollte das jetzt identisch sein, außer dass ich ein eigenes Property namens "DBValue" definiert habe, das aber doch exakt das gleiche macht und obendrein die Bindungsrichtung auf "einseitig" festlegt - ich bin ratlos.

**Hintergrund **
Es handelt sich hierbei um nur ein Control unter vielen anderen, die alle eine Schnittstelle namens IDBControl unseres Frameworks implementieren und daher das Property DBValue erforderlich ist.

Bin für jeden Denkanstoß dankbar,
thanx
ron

4.221 Beiträge seit 2005
vor 16 Jahren

Setze mal im DBValue - Property im get und im set einen Brakepoint.

Früher (1.1) war es so, dass nach jedem set ein get abläuft... und wenn der an den set übergebene Wert nicht dem Wert aus get entspricht hast Du einen Change.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

citizen.ron Themenstarter:in
432 Beiträge seit 2005
vor 16 Jahren

hi programmierhans,

die werte stimmen ja aber überein - das problem scheint irgendwas mit persistenz und validierung zu tun zu haben.

gefunden habe ich schon einige internationale threads dazu, aber keiner hat eine befriedigende antwort; folgende funktioniert bei mir z.B. leider nicht:
http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic36268.aspx

Most likely because there is no similar named Changed event, so the property
value always get's persisted at Control.Validating. And a DataRow doesn't
care if it's the same value, if a field is set then it's marked as changed.

Add a changed event to your UserControl ...

With a Changed event, it will persist the value at Control.Validating but
only if the Changed event has fired before the Validating event.

Manche gehen in der Tat von einem Bug aus und programmieren eigene Prüfmethoden, um auf Tabellenebene ein HasChanges(string Tablename) zu machen, indem Sie in allen Tabellen des DataSets den aktuellen Datensatz und seine RowVersion angucken - das kann ja wohl nicht sein...

Ich suche mich jetzt schon seit dem ersten Post heute morgen durch diesen sch.... und bin schon am Durchdrehen 🙁

4.221 Beiträge seit 2005
vor 16 Jahren

So nun noch alles Zusammengefasst:

Du muss für Dein Beispiel noch folgendes implementieren:

Einen Event mit Namen: DBValueChanged
Ein bool Property mit Namen: DBValueIsNull

Dann sollte es funzen.

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

G
84 Beiträge seit 2007
vor 16 Jahren

Ich befürchte es handelt sich hier tatsächlich um einen Bug.

Ist schon bisi länger her, da hatte ich massiv Probleme mit HasChanges() und RowState() von DataSets.

Ausführliche Tests und Recherchen haben mich am Ende auch zu dem Schluss kommen lassen, dass es sich um einen Bug handelt.

Vgl: HasChanges() Bug?

Ich hab das Problem dann für mich mit einem häßlichen, dreckigen und unbefriedigenden Workaround gelöst 😉

citizen.ron Themenstarter:in
432 Beiträge seit 2005
vor 16 Jahren

hi ihr lieben helfer

hab auch nochmal mit den eventnamen herumexperimentiert und tatsächlich: DBValueChanged passt!

Tausend Dank für Eure Unterstützung.

Die Erklärung habe ich allerdings noch nicht so richtig verstanden...?

Gruß
ron

4.221 Beiträge seit 2005
vor 16 Jahren

Das mit dem DBValueIsNull musste aber auch noch implementieren... sonst kommt das Problem wieder auf Dich zu wenn Du mit DBNullValues arbeitest.

Mit der Erklärung ist es so eine Sache.... nimm es einfach hin... es ist nun mal so 🙂

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

F
10.010 Beiträge seit 2004
vor 16 Jahren

Erklärung ist auch recht einfach.

Per convention muss das ChangedEvent genauso heissen wie das Property,

  • eben Changed.

Oder Du implementierst INotifyPropertyChanged.
Das gibt es seit FW 2.0 und ist natürlich viel einfacher.