V dnešním článku si vytvoříme jednoduchou aplikaci, která bude zobrazovat aktuální kurzovní lístek. Tuto aplikaci si pak budete moci umístit např. na Vaše vlastní stránky.
1. krok – Návrh aplikace
Jako zdroj dat budeme využívat kurzovní lístek Československé obchodní banky, a.s. (ČSOB), který najdeme k dispozici ve formátu txt na adrese http://www.csob.cz/webcsob/kurzy/kurzynewcz.txt. Struktura souboru vypadá následovně:
2010-12-23 17:20:00
;;;;Devizy;;;Valuty
Země;Množství;Měna;Změna;Nákup;Prodej;Střed;Nákup;Prodej;Střed
Austrálie;1;AUD;0,80;18,953;19,687;19,320;0,00;0,00;0,00
Dánsko;1;DKK;0,20;3,330;3,460;3,395;3,32;3,46;3,39
EMS;1;EUR;0,20;24,817;25,779;25,298;24,77;25,83;25,30
Chorvatsko;1;HRK;0,10;3,358;3,488;3,423;0,00;0,00;0,00
Japonsko;100;JPY;1,20;22,744;23,672;23,208;0,00;0,00;0,00
Kanada;1;CAD;0,80;18,697;19,421;19,059;0,00;0,00;0,00
Maďarsko;100;HUF;0,50;8,976;9,342;9,159;0,00;0,00;0,00
Norsko;1;NOK;0,10;3,162;3,284;3,223;3,15;3,29;3,22
Polsko;1;PLN;0,50;6,217;6,471;6,344;0,00;0,00;0,00
Rumunsko;1;RON;0,30;5,789;6,025;5,907;0,00;0,00;0,00
Rusko;100;RUB;0,70;61,783;64,177;62,980;0,00;0,00;0,00
Švédsko;1;SEK;0,00;2,762;2,870;2,816;2,76;2,88;2,82
Švýcarsko;1;CHF;1,00;19,883;20,653;20,268;19,84;20,70;20,27
Turecko;1;TRY;0,70;12,199;12,671;12,435;0,00;0,00;0,00
USA;1;USD;0,40;18,908;19,680;19,294;18,87;19,71;19,29
Velká Británie;1;GBP;0,00;29,154;30,344;29,749;29,10;30,40;29,75
Vytvoříme si třídu, která bude reprezentovat aktuální kurz:
/// <summary>
/// Kurz
/// </summary>
public class ExchangeRate
{
/// <summary>
/// Měna
/// </summary>
public string Currency { get; set; }
/// <summary>
/// Země
/// </summary>
public string Country { get; set; }
/// <summary>
/// Množství
/// </summary>
public int Amount { get; set; }
/// <summary>
/// Změna
/// </summary>
public float Change { get; set; }
/// <summary>
/// Devizy - nákup
/// </summary>
public float PurchaseCashless { get; set; }
/// <summary>
/// Devizy - prodej
/// </summary>
public float SalesCashless { get; set; }
/// <summary>
/// Devizy - střed
/// </summary>
public float MidPointCashless { get; set; }
/// <summary>
/// Valuty - nákup
/// </summary>
public float PurchaseCash { get; set; }
/// <summary>
/// Valuty - prodej
/// </summary>
public float SalesCash { get; set; }
/// <summary>
/// Valuty - střed
/// </summary>
public float MidPointCash { get; set; }
/// <summary>
/// Graf
/// </summary>
public string Chart { get; set; }
}
Máme třídu reprezentující konkrétní kurz a ještě si vytvoříme třídu, která bude obsahovat až n-kurzů
/// <summary>
/// Kurzy
/// </summary>
public class ExchangeRates
{
/// <summary>
/// Seznam jednotlivych kurzu
/// </summary>
public ObservableCollection<ExchangeRate> Data { get; set; }
}
Máme data, potřebné třídy a tak už jenom stačí data stáhnout, inicializovat třídy a nějak pěkně zobrazit.
2. krok – Vytvoření WCF služby pro stažení dat z internetu
Pro stažení dat využijeme WCF službu, kde pomocí třídy WebClient a metody DownloadStringAsync stáhneme kurzovní lístek.
Poznámka: Jelikož server csob.cz neobsahuje potřebný soubor(y) crossdomain.xml a clientaccesspolicy.xml využívám pro stažení WCF službu. Kdybych použil WebClient.DownloadStringAsync přímo ze Silverlightu, dostal bych vždy SecurityException. Více o bezpečnostních omezeních najdete v článku Network Security Access Restrictions in Silverlight
WCF služba bude obsahovat pouze jednu metodu nazvanou GetData, která vrátí obsah kurzovního lístku. POZOR !!!tato WCF služba je umístěna ve webové aplikaci, ve které spuštíme silverlight aplikaci.
[ServiceContract]
public interface IExchangeRatesService
{
[OperationContract]
string GetData();
}
public class ExchangeRatesService : IExchangeRatesService
{
public string GetData()
{
string data = null;
using ( WebClient client = new WebClient() )
{
try
{
data = client.DownloadString(@"http://www.csob.cz/webcsob/kurzy/kurzynewcz.txt");
}
catch ( Exception ex )
{
data = ex.Message;
}
}
return data;
}
}
Poznámka: Url adresa kurzovního lístku je zde napevno, což není nejlepší řešení, takže bych spíše doporučil tuto adresu předávat jako parametr metody GetData. V naší ukázce to ale ničemu vadit nebude.
Upravíme ještě nastavení služby ve web.config-u
<system.serviceModel>
<services>
<service name="ExchangeRatesSample.Web.ExchangeRatesService">
<endpoint address=""
binding="basicHttpBinding"
contract="ExchangeRatesSample.Web.IExchangeRatesService">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
3. krok – Stažení dat pomocí WCF služby
V silverlight aplikaci přidáme referenci na WCF službu kliknutím pravým na References a vybráním volby “Add Service Reference”. Jakmile přidáme referenci, tak si otevřeme kód na pozadí MainPagexaml.cs a obsloužíme událost Loaded.
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.Loaded += MainPageOnLoaded;
}
void MainPageOnLoaded(object sender, RoutedEventArgs args)
{
var proxy = new ExchangeRatesServiceClient();
proxy.GetDataCompleted += GetDataOnCompleted;
proxy.GetDataAsync();
}
void GetDataOnCompleted(object sender, GetDataCompletedEventArgs args)
{
if ( args.Error == null && !args.Cancelled )
{
var rates = new ExchangeRates();
rates.Data = new ObservableCollection< ExchangeRate >();
var lines = from l in args.Result.Split('\r', '\n').Skip(8)
where !string.IsNullOrWhiteSpace(l)
select l;
foreach ( var line in lines )
{
var rate = ExchangeRate.FromLine(line);
if ( rate != null )
rates.Data.Add(rate);
}
}
}
}
Třída ExchangeRatesServiceClient se mi vygenerovala, když jsem přidal referenci na WCF službu. V metodě GetDataOnCompleted si ziskám pouze ty řádky, na kterých jsou kurzy a převedu je na objekty typu ExchangeRate pomocí pomocné statické metody FromLine
public static ExchangeRate FromLine(string line)
{
var data = line.Split(';');
return new ExchangeRate()
{
Country = data[0],
Amount = int.Parse(data[1]),
Currency = data[2],
Change = float.Parse(data[3]),
PurchaseCashless = float.Parse(data[4]),
SalesCashless = float.Parse(data[5]),
MidPointCashless = float.Parse(data[6]),
PurchaseCash = float.Parse(data[7]),
SalesCash = float.Parse(data[8]),
MidPointCash = float.Parse(data[9])
};
}
4. krok – Zobrazení dat v DataGridu
Přidáme si do MainPage DataGrid
<data:DataGrid Grid.Row="1" ItemsSource="{Binding Data}"
AutoGenerateColumns="True"/>
kde data je definice namespace ve kterém se DataGrid nachází (xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"). No a pak už jenom stačí za cyklem foreach nastavit DataContext na nově vytvořený objekt rates, který ve vlastnosti Data uchovává veškeré kurzy. Po spuštění může aplikace vypadat takto:

5. Krok – Úprava hlavičky DataGridu
Aplikace funguje – data se stáhnou, zobrazí, ale není to ještě úplně ono. Např. některé sloupce jsou zde uvedeny 2x - prodej, nákup a střed. První trojice je pro Devizy a druhá pro Valuty, ale zatím to poznám pouze já. Upravíme tedy hlavičku našeho gridu tak, aby měla “2 řádky”. Druhý řádek bude obsahovat zmiňované sloupce, ale první bude roztažen přes 3 a bude obsahovat konkrétní nápis buď Devizy nebo Valuty.
<Style x:Key="datagridHeaderStyle" TargetType="primitives:DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="primitives:DataGridColumnHeader">
<Grid x:Name="Root">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundRectangle"
Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#7FFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#CCFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#F2FFFFFF"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundRectangle"
Storyboard.TargetProperty="(Fill).Color" To="#FF448DCA"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[0].Color" To="#D8FFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[1].Color" To="#C6FFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[2].Color" To="#8CFFFFFF"/>
<ColorAnimation Duration="0"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Fill).(GradientStops)[3].Color" To="#3FFFFFFF"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SortStates">
<VisualState x:Name="Unsorted"/>
<VisualState x:Name="SortAscending" />
<VisualState x:Name="SortDescending" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="BackgroundRectangle" Fill="#FF1F3B53" Stretch="Fill" Grid.ColumnSpan="2"/>
<Rectangle x:Name="BackgroundGradient" Stretch="Fill" Grid.ColumnSpan="2">
<Rectangle.Fill>
<LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
<GradientStop Color="#FCFFFFFF" Offset="0.015"/>
<GradientStop Color="#F7FFFFFF" Offset="0.375"/>
<GradientStop Color="#E5FFFFFF" Offset="0.6"/>
<GradientStop Color="#D1FFFFFF" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Grid HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="1"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="1"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<!-- Row 0 -->
<ContentPresenter Content="{TemplateBinding Content}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.ColumnSpan="5"/>
<!-- Row 1 -->
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Height="1"
Visibility="Visible" Grid.Row="1" Grid.ColumnSpan="5" />
<!-- Row 2 -->
<TextBlock Text="Purchase" VerticalAlignment="Center"
HorizontalAlignment="Center" Grid.Row="2"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible" Grid.Row="2" Grid.Column="1"/>
<TextBlock Text="Sales" VerticalAlignment="Center"
HorizontalAlignment="Center" Grid.Row="2" Grid.Column="2"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible" Grid.Row="2" Grid.Column="3"/>
<TextBlock Text="MidPoint" VerticalAlignment="Center"
HorizontalAlignment="Center" Grid.Row="2" Grid.Column="4"/>
</Grid>
<Rectangle x:Name="VerticalSeparator" Fill="#FFC9CACA"
VerticalAlignment="Stretch" Width="1" Visibility="Visible"
Grid.Row="1" Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Poznámka: Informace o tom jak upravit hlavičku datagridu jsem čerpal z následující stránky
Ve stručnosti vysvětleno, jde o vytvoření gridu se 3 řádky a 5 sloupci. Kde v prvním řádku je hlavička (devizy nebo valuty), ve druhém oddělovač a v posledním hodnoty Prodej, oddělovač, nákup, oddělovač a střed (proto 5 sloupců). Upravíme tedy datagrid aby negenoravl sloupečky automaticky, ale nechal to na našem manuálním vytvoření
<data:DataGrid Grid.Row="1" ItemsSource="{Binding Data}"
AutoGenerateColumns="False" IsReadOnly="True">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="Currency"
Binding="{Binding Currency}"/>
<data:DataGridTextColumn Header="Country"
Binding="{Binding Country}"/>
<data:DataGridTextColumn Header="Amount"
Binding="{Binding Amount}"/>
<data:DataGridTextColumn Header="Change"
Binding="{Binding Change, StringFormat=P}"/>
<data:DataGridTemplateColumn Header="Cashless" HeaderStyle="{StaticResource datagridHeaderStyle}">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PurchaseCashless}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible"/>
<TextBlock Text="{Binding SalesCashless}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible"/>
<TextBlock Text="{Binding MidPointCashless}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTemplateColumn Header="Cash" HeaderStyle="{StaticResource datagridHeaderStyle}">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PurchaseCash}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible"/>
<TextBlock Text="{Binding SalesCash}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
<Rectangle Fill="#FFC9CACA" VerticalAlignment="Stretch" Width="1"
Visibility="Visible"/>
<TextBlock Text="{Binding MidPointCash}" VerticalAlignment="Center"
HorizontalAlignment="Center" Width="50"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTextColumn Header="Chart"
Binding="{Binding Chart}"
Width="*"/>
</data:DataGrid.Columns>
</data:DataGrid>
Většina sloupečků je typu TextColumn, protože nám stačí zobrazení pouhého textu, ale u hodnot pro Devizy a Valuty je nutné použít TemplateColumn. Pomocí DataTemplate vytvoříme StackPanel, obsahující 3x Textbox (Nákup, Prodej, Střed) oddělený oddělovačem (podobně jako v definici stylu)

Poznámka: U posledního sloupce Chart nastavíme šířku pomocí hvězdičky, což znamená, že se sloupec roztáhne až do konce celého datagridu.
6. krok – Přidání odkazu
Aby naše aplikace byla dle našich představ je potřeba přidat odkaz u sloupce Country, tzn. když uživatel klikne na název země, aby došlo k přesměrování na stránky s detailními infromacemi o měně. Pro tuto funkcionalitu si vytvoříme vlastní DetailLinkButton. Je potřeba nastavit NavigateUri na odkaz dle aktuální měny, která se předává jako parametr.
public class DetailLinkButton : HyperlinkButton
{
private const string BaseCsUri = "http://www.csob.cz/cz/Csob/Kurzovni-listky/Stranky/kurzovni-listek-detail.aspx?Currency=AUD={0}";
public static readonly DependencyProperty CurrencyProperty =
DependencyProperty.Register("Currency", typeof(string),
typeof(DetailLinkButton), null);
public DetailLinkButton()
{
Click += DetailLinkOnClick;
}
void DetailLinkOnClick(object sender, RoutedEventArgs e)
{
TargetName = "_blank";
NavigateUri = new Uri(string.Format(BaseCsUri, Currency));
}
public string Currency
{
get { return GetValue(CurrencyProperty).ToString(); }
set { SetValue(CurrencyProperty, value); }
}
}
Stačí už jenom upraviz xaml, aby místo DataGridTextColumn používal DetailLinkButton
<data:DataGridTemplateColumn Header="Country">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:DetailLinkButton Currency="{Binding Currency}"
Content="{Binding Country}"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Do Currency si načteme kód měny a jako obsah odkazu zůstane název země. Podobně se změní i sloupec pro graf, kde nyní použijeme ikonu
<data:DataGridTemplateColumn Header="Chart" Width="*">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:DetailLinkButton Currency="{Binding Currency}">
<Image Source="Images/ico-graf.png" Stretch="Fill" Width="16" Height="16"/>
</local:DetailLinkButton>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
7. krok – Přidání ikon
Jeden obrázek už v aplikaci máme a nyní přidáme ostatní. Nejdříve přidáme jednotlivé vlaječky k názvům měny. Tyto vlaječky si uložíme do složky Images

A k tomu aby jsme je zobrazily v gridu, bude potřeba vytvořit Converter, který na základě kódu měny vrátí správný název obrázku.
public class CurrencyToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ( value == null )
return null;
return string.Format("Images/{0}.png", value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
V xamlu pak stačí upravit pár řádků pro Currency
<data:DataGridTemplateColumn Header="Currency">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Currency, Converter={StaticResource conv}}"
Width="16" Height="16" VerticalAlignment="Center"
HorizontalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Currency}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="5"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
a Chart
<data:DataGridTemplateColumn Header="Chart" Width="*">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:DetailLinkButton Currency="{Binding Currency}" Margin="5">
<Image Source="Images/ico-graf.png" Stretch="Fill" Width="16" Height="16"/>
</local:DetailLinkButton>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
V poslední řadě si pak vytvoříme konverte, který na základě změny (Change) zobrazí buď zelenou (kladné hodnoty), modrou (beze změny), červenou šipku (záporné hodnoty)
public class ChangeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ( value == null )
return null;
float change = float.Parse(value.ToString());
if ( change > 0 )
return "Images/arrow_up_green.png";
else if ( change == 0 )
return "Images/arrow_right_blue.png";
else
return "Images/arrow_down_red.png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
V xamlu bude úprava téměř totožná jako sloupeček s vlajkami.
<data:DataGridTemplateColumn Header="Change">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Change, StringFormat=P}"
VerticalAlignment="Center" HorizontalAlignment="Center"
Margin="5"/>
<Image Source="{Binding Change, Converter={StaticResource conv2}}"
Width="16" Height="16" HorizontalAlignment="Center"
VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>

8. krok – Lokalizace
Předposledním krokem bude lokalizace aplikace, která ať se Vám může zdát složitá je naopak velice jednoduchá. Přidáme si do aplikace Resources (Strings.resx)

A anglickou verzi Strings.en-US.resx

Aby jsme se dostali ze XAMLu na naše Resources, přidíme si instanci jako StaticResource
<local:CustomResources x:Key="LocStrings"/>
LocStrings je typu CustomResources. Jedná se o pomocnou třídu, která má vlastnost LocalizedStrings typu Strings(Resource) a slouží především proto, že když uživatel za běhu změní kulturu, tak abych mohl notifikovat UI o tom, že došlo ke změně a došlo k přečtení správného resource
public class CustomResources : INotifyPropertyChanged
{
private readonly Resources.Strings _strings = new Resources.Strings();
public Resources.Strings LocalizedStrings
{
get { return _strings; }
set { OnPropertyChanged(MethodBase.GetCurrentMethod()); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(MethodBase methodBase)
{
if ( PropertyChanged != null )
{
PropertyChanged(this, new PropertyChangedEventArgs(methodBase.Name.Substring(4)));
}
}
}
Nyní všechen text, který mám v XAMLu natvrdo nahradím části kódu, která načte příslušný string na základě zvolené kultury. Ukázka jak toho docílit např. pro měnu:
<TextBlock Text="{Binding Source={StaticResource LocStrings}, Path=LocalizedStrings.Currency}"/>
Problém je, že toto nefunguje u vlastnosti Header, takže místo přeloženého textu se nám zobrazí System.Windows.Data.Binding. Je nutné to trošku objeít např. tímto způsobem
public class ExDataGridTextColumn : DataGridTextColumn
{
public string ColumnHeader
{
get
{
return ( string ) GetValue(ColumnHeaderProperty);
}
set
{
SetValue(ColumnHeaderProperty, value);
}
}
public static readonly DependencyProperty ColumnHeaderProperty =
DependencyProperty.Register("ColumnHeader", typeof(string),
typeof(ExDataGridTextColumn), new PropertyMetadata(ColumnHeaderChanged)
);
private static void ColumnHeaderChanged(object sender, DependencyPropertyChangedEventArgs args)
{
DataGridTextColumn dgc = sender as DataGridTextColumn;
string h = (string) args.NewValue;
if ( args.NewValue != args.OldValue )
{
dgc.Header = h;
}
}
}
public class ExDataGridTemplateColumn : DataGridTemplateColumn
{
public string ColumnHeader
{
get
{
return ( string ) GetValue(ColumnHeaderProperty);
}
set
{
SetValue(ColumnHeaderProperty, value);
}
}
public static readonly DependencyProperty ColumnHeaderProperty =
DependencyProperty.Register("ColumnHeader", typeof(string),
typeof(ExDataGridTemplateColumn), new PropertyMetadata(ColumnHeaderChanged)
);
private static void ColumnHeaderChanged(object sender, DependencyPropertyChangedEventArgs args)
{
ExDataGridTemplateColumn dgc = sender as ExDataGridTemplateColumn;
string h = ( string ) args.NewValue;
if ( args.NewValue != args.OldValue )
{
dgc.Header = h;
}
}
}
Vytvoříme si dvě třídy ExDataGridTextColumn a ExDataGridTemplateColumn, kde definujeme jednu vlastnost ColumnHeader. Tato vlastnost slouží jako náhrada vlastnosti Header (nyní lze už použít Binding). Můžeme tedy nastavit hlavičku sloupce jednoduchým způsobem:
ColumnHeader="{Binding Source={StaticResource LocStrings}, Path=LocalizedStrings.Cash}"
Na závěr už pak jenom přidáme ComboBox, ve kterém bude seznam dostupných lokalizací a uživatel si tak bude moct zvolit, kterou jazykovou verzi bude chtít využít.
<ComboBox x:Name="ui_cbLanguages" SelectionChanged="LanguagesOnSelectionChanged"
HorizontalAlignment="Right" Grid.Row="0">
<ComboBoxItem Tag="en-Us">
<Image Source="Images/USD.png" Width="16" Height="16"/>
</ComboBoxItem>
<ComboBoxItem Tag="cs-CZ">
<Image Source="Images/CZE.png" Width="16" Height="16"/>
</ComboBoxItem>
</ComboBox>
No a v obsluze metody LanguagesOnSelectionChanged provedeme pouze změnu nastavení kultury
private void LanguagesOnSelectionChanged(object sender, SelectionChangedEventArgs args)
{
ComboBoxItem item = ui_cbLanguages.SelectedItem as ComboBoxItem;
string code = item.Tag.ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(code);
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(code);
// notifikuju o zmene
(( CustomResources ) this.Resources["LocStrings"]).LocalizedStrings = new Resources.Strings();
}
9. krok – Izolované uložiště
Úplně poslední krok dnešního tutoriálu je ukázka jak využít izolované uložiště. Jakmile se aplikace spustí, stáhne data pomocí WCF služby a poté uloží stažené data do izolovaného uložiště. Při dalším spuštění aplikace ověří, zda již neexistují data a pokud existují a jsou platná, nahrají se z toho uložiště a není potřeba volat znovu WCF službu.
private void SaveData(string data, string path)
{
using ( IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication() )
{
using ( IsolatedStorageFileStream isolatedStorageFileStream =
new IsolatedStorageFileStream(path, FileMode.Create, file) )
{
using ( StreamWriter writer = new StreamWriter(isolatedStorageFileStream) )
{
writer.Write(data);
}
}
}
}
public bool GetData(string path, out string data)
{
data = string.Empty;
using ( IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication() )
{
if ( !file.FileExists(path) )
return false;
using ( IsolatedStorageFileStream isolatedStorageFileStream =
new IsolatedStorageFileStream(path, FileMode.OpenOrCreate, file) )
{
using ( StreamReader reader = new StreamReader(isolatedStorageFileStream) )
{
string dateTimeLine = reader.ReadLine();
DateTime lastDateTime = DateTime.Parse(dateTimeLine);
if ( lastDateTime.Year != DateTime.Now.Year ||
lastDateTime.DayOfYear != DateTime.Now.DayOfYear )
return false;
reader.BaseStream.Position = 0;
data = reader.ReadToEnd();
}
}
}
return true;
}
A kontrola v Load události
void MainPageOnLoaded(object sender, RoutedEventArgs args)
{
// podivam se do izolovaneho uloziste
string data =string.Empty;
if ( !GetData("kurzy.txt", out data) )
{
var proxy = new ExchangeRatesServiceClient();
proxy.GetDataCompleted += GetDataOnCompleted;
proxy.GetDataAsync();
}
else
{
FillData(data);
}
}
Tááák a to je pro tuto ukázku vše. Pokud jse se dočetli až sem, tak jsem rád, protože já sám měl problém to do takových rozměrů sepsat 
Ukázkovou aplikaci lze stáhnout na adrese http://lukaskubis.net/Demos/ExchangeRatesSample.zip