Az eddig megismertek felhasználásával már számos Model osztályt fel lehet építeni, ám rengeteg olyan WPF tulajdonság van, amit nem biztos, hogy célszerű az osztályban ugyanolyan típusúra deklarálni.
A legegyszerűbb talán egy adott elem Visibility tulajdonsága.
pl.: <Rectangle Visibility="Hidden"/>
A Visibility 3 értéket vehet fel: Hidden, Visible, Collapsed
Amennyiben kódból kell az értékét változtatni úgy az alábbiak szerint kell eljárni:
MyRectangle.Visibility = Visibility.Hidden;
A Most következő példában a "Collapsed" érték mellőzésre kerül. A láthatóságot egy bool tulajdonság segítségével lehet szabályozni. (true=Visible, false=Hidden)
Ehhez egy nagyon egyszerű Model osztályra lesz szükség:
class Model : INotifyPropertyChanged
{
private bool _rectVisibility;
public bool RectVisibility
{
get { return _rectVisibility; }
set { _rectVisibility = value; OnPropertyChanged("RectVisibility"); }
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// ---------------------------------------------------------------------
public Model(bool visibility)
{
RectVisibility = visibility;
}
}
A Láthatóságot a konstruktoron keresztül célszerű alaphelyzetbe hozni.
A hozzá tartozó ViewModel is pont ugyanennyire egyszerű
class ViewModel : INotifyPropertyChanged
{
private Model _myModel;
public Model MyModel
{
get { return _myModel; }
set { _myModel = value; OnPropertyChanged("MyModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModel()
{
MyModel = new Model(true);
}
}
A View kód:
public partial class MainWindow : Window
{
private readonly ViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new ViewModel();
DataContext = _viewModel;
}
}
Legvégül az alap XAML
<Window x:Class="Wpf06_Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Orientation="Horizontal" Height="30">
<Rectangle Width="100" Height="30" Fill="Red"/>
</StackPanel>
</Grid>
</Window>
Az egyenlőre egyetlen darab piros négyzet kirajzolására szolágló kód hamarosan kibővül számos új elemmel, amelyek mind-mind a négyzet láthatóságát fogják szabályozni.
Jelen pillanatban van egy Model osztály, amelynek egyetlen bool típusú publikus tulajdonsága van (RectVisibility). Ezt kell valamilyen módon összepárosítani a Rectangle Visibility tulajdonságával.
Ahhoz, hogy mindezt elérjük, egy konverter osztályra lesz szükség. Ez az osztályt egy külön DLL-ben lesz elhelyezve (ez nem szükséges).
Hozzunk létre új Class Library project-et. A neve legyen: Converter.
Töröld ki az automatikusan létrehozott class file-t, és adj a projekt-hez egy új osztályt VisibilityConverter néven.
A Library-hez az alábbi referenciákat kell hozzáadni (jobb klikk a References lenyíló menün, Add refference): PresentationCore, PresentationFrameWork
Az osztály kódja:
namespace Converter
{
public class VisibilityConverter : IValueConverter
{
// ** Model konvertálása az UI felé
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Hidden;
}
// ** UI konvertálása a Model felé
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}
A fenti namespace és osztály elnevezések pontosan egyezzenek! Természetesen a későbbiekben tetszőleges elnevezést lehet használni, de a példa futtatásához ez most nélkülözhetetlen.
Mivel az osztály másik szerelvényben található ezért az osztály kötelezően publikus, hogy a későbbiekben is elérhető maradjon.
Az osztály használatához szükség van az IValueConverter interface implementálására. Ehhez szükség van a System.Windows.Data namespace-re.
using System.Windows.Data;
Az interface két metódus meglétét követeli meg. A tényleges konvertálás ezeken keresztül történik.
public object Convert
public object ConvertBack
A Convert metódus konvertál minden a Model osztályban deklarált tulajdonságot olyan formára, ahogy azt az UI elvárja.
Jelen esetben egy bool tulajdonság a bement, és az alapján dől el a visszatérési érték, amely már feldolgozható a Rectangle Visibility tulajdonsága számára.
A WPF projekthez referenciaként hozzá kell adni az előbb létrehozott Converter szerelvényt.
A hozzáadás után érdemes egy fordítást végezni a kódon, mivel a Wpf számtalanszor nem találja meg elsőre a beimportált elemeket.
Az XAML kód pedig az alábbiak szerint változik.
Egy új xml namespace deklarációval a konvertert deklarálni kell.
xmlns:converter="clr-namespace:Converter;assembly=Converter"
Ezért volt fontos, hogy az namespace és osztály nevek egyezzenek a példában leírtakkal.
Ez a sor megmondja, hogy melyik DLL és melyik namespace kell a kód működéséhez a továbbiakban.
Következő lépésként a Resource-ban definiálni kell a konvertert.
<Grid.Resources>
<converter:VisibilityConverter x:Key="RectVisibilityConverter"/>
<converter:VisibilityConverter x:Key="RectVisibilityConverter"/>
</Grid.Resources>
A converter(az elöbb lett definiálva) a Converter namespace alatt található VisibilityConverter osztályra mutat, az x:Key pedig egy tetszőlegesen elnevezett hivatkozási nevet definiál.
A Rectangle az alábbiak szerint változik.
<Rectangle Width="100" Height="30" Fill="Red" Visibility="{Binding MyModel.RectVisibility, Mode=OneWay, Converter={StaticResource RectVisibilityConverter}}"/>
Az adatkapcsolás a Model osztály RectVisibility tulajdonságát használja.
A Mode=OneWay utasítja a keretrendszert, hogy az adatkapcsolás csak egyirányú, azaz csak a Model osztály irányából a Rectangle felé. (az ezt megelőzően egy textboxba írt szöveg tartalmát a framework bemásolta a Model osztály tulajdonságába)
A Converter a Grid.Resource-ban definiált konverterre mutat, ami a bool értéket alakítja át olyan típusra, amit a Rectangle Visibility tulajdonsága értelmezni tud.
Az XAML jelenleg itt tart:
<Window x:Class="Wpf06_Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:Converter;assembly=Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<converter:VisibilityConverter x:Key="RectVisibilityConverter"/>
</Grid.Resources>
<StackPanel Orientation="Horizontal" Height="30">
<Rectangle Width="100" Height="30" Fill="Red" Visibility="{Binding MyModel.RectVisibility, Mode=OneWay, Converter={StaticResource RectVisibilityConverter}}"/>
</StackPanel>
</Grid>
</Window>
Lefordítva és lefuttatva a négyzet továbbra is látható. A ViewModel osztály konstruktorában egyetlen sor található: MyModel = new Model(true);
Amennyiben a true értékét false-ra cseréljük a program futtatásakor a négyzet láthatatlan lesz.
Egészítsük ki a XAML kódot egy CheckBox-al.
<CheckBox Content="Rect visibility" IsChecked="{Binding MyModel.RectVisibility, Mode=TwoWay}"/>
Mivel az IsChecked bool típust vár ezért nincs szükség konverter használatára.
A Mode=TwoWay (default) miatt a CheckBox változtatásával a Model osztály RectVisibility tulajdonsága is változik.
<Window x:Class="Wpf06_Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:Converter;assembly=Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<converter:VisibilityConverter x:Key="RectVisibilityConverter"/>
</Grid.Resources>
<StackPanel Orientation="Horizontal" Height="30">
<Rectangle Width="100" Height="30" Fill="Red" Visibility="{Binding MyModel.RectVisibility, Mode=OneWay, Converter={StaticResource RectVisibilityConverter}}"/>
<CheckBox Content="Rect visibility" IsChecked="{Binding MyModel.RectVisibility, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</Window>
Az eddig megismertek segítségével tetszőleges tulajdonságot lehet bármilyen más típusra alakítani.
A következőkben a User Interface-n történt változtatást fogjuk a Model osztály számára feldolgozni.
A VisibilityConverter osztály ConvertBack metódusát az alábbiak szerint kell megváltoztatni.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var sValue = value as string;
if (sValue != null)
{
if (sValue == "igen")
return true;
if (sValue == "nem")
return false;
}
return value;
}
Az XAML-ben pedig egy TextBox-ot kell elhelyezni, amely bemenetként az igen vagy a nem szavakat várja.
<TextBox Text="{Binding MyModel.RectVisibility, Mode=OneWayToSource, Converter={StaticResource RectVisibilityConverter}}" Width="50"/>
A TextBox szintén a Model osztály RectVisibility tulajdonságát használja adatkötésre.
A Mode=OneWayToSource beállítással elérhető, hogy a TextBox tartalmának változásával a Model osztály RectVisibility tulajdonsága megváltozzon, de ha a tulajdonság bárhol máshol megváltozna, azt a TextBox nem követi.
Fontos, hogy ez a VisulStudio szerkesztőjében NullReferenceException-t okozhat, de lefuttatva működni fog. Ha zavaró a hibaüzenet, a beállítás nyugodtan törlésre kerülhet.
A TextBox konvertálásra ugyanazt az osztályt használja mint a Rectangle, csak most visszafelé kell konvertálni.
A végleges XAML kód:
<Window x:Class="Wpf06_Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:Converter;assembly=Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<converter:VisibilityConverter x:Key="RectVisibilityConverter"/>
</Grid.Resources>
<StackPanel Orientation="Horizontal" Height="30">
<Rectangle Width="100" Height="30" Fill="Red" Visibility="{Binding MyModel.RectVisibility, Mode=OneWay, Converter={StaticResource RectVisibilityConverter}}"/>
<CheckBox Content="Rect visibility" IsChecked="{Binding MyModel.RectVisibility, Mode=TwoWay}"/>
<TextBlock Margin="20,0,0,0" Text="Látható(ird be igen vagy nem):"/>
<TextBox Text="{Binding MyModel.RectVisibility, Mode=OneWayToSource, Converter={StaticResource RectVisibilityConverter}}" Width="50"/>
<Button Content="click"/>
</StackPanel>
</Grid>
</Window>
A szövegmező után elhelyezett gomb azért kell, mert a tulajdonság frissítése csak a fókusz elvesztése után történik meg.
Ebben a példában egyetlen tulajdonságot 3 különböző elemhez kapcsoltunk hozzá különböző módon, követve, hogy az adatkötés ténylegesen csak abba az irányba történjen,amelyre feltétlenül szükség van.
A koverter természetesen számos más célra is felhasználható, de a lényege ugyanaz marad. Két tulajdonság között értelmezhető kapcsolat keletkezzen.
Nincsenek megjegyzések:
Megjegyzés küldése