Az adatkötés használata során elég gyakran kell egy tetszőleges elem értékét több - egymástól különböző típusú, vagy különböző helyen deklarált - tulajdonság értéke alapján megállapítani.
Noha erre lehet mögöttes kódot írni, ami mindig figyeli mi történik éppen a felhasználói felületen, sokkal egyszerűbb megoldás is létezik a probléma megoldására.
Legyen adott egy cég különböző termékekkel és azok éves bevételivel.
A program ezen adatokat egy listában jelenítse meg.
Kérje be továbbá az éppen aktuális Euro árfolyamot és az összbevételt számítsa ki Euroban.
A felhasznált Model tartalmazza a termék nevét és az éves bevételt.
class Model : INotifyPropertyChanged { private string _productName; public string ProductName { get { return _productName; } set { _productName = value; OnPropertyChanged("ProductName"); } } private decimal _income; public decimal Income { get { return _income; } set { _income = value; OnPropertyChanged("Income"); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } // --------------------------------------------------------------------- public Model(string productName, decimal income) { ProductName = productName; Income = income; } }
A kapcsolódó ViewModel egy gyűjteményben tárolja a cég termékeit, valamint a ViewModel-ben kerül tárolásra az éppen aktuális Euro értéke.
A gyűjtemény 3 termékkel kerül feltöltésre a konstruktorban:
class ViewModel : INotifyPropertyChanged { private ObservableCollection<Model> _myModel; public ObservableCollection<Model> MyModel { get { return _myModel; } set { _myModel = value; OnPropertyChanged("MyModel"); } } private decimal _euroValue; public decimal EuroValue { get { return _euroValue; } set { _euroValue = value; OnPropertyChanged("EuroValue"); } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } public ViewModel() { MyModel = new ObservableCollection<Model> { new Model("Product1", 10000), new Model("Product2", 55891), new Model("Product3", 158689) }; EuroValue = 305.23m; } }
A View kódja a már megszokott egyszerűséget hozza
public partial class MainWindow : Window { private readonly ViewModel _viewModel; public MainWindow() { InitializeComponent(); _viewModel = new ViewModel(); DataContext = _viewModel; } }
A View-ban megjlenítésre kerül egy lista, amiben az egyes termékek nevei illetve az éves bevételük látható. A Lista megjelenítése a Grid.Resource-ba lévő DataTemplate-ben található.
A View-ban található továbbá az éppen aktuális Euro bevitelére szolgáló input mező, valamint legvégül az átváltott összeg, ami jelen esetben egyenlőre egy nagy kérdőjel.
<Grid> <Grid.Resources> <DataTemplate x:Key="MyListTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Width="100" Text="{Binding ProductName}"/> <TextBlock Width="100" Text="{Binding Income}"/> </StackPanel> </DataTemplate> </Grid.Resources> <StackPanel Orientation="Vertical"> <ListBox ItemsSource="{Binding MyModel}" ItemTemplate="{StaticResource MyListTemplate}"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="Euro: "/> <TextBox Text="{Binding EuroValue}" Width="100"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Teljes bevétel Euroban: "/> <TextBlock Text="?"/> </StackPanel> </StackPanel> </Grid>
A nyilvánvaló nagy kérdés az, hogy mi kerül a kérdőjel helyére? A feladat egyértlemű. Add össze a bevételeket majd a kapott értéket az aktuális Euro értékével osztani kell.
Ehhez két input paraméter kell, amelyek a ViewModel osztályban találhatóak.
A gyűjtemény, amely tartalmazza az összes terméket (MyModel)
Valamint az Euro aktuális értéke. (EuroValue)
A fenti számítás egy külön osztályban kerül kiértékelésre.
class EuroConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (values != null && values.Length == 2) { var collection = values[0] as ObservableCollection<Model>; var euroValue = (decimal)values[1]; var totalIncome = collection.Sum(model => model.Income); var incomeInEuro = totalIncome/euroValue; return incomeInEuro.ToString(); } return Binding.DoNothing; } // ******************************************************************** public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { return null; } }
Az osztálynak muszáj megvalósítania az IMultiValueConverter interface-t.
A számításhoz fontos műveletek a Convert metódusban találhatóak.
A bejövő pareméter immáron egy tömb (object[] values) amely tartalmazza a gyűjteményt, valamint az Euro értékét.
A kódot közelebbről megvizsgálva látható, hogy a tömb eleminek feltöltésekor ügyelni kell a sorrendre. Jelen esetben a program elvárja, hogy a gyűjtemény legyen a tömb első és az Euro értéke a tömb második eleme.
Az összeg kiszámítása után azt stringként vissza kell adni a TextBlock részére.
A View több ponton is változik.
Be kell vezetni egy új namespace-t, ami attól függ, hogy a projekt (de leginkább az EuroConverter osztály) milyen namespace alatt található.
az új sor így kell, hogy kinézzen:
xmlns:multiConverter="clr-namespace:A TE NAMESPACE ELNEVEZÉSED"
A Grid.Resource is bővítésre szorul, végső formája az alábbiakban található
<Grid.Resources> <DataTemplate x:Key="MyListTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Width="100" Text="{Binding ProductName}"/> <TextBlock Width="100" Text="{Binding Income}"/> </StackPanel> </DataTemplate> <multiConverter:EuroConverter x:Key="EuroMultiConverter"/> </Grid.Resources>
A Konverterre a Grid-en belül a továbbiakban az EuroMultiConverter névvel lehet hivatkozni.
Legvégül maga az adatkötés:
<StackPanel Orientation="Horizontal"> <TextBlock Text="Teljes bevétel Euroban: "/> <TextBlock Width="100"> <TextBlock.Text> <MultiBinding Converter="{StaticResource EuroMultiConverter}"> <Binding Path="MyModel"/> <Binding Path="EuroValue"/> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel>
Mivel a végső érték több bemeneti érték kiszámításával kerül kiértékelésre, ezért MultiBinding adatkapcsolást kell végrehajtani. A konverter az előbb meghatározott EuroMultiConverter.
A paraméterek pedig abban a sorrendben kerülnek átadásra, ahogy azt az EuroConverter osztály elvárja. Az első a gyűjtemény, a második az Euro értéke.
A végső View az alábbi:
<Window x:Class="Wpf07_MuliBinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:multiConverter="clr-namespace:Wpf07_MuliBinding" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.Resources> <DataTemplate x:Key="MyListTemplate"> <StackPanel Orientation="Horizontal"> <TextBlock Width="100" Text="{Binding ProductName}"/> <TextBlock Width="100" Text="{Binding Income}"/> </StackPanel> </DataTemplate> <multiConverter:EuroConverter x:Key="EuroMultiConverter"/> </Grid.Resources> <StackPanel Orientation="Vertical"> <ListBox ItemsSource="{Binding MyModel}" ItemTemplate="{StaticResource MyListTemplate}"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="Euro: "/> <TextBox Text="{Binding EuroValue, UpdateSourceTrigger=PropertyChanged}" Width="100"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="Teljes bevétel Euroban: "/> <TextBlock Width="100"> <TextBlock.Text> <MultiBinding Converter="{StaticResource EuroMultiConverter}"> <Binding Path="MyModel"/> <Binding Path="EuroValue"/> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel> </StackPanel> </Grid> </Window>
Az Euro érték megadására szolgáló TextBox adatkapcsolásának frissítését átállítottam (UpdateSourceTrigger=PropertyChanged), ezáltal abban a pillanatban kiszámításra kerül az új érték, ahogy egy billentyű lenyomásra kerül.
Nincsenek megjegyzések:
Megjegyzés küldése