2012-10-18 12 views
5

Zamierzam użyć powiązania danych między kilkoma z moich zajęć. Innymi słowy, nie wiążę wartości między klasą modelu a interfejsem użytkownika, ale wiążę zmienne między różnymi klasami.Jak wykonać powiązanie danych w kodzie za pomocą C#?

Przeczytałem o powiązaniu danych w języku C# w kilku miejscach, ale większość z nich odnosi się do powiązania między formularzem Windows i obiektem źródłowym.

Wciąż jestem nowy w C#. Jest to, jak rozumiem, co należy zrobić:

Po pierwsze, dla mojego źródłowego obiektu, powiedzmy ma nazwę klasy DataObject. Obiekt źródłowy musi zaimplementować interfejs INotifyPropertyChange, a następnie wywołać zdarzenie, gdy właściwość, health, zostanie zmieniona. Nie mam z tym problemu.

Załóżmy teraz, że mam obiekt docelowy o nazwie o nazwie CharacterClass. life jest własnością w CharacterClass, a także jest obiektem docelowym , który chcę powiązać z właściwością obiektu źródłowego: health.

W jaki sposób powiązać te dwie właściwości (zarówno jednokierunkowe, jak i dwukierunkowe) w kodzie za pomocą zwykłej platformy .NET?

Trochę podstawowych informacji o tym, dlaczego ja to pytanie:

Tylko w przypadku, to jest powielany pytanie, to nie jest. Przeszukałem SE. Pozostałe pytania dotyczące wiązania danych w kodzie są w kontekście WPF lub XAML, co nie jest dla mnie. Przeczytałem także kilka artykułów na temat MSDN i wydaje mi się, że mogę stworzyć obiekt Binding, a następnie powiązać źródło i cel poprzez BindingOperations.SetBinding(). Jednak klasa Binding wydaje się być częścią biblioteki WPF w obszarze nazw System.Windows.Data.Binding. Chociaż używam C#, wątpię, bym miał luksus dostępu do bibliotek WPF, ponieważ używam głównie języka C# jako języka skryptowego w Unity3D. Sądzę, że mam tylko dostęp do frameworka .Net. Ale nie jestem tego pewien, ponieważ wciąż jestem nowy w C#.

Odpowiedz

7

Chociaż istnieje wiele wsparcia dla powiązań, które są ściśle powiązane z wykorzystywaną strukturą interfejsu użytkownika, nadal można łatwo napisać własną ramkę wiążącą.

Oto POC, który implementuje jednostronne wiązanie między właściwościami dwóch obiektów.

Uwaga: Jest to tylko jeden z możliwych sposobów, w najlepszym przypadku POC (może wymagać dostrojenia do scenariusza wysokiej wydajności/produkcji) i wykorzystuje klasy .Net 2.0 i interfejsy bez zależności od jakiegokolwiek interfejsu użytkownika (" struktura waniliowa '.net w twoich słowach :)).Kiedy już to zrozumiał, można łatwo rozszerzyć ten wspierać wiążącą także

class Program 
{ 
    public static void Main() 
    { 
     Source src = new Source(); 
     Destination dst = new Destination(src); 
     dst.Name = "Destination"; 
     dst.MyValue = -100; 
     src.Value = 50; //changes MyValue as well 
     src.Value = 45; //changes MyValue as well 
     Console.ReadLine(); 
    } 
} 

//A way to provide source property to destination property 
//mapping to the binding engine. Any other way can be used as well 
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
internal class BindToAttribute : Attribute 
{ 
    public string PropertyName 
    { 
     get; 
     private set; 
    } 

    //Allows binding of different properties to different sources 
    public int SourceId 
    { 
     get; 
     private set; 
    } 

    public BindToAttribute(string propertyName, int sourceId) 
    { 
     PropertyName = propertyName; 
     SourceId = sourceId; 
    } 
} 

//INotifyPropertyChanged, so that binding engine knows when source gets updated 
internal class Source : INotifyPropertyChanged 
{ 
    private int _value; 
    public int Value 
    { 
     get 
     { 
      return _value; 
     } 
     set 
     { 
      if (_value != value) 
      { 
       _value = value; 
       Console.WriteLine("Value is now: " + _value); 
       OnPropertyChanged("Value"); 
      } 
     } 
    } 

    void OnPropertyChanged(string propertyName) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

internal class Destination 
{ 
    private BindingEngine<Destination> _binder; 

    private int _myValue; 

    [BindTo("Value", 1)] 
    public int MyValue 
    { 
     get 
     { 
      return _myValue; 
     } 
     set 
     { 
      _myValue = value; 
      Console.WriteLine("My Value is now: " + _myValue); 
     } 
    } 

    //No mapping defined for this property, hence it is not bound 
    private string _name; 
    public string Name 
    { 
     get 
     { 
      return _name; 
     } 
     set 
     { 
      _name = value; 
      Console.WriteLine("Name is now: " + _name); 
     } 
    } 

    public Destination(Source src) 
    { 
     //Binder for Source no 1 
     _binder = new BindingEngine<Destination>(this, src, 1); 
    } 
} 

internal class BindingEngine<T> 
{ 
    private readonly T _destination; 
    private readonly PropertyDescriptorCollection _sourceProperties; 
    private readonly Dictionary<string, PropertyDescriptor> _srcToDestMapping; 

    public BindingEngine(T destination, INotifyPropertyChanged source, int srcId) 
    { 
     _destination = destination; 

     //Get a list of destination properties 
     PropertyDescriptorCollection destinationProperties = TypeDescriptor.GetProperties(destination); 

     //Get a list of source properties 
     _sourceProperties = TypeDescriptor.GetProperties(source); 

     //This is the source property to destination property mapping 
     _srcToDestMapping = new Dictionary<string, PropertyDescriptor>(); 

     //listen for INotifyPropertyChanged event on the source 
     source.PropertyChanged += SourcePropertyChanged; 

     foreach (PropertyDescriptor property in destinationProperties) 
     { 
      //Prepare the mapping. 
      //Add those properties for which binding has been defined 
      var attribute = (BindToAttribute)property.Attributes[typeof(BindToAttribute)]; 
      if (attribute != null && attribute.SourceId == srcId) 
      { 
       _srcToDestMapping[attribute.PropertyName] = property; 
      } 
     } 
    } 

    void SourcePropertyChanged(object sender, PropertyChangedEventArgs args) 
    { 
     if (_srcToDestMapping.ContainsKey(args.PropertyName)) 
     { 
      //Get the destination property from mapping and update it 
      _srcToDestMapping[args.PropertyName].SetValue(_destination, _sourceProperties[args.PropertyName].GetValue(sender)); 
     } 
    } 
} 

enter image description here

0

uważam, że mam dostęp do ram .NET wanilia

rzeczywistość jest taka, że ​​powiązanie danych jest używane tylko w interfejsie użytkownika. Jeśli więc ktoś mówi o powiązaniu danych, automatycznie zakłada "powiązanie danych w [nazwa interfejsu użytkownika interfejsu]".

Można rozważyć mapowanie obiekt-obiekt zamiast powiązania danych.

+0

Właściwie tak, masz rację o wiążących byt dla interfejsu danych 2-drożny. W mojej sytuacji buduję interfejs użytkownika, ale nie za pomocą formularza Windows. Wszystkie te * docelowe obiekty *, o których mówiłem, to głównie moje klasy UI. Tak więc mam nadzieję na "niestandardowe" techniki wiązania danych sortowania, które mógłbym wdrożyć na moim własnym interfejsie użytkownika innego niż WinForm. – Carven

+0

@xEnOn: głównym problemem jest to, że silniki wiążące są ściśle powiązane z wewnętrznymi strukturami interfejsu użytkownika. Np. Powiązanie WPF używa właściwości zależności, więc jeśli chcesz go użyć, musisz napisać klasy interfejsu użytkownika, aby być potomkami 'DependencyObject'. Myślę, że powinieneś napisać swój własny silnik wiążący. – Dennis

+0

Rozumiem. Dzięki! Wygląda na to, że nie mam szczęścia, aby użyć istniejącego mechanizmu powiązania danych C#. :(Chyba muszę sprawdzić, w jaki sposób mogę napisać mój własny silnik wiążący. Może zadam tu nowe pytanie, żeby zrozumieć, jak silniki wiążące dane działają pod maską, żeby dać sobie pomysł, jak napisać powiązanie danych Bardzo dziękuję, Dennis. :) – Carven

Powiązane problemy