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