C# WPF - Databinding kódból, converter-rel

C# WPF - Databinding kódból, converter-rel
2013-02-14T11:49:29+01:00
2013-02-14T19:58:11+01:00
2022-11-28T06:50:50+01:00
Scrage
Üdv!

Létezik egy osztályom, Cell.cs, ebből példányosítok elemeket egy másik osztályban (LifeSimulation.cs) egy kétdimenziós tömböt:

private Cell[,] grid;

A Cell típusú objektum egy bool típusú state taggal rendelkezik. A LifeSimulation osztályban a grid mátrixot 2 for ciklussal feltöltöm a következőképpen:

//GridSize a mátrix méretét meghatározó property. for (int i = 0; i < GridSize; i++) { for (int j = 0; j < GridSize; j++) { //A konstruktorban egy bool típust fogad el a Cell, a DEAD konstans értéke false. Ez lesz az objektum state privát tagjának az értéke. grid[i, j] = new Cell(DEAD); } }

Létrejön így tehát egy 2 dimenziós tömb, elemei Cell osztálybeli objektumok, amiknek van bool típusú state-jük.
A MainWindow.xaml-ben van egy wrappanel, annak elemeit kódból adom meg. A MainWindow.xaml.cs konstruktora előtt meghatározom a wrappanel elemeinek számát a cellCapacityNumber-el, ekkora lesz a grid tömb mérete is. Példányosítom a LifeSimulation osztályból a simulation objektumot:

//Meghatározza, hányszor hanyas lesz a sejttér mérete. LifeSimulation példányosítása. const int cellCapacityNumber = 20; LifeSimulation simulation;

Ezután a MainWindow.xaml.cs konstruktorában feltöltöm annyi rectangle-el a wrappanel-t, ahány eleme van a grid mátrixnak. A létrehozott rectangle-ök Fill property-jéhez adatkötéssel kötöm kódból a simulation objektumban létező grid tömb elemeinek (a Cell objektumoknak) a state tulajdonságait. Tehát a for ciklusban sorra veszi az indexer-rel (ami a grid-ben a soron következő elemre mutat, mintha sorfolytonos tömb lenne) a grid tömb elemeit, megnézi, az elemnek a state-je és adatköti a rectangle Fill proprety-jéhez, átengedve egy converter-en:

public MainWindow() { InitializeComponent(); simulation = new LifeSimulation(cellCapacityNumber, true); wrapPanelGrid.ItemHeight = wrapPanelGrid.Height / cellCapacityNumber; wrapPanelGrid.ItemWidth = wrapPanelGrid.Width / cellCapacityNumber; for (int i = 0; i < simulation.GridSize * simulation.GridSize; i++) { Rectangle cell = new Rectangle(); cell.Name = "cell" + i.ToString(); cell.StrokeThickness = 1; cell.Stroke = Brushes.Gray; //Databinding. Az adatkötés. Binding cellColourBinding = new Binding("State"); cellColourBinding.Source = simulation[i].State; cellColourBinding.Converter = new CellStateConverter(); cell.SetBinding(Rectangle.FillProperty, cellColourBinding); //A négyzet hozzáadása a wrappanel-hez. wrapPanelGrid.Children.Add(cell); } }

A converter a CellStateConverter.cs osztályból van példányosítva, ami a következőképpen néz ki:

class CellStateConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (true) { bool state = (bool)value; return state ? Brushes.Black : Brushes.White; } else { throw new NotImplementedException(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
Ha a Cell objektum state-jének értéke false, akkor ilyen színű Brush-t adna vissza a Convert metódus, ha false, amolyan színűt.

Szeretném a közösség segítségét kérni, mert debug-olás folyamán a wrappanel elemeinek mindegyike kitöltés nélkül jön létre. A rectangle Fill property-je null referenciát kap mindegyik esetben. Rossz a databind, de nem tudom, miért rossz. Sokat kutattam már a neten, nem tudok már mástól sem segítséget kapni.
Előre is hálásan köszönöm, ha a legkisebb segítség is érkezik a problémámra!
Mutasd a teljes hozzászólást!
Szia,

próbáld, meg hogy ezt a sort kicseréled
Binding cellColourBinding = new Binding("State");

erre:
Binding cellColourBinding = new Binding();

Mert te most közvetlenül a Source-hoz szeretnél kötni, nem pedig egy objektum tulajdonságához, és a Binding paramétere rögtön beállítaná a Path-t.
Mutasd a teljes hozzászólást!

  • Szia!
    Köszönöm, hogy végigolvastad és energiát fordítottál a kérdésre! Ez a megoldás, így már működik.:) Azt, hogy pontosan mit akart a kód csinálni úgy, ahogy először írtam, nem értem pontosan. Keresett egy "State" azonosítóval ellátott resource után app szinten? Én azt gondoltam, hogy a "State" paraméterként a property nevét adom meg és egy sorral alább kapja meg az objektumot, aminek a property-jei közt kell keresnie azt.
    Mindenesetre jó lecke volt, még egyszer köszönöm a segítségedet! :)
    Mutasd a teljes hozzászólást!
  • Még egy kérdés:

    Ha itt egy DispatcherTimer tick eseményében egy metódus újra és újra módosítja annak a Cell típusú grid-nek az elemeit, pontosabban az elemek State property-jét, valós időben nem változik a wrappanel-en a négyzetek színe. INotifyPropertyChange-et kellene használni valahogy? Azt gondoltam, hogy az adatkötés miatt állandóan frissülni fog az adott rectangle Fill property-je, ha a hozzá kötött Cell grid[,]-beli elem State property-je változik.
    Megjegyzés:
    Beállítottam még egyirányúvá a binding-ot:

    Binding cellColourBinding = new Binding(); cellColourBinding.Source = simulation[i].State; cellColourBinding.Converter = new CellStateConverter(); cellColourBinding.Mode = BindingMode.OneWay; cell.SetBinding(Rectangle.FillProperty, cellColourBinding);
    Mutasd a teljes hozzászólást!
  • Szia,

    Két megoldása is lehet ennek a problémának, az első, hogy az adatkötés a bindingnak megadott Source alapján működik, ez az amit elfogadtál. Ebben az esetben a logikai értéket a bindingnak adtad meg, ekkor nem kell Path-t megadni, ez abban az esetben szükséges, ha egy olyan objektumot kötsz, aminek vannak tulajdonságai, és az objektum valamely tulajdonságát akarod kötni.

    Úgy is működhet, ahogy szeretted volna, ekkor meg kell adnod a Path-t (az objektumod tulajdonságának a neve), viszont ebben az esetben a Sourceod, nem maga a tulajdonság, hanem az objektum kell, hogy legyen.
    Binding cellColourBinding = new Binding("State"); cellColourBinding.Source = simulation[i];

    Továbbá az osztályodnak meg kell valósítania az INotifyPropertyChange interface-t.
    Mutasd a teljes hozzászólást!
  • A binding path és source része akkor már világos, köszönöm.
    Jelenleg nem valósítja meg a Cell osztály az INotifyPropertyChange interface-t, ez az oka annak, hogy amikor egy elemnek a grid tömbben változik a State property-je, nem változik az annak az elemnek megfeleltetett rectangle színe a wrappanel-en?

    Próbáltam ráhúzni a Cell osztályra az INotifyPropertyChange-et, a LifeSimulation osztályban pedig implementálni a PropertyChange eseményt, de szembesültem egy problémával:
    Amikor változik a grid mátrixban egy elem State property-je, kiváltódik az event, de arról közvetlenül nem lesz tudomásom, hogy pontosan melyik elem váltotta ki, hogy ettől melyik rectangle Fill property-jét változtassam. Nem tudom, érthető-e így, mi a problémám... Azt kell elérnem, hogy valahányszor változik annak a 2 dimenziós tömbben valamely elemének egy property-je, akkor az annak az elemnek megfelelő Rectangle objektum Fill property-je is frissüljön a wrappanel-ben.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd