Skip Navigation Links.
Skip Navigation Links.

.net student

microsoft

Vyhledávání v obsahu portálu


Přihlášení uživatele

Přihlášení uživatele

Členové:
  • Poslední nový uživatel Poslední: liborsky
  • Nový dnes Dnes nových 1
  • Nový včera Včera nových: 0
  • Počet uživatelů: Celkem: 4708
Lidé online:
  • Návštěvníci: Návštěvníků: 32
  • Registrovaných Členů: 0
  • Celkem Celkem: 32
Mapa Stranek

WPF databinding jak jej možná neznáte - Datová konverze

O tom, že se bez databindingu nelze obejít, ví asi každy. Stejně tak asi každý ví, že díky WPF lze přímo v jazyce XAML vytvářet datové zdroje a navazovat je na různé ovládací prvky. Ale ví také každý, že se nemusí programátor omezit na "pouhé" navázání, přestože mu třeba původní podoba dat nevyhovuje? Tento problém, se kterým se jistě již setkala většina z vás, odstraňuje možnost použití datové konverze.

Pro datovou konverzi nabízí WPF několik hotových konvertorů, mezi které patří např. BooleanToVisibilityConverter, který převádí hodnoty Boolean na výčtový typ Visibility, ZoomPercentageConverter, konvertující typ Double, a JournalEntryListConverter, který však nemůžeme použít přímo.

Mnoho jich není, a tak s velkou pravděpodobností nastane situace, že budeme potřebovat konvertovat data z jiných a do jiných formátu, než umožňují již hotové nabízené konvertory. Pro tyto případy si dnes zkusíme několik vlastních konvertorů vytvořit.

Teorii o tom jak konvertor vypadá z hlediska kódu necháme na později, až budeme programovat samotný konvertor, a nejprve si vytvoříme základ našeho projektu.

 

1. Přípravná fáze

Ve Visual Studiu necháme vytvořit nový projekt v jazyce C# a .NET Frameworku 3.0 nebo 3.5. Pojmenujeme jej jako "databinding" a po vytvoření projektu se přepneme do editace XAML kódu, kde nahradíme původní element Grid StackPanelem. 

Váš kód by měl vypadat takto:

<Window x:Class="databinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <StackPanel>


   </StackPanel>
</Window>

Nyní si vytvoříme data, která budeme navazovat. Půjde o data ve formátu XML, reprezentující dopravní prostředky, kde každý má nastaven název, barvu a atribut zda-li létá či nikoliv.

<Window.Resources>
        
        <XmlDataProvider x:Key="data" XPath="/DopravniProstredky/DopravniProstredek">
            <x:XData>
                <DopravniProstredky xmlns="">
                    <DopravniProstredek Nazev="Auto" Barva="červená" Leta="false" />
                    <DopravniProstredek Nazev="Loď" Barva="bílá" Leta="false" />
                    <DopravniProstredek Nazev="Vlak" Barva="modrá" Leta="false" />
                    <DopravniProstredek Nazev="Letadlo" Barva="zelená" Leta="true" />
                </DopravniProstredky>
            </x:XData>
        </XmlDataProvider>

</Window.Resources>

Data budeme navazovat na ListBox, ale ještě než to uděláme, vytvoříme si v sekci zdrojů datovou šablonu.

<DataTemplate x:Key="sablona">
   <StackPanel Orientation="Horizontal">
      <TextBlock Width="100" Text="{Binding XPath=@Nazev}"/>
      <TextBlock Width="100" Text="{Binding XPath=@Barva}" />
      <TextBlock Width="100" Text="{Binding XPath=@Leta}"/>
   </StackPanel>
</DataTemplate>

Poslední části, před vytvářením konvertoru, je přidání vlastního ListBoxu.

<ListBox x:Name="pole" Height="150"
   ItemTemplate="{StaticResource sablona}" 
   ItemsSource="{Binding Source={StaticResource data}}" />

 2. Vytváření vlastního konvertoru

Teď když spustíte dosud napsanou aplikaci tak zjistíte, že jsou v Listboxu např. u dopravních prostředků zobrazené hodnoty true a false, což není zrovna moc uživatelsky přívětivé. Mnohem lepší by bylo, kdyby se zde zobrazovaly hodnoty ano a ne. A právě teď je ta správná chvíle na datový konvertor.

Datové konvertory jsou vlastně třídy odvozené od rozhraní IValueConverter, které obsahuje dvě metody Convert a ConvertBack.

Přidejme si do našeho projektu novou třídu, kterou pojmenujme např. "DataConverters.cs".

Celou třídu upravíme do této podoby:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Data;
using System.Windows.Media;

namespace databinding.Converters
{
    [ValueConversion(typeof(string), typeof(string))]
    public class AnoNeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
          object parameter, System.Globalization.CultureInfo culture)
        {
            
        }

        public object ConvertBack(object value, Type targetType, 
          object parameter, System.Globalization.CultureInfo culture)
        {
                                  
        }
    }
}

Protože je rozhraní IValueConverter obsaženo v prostoru názvů System.Windows.Data, přidáme jej k ostatním použitím direktivy using a nezapomeneme na System.Windows.Media, který budeme potřebovat později. Dále upravíme namespace naší třídy na např. "databinding.Converters". To budeme potřebovat z důvodu přístupu k této třídě z XAML. Konvertor bude převádět string na string, přestože vstup vypadá jako bool, v datech je reprezentován řetězcem. Nakonec potřebujeme implementovat metody Convert a ConvertBack, které si vynucuje rozhraní.

Upravíme metodu Convert:

public object Convert(object value, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture)
{
   bool hodnota = System.Convert.ToBoolean(value.ToString());

   string odpoved;

   if (hodnota)
   {
      odpoved = "Ano";
   }
    else
   {
      odpoved = "Ne";
   }

   return odpoved;
            
 }

Ve velké vetšině případů je možné si vystačit pouze s touto metodou a proto ConvertBack postačí implementovat takto:

public object ConvertBack(object value, Type targetType, 
   object parameter, System.Globalization.CultureInfo culture) { throw new Exception("Tato konverze není implementována!"); }

 Tím máme vytvořen celý konvertor a pojďme jej použít.

Nejdříve upravíme element Window tak, že do něj přidáme připojení prostoru názvů s konvertorem:

xmlns:src="clr-namespace:databinding.Converters"

 V sekci zdrojů vytvoříme objekt s konvertorem:

<src:AnoNeConverter x:Key="prevod"/>

který teď použijeme. Datovou šablonu upravíme následujícím způsobem:

<DataTemplate x:Key="sablona">
   <StackPanel x:Name="pozadi" Orientation="Horizontal">
      <TextBlock Width="100" Text="{Binding XPath=@Nazev}"/>
      <TextBlock Width="100" Text="{Binding XPath=@Barva}" />
      <TextBlock Width="100" 
         Text="{Binding Converter={StaticResource prevod}, 
          XPath=@Leta}"/>
   </StackPanel>
</DataTemplate>

A to je vše.

Nyní ještě uděláme, aby barva pozadí TextBlocku, zobrazující název barvy, byla stejná jako jméno barvy. Vytvoříme si druhý konvertor, který bude převádět string na SolidColorBrush. Teď již víte jak na vytváření konvertoru a tak to bude snadné. Kód konvertoru bude vypadat takto:

[ValueConversion(typeof(string), typeof(SolidColorBrush))]
public class StringToColorConverter : IValueConverter
{
   public object Convert(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
   {
      Color barva;

       switch (value.ToString())
      {
         case ("červená"):
         barva = Colors.Red;
         break;

         case ("modrá"):
         barva = Colors.Blue;
         break;

         case ("zelená"):
         barva = Colors.Green;
         break;

         default:
         barva = Colors.White;
         break;
      }

      return new SolidColorBrush(barva);
   }

   public object ConvertBack(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
   {
      throw new Exception("Tato konverze není implementována!");           
      }
}

V XAML vytvoříme objekt konvertoru:

<src:StringToColorConverter x:Key="nabarvu"/>

a upravíme datovou šablonu:

<DataTemplate x:Key="sablona">
   <StackPanel x:Name="pozadi" Orientation="Horizontal">
      <TextBlock Width="100" Text="{Binding XPath=@Nazev}" />
      <TextBlock Width="100" 
        Background="{Binding Converter={StaticResource nabarvu}, 
         XPath=@Barva}" 
        Text="{Binding XPath=@Barva}" />
      <TextBlock Width="100" 
        Text="{Binding Converter={StaticResource prevod}, 
         XPath=@Leta}" />
   </StackPanel>
</DataTemplate>

 3. A ještě jeden na závěr

Dosud jsme vytvářeli konvertory u nichž jsme zanedbávali metodu ConvertBack. Pojďme se na závěr podívat i na zpětnou konverzi. Na ploše aplikace máme ještě trochu místa a tak si vytvoříme jednoduchý překladač třeba z češtiny do angličtiny a zpět.

Pod ListBox z minulého příkladu umístěme následující kód:

<Label>Česky:</Label>
<TextBox x:Name="cz" Width="100"/>
<Label>Anglicky:</Label>
<TextBox x:Name="en" Width="100" 
   Text="{Binding Mode=TwoWay, ElementName=cz, Path=Text}" />

Vlastnost Text druhého TextBoxu je dvoucestně navázána na vlastnost Text prvního TextBoxu, který funguje jako zdroj. Píšete-li něco v prvním, je to zobrazeno zároveň i ve druhém. Naopak to funguje také, ale musíme dát zdroji nějak vědět, že došlo k úpravě na straně "klienta". To uděláme třeba tak, že si vytvoříme handler metody TextChanged. Ten může vypadat takto:

private void en_TextChanged(object sender, TextChangedEventArgs e)
{
   TextBox pole = sender as TextBox;
   BindingExpression binding = 
       pole.GetBindingExpression(TextBox.TextProperty);
   binding.UpdateSource();
}

Pojďme na konvertor:

[ValueConversion(typeof(string), typeof(string))]
public class Prekladac : IValueConverter
{
    public object Convert(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
   {
      string preklad;

      switch (value.ToString())
      {
         case ("červená"):
             preklad = "red";
             break;

         case ("modrá"):
            preklad = "blue";
            break;

         case ("zelená"):
            preklad = "green";
            break;

        default:
           preklad = "";
           break;
               
      }

       return preklad;
    }

    public object ConvertBack(object value, Type targetType, 
      object parameter, System.Globalization.CultureInfo culture)
    {
      string preklad;

      switch (value.ToString())
      {
         case ("red"):
            preklad = "červená";
            break;

        case ("blue"):
           preklad = "modrá";
           break;

        case ("green"):
           preklad = "zelená";
           break;

       default:
          preklad = "";
          break;
      }

      return preklad;            
     }
}

 V XAML vytvoříme objekt konvertoru:

<src:Prekladac x:Key="prekladac"/>

A upravíme TextBox:

<TextBox x:Name="en" TextChanged="en_TextChanged" Width="100" 
   Text="{Binding Converter={StaticResource prekladac}, 
   Mode=TwoWay, ElementName=cz, Path=Text}"/>

A to je již opravdu vše.

Datové konvertory jsou zajímavým mechanismem, který má své využití ve spoustě případů. Mají také spoustu možností, dokážou přijímat parametry a následně je zpracovávat, dokážou pracovat s CultureInfo apod. Doufám, že jsem vám tímto článkem srozumitelnou formou alespoň trochu přiblížil problematiku jejich vytváření a aplikace na elementárních příkladech.

Příště se můžeme podívat třeba na datové validátory.

 


 

 Kompetní výpisy zdrojového kódu:

Window1.xaml

<Window x:Class="databinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:src="clr-namespace:databinding.Converters"
    Title="Window1" Height="300" Width="300">

    <Window.Resources>

        <src:AnoNeConverter x:Key="prevod"/>
        <src:StringToColorConverter x:Key="nabarvu"/>
        <src:Prekladac x:Key="prekladac"/>

        <XmlDataProvider x:Key="data" XPath="/DopravniProstredky/DopravniProstredek">
            <x:XData>
                <DopravniProstredky xmlns="">
                    <DopravniProstredek Nazev="Auto" Barva="červená" Leta="false" />
                    <DopravniProstredek Nazev="Loď" Barva="bílá" Leta="false" />
                    <DopravniProstredek Nazev="Vlak" Barva="modrá" Leta="false" />
                    <DopravniProstredek Nazev="Letadlo" Barva="zelená" Leta="true" />
                </DopravniProstredky>
            </x:XData>
        </XmlDataProvider>

        <DataTemplate x:Key="sablona">
            <StackPanel x:Name="pozadi" Orientation="Horizontal">
                <TextBlock Width="100" Text="{Binding XPath=@Nazev}" />
                <TextBlock Width="100" 
                    Background="{Binding Converter={StaticResource nabarvu}, 
                        XPath=@Barva}" 
                    Text="{Binding XPath=@Barva}" />
                <TextBlock Width="100" 
                    Text="{Binding Converter={StaticResource prevod}, 
                        XPath=@Leta}" />
            </StackPanel>
        </DataTemplate>


    </Window.Resources>


    <StackPanel>

        <ListBox x:Name="pole" Height="150"
            ItemTemplate="{StaticResource sablona}" 
            ItemsSource="{Binding Source={StaticResource data}}" />

        <Label>Česky:</Label>
        <TextBox x:Name="cz" Width="100"/>
        <Label>Anglicky:</Label>
        <TextBox x:Name="en" TextChanged="en_TextChanged" Width="100" 
            Text="{Binding Converter={StaticResource prekladac}, 
            Mode=TwoWay, ElementName=cz, Path=Text}"/>

    </StackPanel>
</Window>

Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace databinding
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void en_TextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox pole = sender as TextBox;
            BindingExpression binding =
                pole.GetBindingExpression(TextBox.TextProperty);
            binding.UpdateSource();
        }

    }
}

DataConverters.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Data;
using System.Windows.Media;

namespace databinding.Converters
{
    [ValueConversion(typeof(string), typeof(string))]
    public class AnoNeConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            bool hodnota = System.Convert.ToBoolean(value.ToString());

            string odpoved;

            if (hodnota)
            {
                odpoved = "Ano";
            }
            else
            {
                odpoved = "Ne";
            }

            return odpoved;

        }


        public object ConvertBack(object value, Type targetType, 
            object parameter, System.Globalization.CultureInfo culture)
        {
            throw new Exception("Tato konverze není implementována!");
        }

    }

    [ValueConversion(typeof(string), typeof(SolidColorBrush))]
    public class StringToColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
           object parameter, System.Globalization.CultureInfo culture)
        {
            Color barva;

            switch (value.ToString())
            {
                case ("červená"):
                    barva = Colors.Red;
                    break;

                case ("modrá"):
                    barva = Colors.Blue;
                    break;

                case ("zelená"):
                    barva = Colors.Green;
                    break;

                default:
                    barva = Colors.White;
                    break;
            }

            return new SolidColorBrush(barva);
        }

        public object ConvertBack(object value, Type targetType,
           object parameter, System.Globalization.CultureInfo culture)
        {
            throw new Exception("Tato konverze není implementována!");
        }
    }

    [ValueConversion(typeof(string), typeof(string))]
    public class Prekladac : IValueConverter
    {
        public object Convert(object value, Type targetType,
          object parameter, System.Globalization.CultureInfo culture)
        {
            string preklad;

            switch (value.ToString())
            {
                case ("červená"):
                    preklad = "red";
                    break;

                case ("modrá"):
                    preklad = "blue";
                    break;

                case ("zelená"):
                    preklad = "green";
                    break;

                default:
                    preklad = "";
                    break;

            }

            return preklad;
        }

        public object ConvertBack(object value, Type targetType,
          object parameter, System.Globalization.CultureInfo culture)
        {
            string preklad;

            switch (value.ToString())
            {
                case ("red"):
                    preklad = "červená";
                    break;

                case ("blue"):
                    preklad = "modrá";
                    break;

                case ("green"):
                    preklad = "zelená";
                    break;

                default:
                    preklad = "";
                    break;
            }

            return preklad;
        }
    }


}

Lukáš Kouřil :: 23. ledna 2008 :: 1080 shlédnutí :: 0 komentářů
kategorie: Obecná .Net témata, Vývoj Windows aplikací

Comments

Nyní zde nejsou žádné kometáře. Buďte první!
Musíte být přihlášen pro posílání komentářů. Přihlásit se můžete zde
Přehled posledních diskuzí

Přehled posledních diskuzí

  1. ASP.NET programátor zlín [12.16.2008 10:17 odp.]
    Dobrý den,v současné době hledáme programátora na pozici: ASP.NET / C# programátor - Zlín Požadavky: schopnost sam...
  2. RE: MS Fest video [12.08.2008 6:20 odp.]
    AhojVideo záznam bude, po večerech pilně pracujeme na jeho stříhání. Po dokončení se zde určitě vyskytne link na stažení...
  3. MS Fest video [12.04.2008 4:18 odp.]
    Existuje videozáznam z MS Festu konaneho (koncem listopadu 2008) o vikendu na Male Strane na fakulty MFF? Je nekde mozne...
  4. Práce pro programátora [11.25.2008 10:12 dop.]
    Hledám nadšeného programátora na vytvoření webstránek online tv/stream video. V případě zájmu pište na email: projekt@tr...
Novinky z klubů

Novinky z klubů

  1. Programátorské večery: F# 15. prosince 2008
    Ve čtvrtek 18.12.2008 se v rámci programátorských večerů uskuteční přednáška n...
  2. Programátorské večery: VSTO 10. prosince 2008
    Nad kancelářským balíkem MS Office se dají tvořit rozsáhlé aplikace. Od jednod...
  3. Programátorské večery: .NET 4.0 3. prosince 2008
    Zajímá Vás, na co se můžete těšit v nové verzi .NETu? Pak se přijďte podívat n...
  4. Záznamy z programátorských večerů 3. prosince 2008
    Záznamy z prog. večerů (prezentace+dema, občas i nějaké to video) můžete dočasně najít na http://ci...
Co se píše jinde

Co se píše jinde

Windows 7 Beta k dispozici pro MSDN předplatitele

Windows 7 Beta k dispozici pro MSDN předplatitele

Silverlight 2 úvod

Zajímá vás psaní webových aplikací na straně klienta ve vašem oblíbeném nástroji v .NETu? Slyšeli jste o Silverlightu...

PF 2009

Co tak popřát čtenářům programátorského blogu do nového roku? Snad abyste dostávali rozumná zadání a nemuseli tenkou ...

TypeConvertery

V tomto díle si vysvětlíme, co jsou TypeConvertery a k čemu se používají. To si také ukážeme na příkladu.

Atributy jmenného prostoru System.ComponentModel

V tomto článku si ukážeme jak efektivně využívat atributy z jmenného prostoru System.ComponentModel při vytváření vla...