W poprzednim artykule opisałem w jaki sposób konwertować pojedyncza wartość. W tym artykule pokażę, w jaki sposób radzić sobie z łączeniem wielu wartości. Oprócz tego omówię zastosowanie szablonów i stylów podczas bindowania do kolekcji typu list. Tak jak poprzednio informacje tyczą się zarówno Silverlight jak i WPF. Przykłady będą oparte o WPF - zastosowanie w Silverlight jest takie samo.
1. Konwersja kilku wartości w jedną
Korzystając z ostatniego przykładu (
poprzedni post), spróbujemy wyświetlić cenę i datę jej wprowadzenia, efekt o który nam chodzi wygląda następująco:
Aby uzyskać taki efekt możemy postąpić na dwa sposoby. Do tego celu wykorzystamy ostatnio poznane właściwości ale w wersji multi:
String Format:
Code:
<TextBlock Grid.Row="4" Grid.ColumnSpan="2">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0:C}, {1:d}">
<Binding ElementName="dgridProducts" Path="SelectedItem.StandardCost" />
<Binding ElementName="dgridProducts" Path="SelectedItem.SellStartDate" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Wykorzystaliśmy tutaj String Format. Teraz kolejny przykład wykorzystujący ValueInStockConverter. Jest to mechanizm podobny do poprzednio pokazanego Value converter. Najważniejszą różnicą jest to, iż otrzymuje on jako argument tablicę obiektów wartości z których tworzymy wynik:
Code:
<TextBlock Grid.Row="4" Grid.ColumnSpan="2">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource dpc}">
<Binding ElementName="dgridProducts" Path="SelectedItem.StandardCost" />
<Binding ElementName="dgridProducts" Path="SelectedItem.SellStartDate" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
namespace WpfApplication1
{
public class DatePriceConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.GlobalizationCultureInfo culture)
{
decimal price = (decimal)values[0];
DateTime sellDate = (DateTime)values[1];
return string.Format("{0:C}, {1:d}", price, sellDate);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.GlobalizationCultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Wynik w drugim przypadku jest taki sam jak w pierwszym.
2. Kontrolki wykorzystujące listy do wyświetlania elementów
W tym rozdziale postaram się przybliżyć kilka ciekawych technik pozwalających w łatwy sposób osiągnąć ciekawy efekt wizualny, podczas bindowania do kontrolek typu listy jak np.drzewa, listy, pasek narzędzi, elementy menu, pasek stanu itd. Zbudowane są one z małych bloczków zwanych elementami. Element to np. przycisk na pasku narzędzi. W przypadku tych kontrolek mamy możliwość bindowania do dwóch kluczowych własności: stylu i szablonu.
Podstawowe właściwości tych kontrolek to m.in.:
ItemSource - źródło danych
DisplayMemberPath - właściwość która ma zostać wyświetlona dla poszczególnych elementów
ItemStringFormat - działa w taki sam sposób jak wcześciej omawiany Binding.StringFormat
ItemContainerStyle - pozwala na ustawienie stylu konteneru wyświetlanego elementu
ItemContainerStyleSelector - pozwala na ustawienie różnych styli dla różnych elementów listy
AlternationCount - pozwala na wyświetlanie na przemian stylów - np wartość dwa spowoduje wyświetlanie danego stylu co drugi element
ItemTemplate - pozwala nadać szablon dla tworzonych elementów
ItemsPanel - Panel na którym umieszczone są wszystkie elementy (najczęściej VirtualizingStackPanel
O ile 3 pierwsze właściwości są zapewne wszystkim znane i wykorzystywane, o tyle warto wspomnieć o pozostałych.
ItemContainerStyle
Przejdźmy od razu do przykładu:
Code:
<ListBox Name="lboxProducts" DisplayMemberPath="Name" Width="250">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="BlueViolet" />
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="4" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Yellow" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
W łatwy sposób zmieniliśmy wygląd elementów ListBoxa. Możemy teraz dla przykładu zmienić poszczególne elementy na konkretne kontrolki np. RadioButtony czy też CheckBoxy:
Code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="554" Width="996" Loaded="Window_Loaded"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<RadioButton Focusable="False" IsChecked="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent} }">
<ContentPresenter></ContentPresenter>
</RadioButton>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox Name="lboxProducts" DisplayMemberPath="Name" Width="250" Style="{StaticResource ResourceKey=RadioButtonListStyle}">
</ListBox>
</Grid>
</Window>
Zmiana stylu co w n-tym elemencie
Bardzo często można spotkać się z efektem zmiany stylu co n-tego elementu, np. co drugi posiada dodatkowy cień. Silverlight jak i WPF posiada wbudowany mechanizm umożliwiający uzyskanie takiego elementu w bardzo łatwy i szybki sposób. Zobaczmy na przykład:
Code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="554" Width="996" Loaded="Window_Loaded"
xmlns:local="clr-namespace:WpfApplication1">
<Grid>
<ListBox Name="lboxProducts" DisplayMemberPath="Name" Width="250" AlternationCount="2">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightBlue" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</Window>
Efekt wywołania:
Style selector
Jeżeli potrzebujemy, aby styl został przypisany pod względem odpowiedniego warunku np. w zależności od danych zawartych w konkretnym elemencie, musimy posłużyć się implementacją własnej klasy, która dziedziczy po klasie StyleSelector.
Zobaczmy na przykład:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
namespace WpfApplication1
{
class ProductByColorSelector : StyleSelector
{
public override System.Windows.Style SelectStyle(object item, System.WindowsDependencyObject container)
{
Product product = (Product)item;
string style = string.Empty;
Window window = Application.Current.MainWindow;
Style returnStyle = null;
switch (product.Color)
{
case "Red":
returnStyle = (Style)window.FindResource("Red");
break;
case "Blue":
returnStyle = returnStyle = (Style)window.FindResource("Blue");;
break;
case "Yellow":
returnStyle = returnStyle = (Style)window.FindResource("Yellow");
break;
default:
return base.SelectStyle(item, container);
break;
}
return returnStyle;
}
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="554" Width="996" Loaded="Window_Loaded"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<Style x:Key="Yellow" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightYellow" />
<Setter Property="Padding" Value="2" />
</Style>
<Style x:Key="Red" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="OrangeRed" />
<Setter Property="Padding" Value="2" />
</Style>
<Style x:Key="Blue" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="AliceBlue" />
<Setter Property="Padding" Value="2" />
</Style>
<local:ProductByColorSelector x:Key="styleSelector" />
</Window.Resources>
<Grid>
<ListBox Name="lboxProducts" DisplayMemberPath="Name" Width="250" ItemContainerStyleSelector="{StaticResource ResourceKey=styleSelector}">
</ListBox>
</Grid>
</Window>
Efekt który uzyskamy będzie następujący:
Oczywiście można się posłużyć bardziej skomplikowaną klasą bardziej generyczną i np. jako nazwy stylów wprowadzić właściwości i odpowiednio wprowadzić je podczas deklaracji stylu dla kontrolki.
Tyle na dzisiaj odnośnie stylów dla elementów w listboxach i podobnych kontrolkach. Informacje które tutaj zawarte są (jak i w poprzednim poście) przydadzą się aby zrozumieć kolejny mój post odnośnie zmiany wyglądu ToolTipa dla wykresów.
Brak komentarzy:
Prześlij komentarz