Tworząc w WPF interfejs użytkownika, często potrzebujemy przykładowych kolekcji danych, np. wyświetlanych w ListBoxie. Korzystając z Blenda, można w prosty sposób wygenerować kolekcje przykładowych danych, składających się z propercji różnych typów.Rozpocząć należy od zakładki Data, widocznej na przykład w prawym górnym rogu okna designera.
W drugim kroku należy dodać kolekcję i zbudować odpowiedni model danych, a więc dodać properties interesującego nas typu.
Jedną z fajniejszych opcji jest możliwość dodawania Stringów różnego typu, w zależności od potrzeb aplikacji.
Dane należy następnie podłączyć jako ItemSource...
a w ListBoxTemplate zbindować odpowiednie kontrolki od odpowiednich propercji, wybierając z menu dostępnego pod prawym przyciskiem myszy opcję Data bind Content to Data..
Jeżeli to nie wystarcza, w każdej chwili można utworzyć przykładowe dane w oparciu o własny plik XML z danymi.
Po dodaniu przykładowych danych warto także spojrzeć do folderu SampleData, tworzonego przez Blenda. Tworzona jest automatycznie klasa wspierająca INotifyPropertyChanged, dzięki czemu wszystkie zmiany modelu danych będą odświeżały UI i na odwrót.
Pokazywanie postów oznaczonych etykietą data. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą data. Pokaż wszystkie posty
wtorek, 26 lutego 2013
wtorek, 28 sierpnia 2012
[WPF] Triggers
Triggery stanowią kolejny bardzo użyteczny mechanizm WPF. Pojedynczy Trigger składa się z zestawy setterów, podobnie jak styl, oraz z warunku, jaki musi zostać spełniony, aby te settery zostały zastosowane.Zatem dzięki zastosowaniu triggerów, mamy okazję przenieść sporo kodu dotyczącego wyglądu z code behind do xaml. Wprowadzono trzy rodzaje triggerów :
Warunek wyzwolenia takiego triggera zależy od tego, czy któreś dependency property osiągnęło pewną wartość. W momencie, gdy property ponownie zmieni wartość, zmiany zostaną cofnięte. Przykład :
Działa podobnie jak Property Trigger, ale może być wyzwolony przez dowolne .NETowe property. Przez to obowiązuje nieco trudniejsza składa przy definiowaniu tego Triggera. Przykładowo tworząc w CodeBehind property IsHidden, możemy sterować widocznością TextBlocka.
Teraz można zdefiniować DataTrigger.
Głównym ich zastosowaniem jest wyzwalanie animacji zapisanych w storyboardach w momencie, gdy zajdzie jakieś zdarzenie. Mając nagrany w Resources storyboard, możemy się posłużyć następującą składnią:
Dodatkowo często chcemy stworzyć bardziej złożoną logikę, niż zajście pojedynczego warunku. Mamy do dyspozycji konstrukcje będące ekwiwalentem logicznych funkcji AND i OR.
Narzędziem, służącym do zbudowania tej funkcji logicznej jest MultiTrigger.
Aby stworzyć funkcję OR, można dodać kilka Triggerów obok siebie z tymi samymi setterami. Taka konstrukcja będzie odpowiadała alternatywie.
- Property Trigger
Warunek wyzwolenia takiego triggera zależy od tego, czy któreś dependency property osiągnęło pewną wartość. W momencie, gdy property ponownie zmieni wartość, zmiany zostaną cofnięte. Przykład :
<Style TargetType="{x:Type Button}"> <Setter Property="Background" Value="Red" /> <Setter Property="Foreground" Value="White" /> <Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderBrush" Value="Blue" /> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Green" /> </Trigger> </Style.Triggers> </Style>
- Data Trigger
Działa podobnie jak Property Trigger, ale może być wyzwolony przez dowolne .NETowe property. Przez to obowiązuje nieco trudniejsza składa przy definiowaniu tego Triggera. Przykładowo tworząc w CodeBehind property IsHidden, możemy sterować widocznością TextBlocka.
public bool IsHidden { get { return _isHidden; } set { _isHidden = value; Debug.WriteLine(value); if(PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsHidden")); } }
Teraz można zdefiniować DataTrigger.
<Style TargetType="{x:Type TextBlock}" > <Style.Triggers> <DataTrigger Value="True" Binding="{Binding ElementName=Window,Path=IsHidden}"> <Setter Property="Visibility" Value="Collapsed" /> </DataTrigger> </Style.Triggers> </Style>
- EventTrigger
Głównym ich zastosowaniem jest wyzwalanie animacji zapisanych w storyboardach w momencie, gdy zajdzie jakieś zdarzenie. Mając nagrany w Resources storyboard, możemy się posłużyć następującą składnią:
<Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource MyStoryboard}"/> </EventTrigger> </Window.Triggers>
Dodatkowo często chcemy stworzyć bardziej złożoną logikę, niż zajście pojedynczego warunku. Mamy do dyspozycji konstrukcje będące ekwiwalentem logicznych funkcji AND i OR.
- AND
Narzędziem, służącym do zbudowania tej funkcji logicznej jest MultiTrigger.
<Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="True" /> <Condition Property="IsVisible" Value="True" /> </MultiTrigger.Conditions> <MultiTrigger.Setters> <Setter Property="Foreground" Value="Black"/> </MultiTrigger.Setters> </MultiTrigger> </Style.Triggers>
- OR
Aby stworzyć funkcję OR, można dodać kilka Triggerów obok siebie z tymi samymi setterami. Taka konstrukcja będzie odpowiadała alternatywie.
niedziela, 26 sierpnia 2012
[WPF] HierarchicalDataTemplate
Podczas stosowanie data bindingu, dane mogą być nam dostarczone w różnej postaci. Mogą to być pojedyncze wartości, kolekcje przeróżnych typów, ale często zdarzy się, że są dostarczone w postaci hierarchicznej. WPF ułatwia bindowanie się do plików XML, za pomocą specjalnych mechanizmów.Pierwszy z nich do XmlDataProvider zapewniający binding i notyfikację każdej zmiany, jaka zajdzie po obu stronach. Kolejnym jest HierarchicalDataTemplate, który umożliwia binding do hierarchicznej struktury.
Mając przykładowe dane w pliku Countries.xml:
Możemy do resourców dodać XmlProvider z ścieżką do pliku.
A następnie, również w resourcach dodać hierarchiczny szablon danych.
Ostatecznie możemy zbindować dane z providera do kontrolki TreeView.
I tym sposobem możemy przeglądać drzewo z naszymi danymi zapisanymi w takiej hierarchii, jak w pliku XML
Mając przykładowe dane w pliku Countries.xml:
<?xml version="1.0" encoding="utf-8" ?> <Countries> <Country Name="Poland"> <CapitalCity>Warsaw</CapitalCity> <Voivodeships> <Voivodeship Name="Masovian"> <Cities> <City>Plock</City> <City>Radom</City> <City>Warsaw</City> </Cities> </Voivodeship> <Voivodeship Name="Lesser Poland"> <Cities> <City>Cracow</City> <City>Tarnow</City> </Cities> </Voivodeship> <Voivodeship Name="Greater Poland"> <Cities> <City>Kalisz</City> <City>Poznan</City> </Cities> </Voivodeship> </Voivodeships> </Country> </Countries>
Możemy do resourców dodać XmlProvider z ścieżką do pliku.
<XmlDataProvider Source="Countries.xml" XPath="Countries" x:Key="MyProvider" />
A następnie, również w resourcach dodać hierarchiczny szablon danych.
<HierarchicalDataTemplate DataType="Countries" ItemsSource="{Binding XPath=*}"> <TextBlock Text="Countries" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="Country" ItemsSource="{Binding XPath=*}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding XPath=@Name}" /> <TextBlock Text=", Capital City : " /> <TextBlock Text="{Binding XPath=CapitalCity}" /> </StackPanel> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="Voivodeships" ItemsSource="{Binding XPath=*}"> <TextBlock Text="Voivodeships :" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="Voivodeship" ItemsSource="{Binding XPath=*}"> <TextBlock Text="{Binding XPath=@Name}" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="Cities" ItemsSource="{Binding XPath=*}"> <TextBlock Text="Cities" /> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="City" > <TextBlock Text="{Binding XPath=.}" /> </HierarchicalDataTemplate>
Ostatecznie możemy zbindować dane z providera do kontrolki TreeView.
<TreeView ItemsSource="{Binding Source={StaticResource ResourceKey=MyProvider}, XPath=.}" />
I tym sposobem możemy przeglądać drzewo z naszymi danymi zapisanymi w takiej hierarchii, jak w pliku XML
piątek, 24 sierpnia 2012
[WPF] Data Binding : Value Converter
Używając data bindingu może zajść potrzeba zbindowania dwóch różnych typów. Innym razem możemy chcieć dodać pewne reguły walidacji bindowanych danych, np. saturację wartości liczbowych. Te zadania wymagają stworzenia własnego Value Convertera - specjalnej klasy, zawierającej logikę konwertującą, czy walidującą.
W pierwszym kroku należy zdefiniować klasę implementującą interfejs IValueConverter.
Jeżeli interesuje nas binding w jedną stronę, metody ConvertBack nie trzeba implementować.
Następnie należy dodać do zasobów okna nasz konwerter.
Utworzona zostanie instancja konwertera, którą można przywoływać, jako StaticResource.
W końcu można przejść w xaml do stworzenia bindingu.
Dodatkową opcją jest ConverterParameter - obiekt, który będzie przekazany do metody konwertera.jako parameter.
W pierwszym kroku należy zdefiniować klasę implementującą interfejs IValueConverter.
public class DigitToBrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { float res = 0; bool valid = float.TryParse(value.ToString(), out res); if(valid) { var props = typeof(Colors).GetProperties(); try { return new SolidColorBrush(Color.FromRgb((byte)res, 0, 0)); } catch (Exception) { return new SolidColorBrush(Color.FromRgb(0, 0, 0)); } } return Brushes.Wheat; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
Jeżeli interesuje nas binding w jedną stronę, metody ConvertBack nie trzeba implementować.
Następnie należy dodać do zasobów okna nasz konwerter.
<Window.Resources> <local:DigitToBrushConverter x:Key="myConverter" /> </Window.Resources>
Utworzona zostanie instancja konwertera, którą można przywoływać, jako StaticResource.
W końcu można przejść w xaml do stworzenia bindingu.
<Slider x:Name="colorSlider" /> <Button Background="{Binding ElementName=colorSlider, Converter={StaticResource myConverter}, Path=Value}" Content="BoundBackground" />
Dodatkową opcją jest ConverterParameter - obiekt, który będzie przekazany do metody konwertera.jako parameter.
[WPF] Data Binding : Relative Source
Ustawiając w xaml data binding, możemy określić źródło korzystając z RelativeSource. Istnieje kilka sytuacji, w których warto użyć tej opcji.
...możemy stworzyć instancję takiej kontrolki, ustawić jej marginesy i zostaną one zbindowane do textblocka.
Istnieje także możliwość określenie, który z kolei przodek nas interesuje, dopisując AncestorLevel=...
- Gdy chcemy, aby źródło i cel bindingu były tą samą kontrolką, np. zmiana wartości slidera powoduje zmianę jego nieprzezroczystości.
<Slider Minimum="0.1" Maximum="1" TickFrequency="0.1" Value="{Binding Path=Opacity, RelativeSource={RelativeSource Self}}" />
- Podczas tworzenia CustomControl w WPF, dodajemy do niej styl w pliku Generic.xaml. Custom control może składać się z wielu mniejszych kontrolek i gdy chcemy aby taka kontrolka bindowała się do property z CustomControl, używamy RelativeSource TemplatedParent.
<Style TargetType="{x:Type local:MySimpleCustomControl}"> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MySimpleCustomControl}"> <Grid Margin="3"> <Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Margin}" Width="60" TextAlignment="Right" Padding="5"/> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
...możemy stworzyć instancję takiej kontrolki, ustawić jej marginesy i zostaną one zbindowane do textblocka.
- Czasami zachodzi potrzeba zbindowania się do property z kontrolki, która znajduje się wyżej w drzewie wizualnym. Możemy znaleźć najbliższego przodka danego typu.
<TextBlock Text="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}}" />
Istnieje także możliwość określenie, który z kolei przodek nas interesuje, dopisując AncestorLevel=...
Etykiety:
ancestor,
bind,
binding,
c#,
custom control,
data,
foundation,
presentation,
relative,
self,
source,
template,
templated parent,
windows,
wpf
środa, 15 sierpnia 2012
[WPF] Dependency Property
Jedną z najważniejszych koncepcji w WPF jest Dependency Property. Umożliwiają one stylowanie, automatyczny data binding czy animowanie dowolnych właściwości. Dependency Property może być zmieniane przez wiele obiektów jednocześnie, a jego największą zaletą jest wbudowana zdolność do notyfikowania o zmianach wartości. DP mogą być także zmieniane z poziomu xaml co jest następną ich zaletą. Zatem przy tworzeniu własnych kontrolek jest to bardzo wygodne rozwiązanie. Kolejną korzyścią wynikającą z zastosowania DP jest możliwosć zdefiniowania Property Triggerów - warunkowych akcji wykonywanych, gdy property osiągnie pewną wartość
Od strony języka DP są propertiesami, budowanymi w dość nietypowy sposób. Należy dodać do klasy kontrolki statyczne publiczne pole tylko do odczytu, gdzie nazwa to nazwa property z dodanym "Property". Następnie w statycznym konstruktorze rejestruje się DP za pomocą metody DependencyProperty.Register. Przez parametry tej metody można określić domyślną wartość i metodę wołaną przy zmianie wartości. Można także zdefiniować pewien "wrapper", który będzie opakowywał ustawianie i odczyt wartości DP.
W poniższym przykładzie dla kontrolki tykającego zegarka zdefiniowano Dependency Property Ticks, które reprezentuje liczbę uderzeń zegarka.
Kontrolkę taką można dodać do okna i zbindować się do naszego Dependency Property Ticks.
Od strony języka DP są propertiesami, budowanymi w dość nietypowy sposób. Należy dodać do klasy kontrolki statyczne publiczne pole tylko do odczytu, gdzie nazwa to nazwa property z dodanym "Property". Następnie w statycznym konstruktorze rejestruje się DP za pomocą metody DependencyProperty.Register. Przez parametry tej metody można określić domyślną wartość i metodę wołaną przy zmianie wartości. Można także zdefiniować pewien "wrapper", który będzie opakowywał ustawianie i odczyt wartości DP.
W poniższym przykładzie dla kontrolki tykającego zegarka zdefiniowano Dependency Property Ticks, które reprezentuje liczbę uderzeń zegarka.
public partial class TickingTimer : UserControl { public static readonly DependencyProperty TicksProperty; public int Ticks { get { return (int)GetValue(TickingTimer.TicksProperty); } set { SetValue(TickingTimer.TicksProperty, value); } } static TickingTimer() { TickingTimer.TicksProperty = DependencyProperty.Register("Ticks", typeof(int), typeof(TickingTimer), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnTicksChanged))); } private static void OnTicksChanged(DependencyObject o, DependencyPropertyChangedEventArgs eventArgs) { Console.Beep(); } //LOGIKA KONTROLKI }
Kontrolkę taką można dodać do okna i zbindować się do naszego Dependency Property Ticks.
<Window x:Class="Dependency_Property.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Dependency_Property="clr-namespace:Dependency_Property" Title="MainWindow" Height="350" Width="525"> <Grid> <Dependency_Property:TickingTimer x:Name="Timer" Ticks="5" /> <Label Content="{Binding ElementName=Timer, Path=Ticks}" /> </Grid> </Window>
sobota, 28 lipca 2012
[Wzorce projektowe] Repository
Analogia z życia:
Chcąc wypożyczyć książki udajemy się do biblioteki. Osoby pracujące tam pomagają nam znaleźć interesujące nas pozycje, podają z magazynu egzemplarze, które nas interesują oraz wstawiają te, które chcemy oddać. W przypadku bibliotek samoobsługowych musielibyśmy wiedzieć o tym, w jaki sposób wyszukiwać i gdzie fizycznie szukać konkretnych egzemplarzy.
Zastosowanie:
Wszędzie tam, gdzie mamy do czynienia z dostępem do danych : połączenia SQL, pobieranie danych z plików, czy webserwisów. Wzorzec repozytorium zapewnia enkapsulację kodu odpowiadającego za połączenie ze źródłem danych w taki sposób. Jest to zatem warstwa pośrednia pomiędzy buissness logic, a źródłem danych, co ułatwia np. testowanie. Wzorzec często występuję w parze z wzorcem Unit of Work odpowiadającym za zatwierdzanie lub wycofywanie zmian.
Zasada działania:
Wprowadza się obiekt repozytorium udostępniający pewne API. W skład API wchodzą takie metody, jak dodawanie encji, usuwanie czy przeszukiwanie kolekcji encji. Bazowe API może być generycznym interfejsem, z którego dziedziczą konkretne repozytoria.
Przykład implementacyjny:
Chcąc wypożyczyć książki udajemy się do biblioteki. Osoby pracujące tam pomagają nam znaleźć interesujące nas pozycje, podają z magazynu egzemplarze, które nas interesują oraz wstawiają te, które chcemy oddać. W przypadku bibliotek samoobsługowych musielibyśmy wiedzieć o tym, w jaki sposób wyszukiwać i gdzie fizycznie szukać konkretnych egzemplarzy.
Zastosowanie:
Wszędzie tam, gdzie mamy do czynienia z dostępem do danych : połączenia SQL, pobieranie danych z plików, czy webserwisów. Wzorzec repozytorium zapewnia enkapsulację kodu odpowiadającego za połączenie ze źródłem danych w taki sposób. Jest to zatem warstwa pośrednia pomiędzy buissness logic, a źródłem danych, co ułatwia np. testowanie. Wzorzec często występuję w parze z wzorcem Unit of Work odpowiadającym za zatwierdzanie lub wycofywanie zmian.
Zasada działania:
Wprowadza się obiekt repozytorium udostępniający pewne API. W skład API wchodzą takie metody, jak dodawanie encji, usuwanie czy przeszukiwanie kolekcji encji. Bazowe API może być generycznym interfejsem, z którego dziedziczą konkretne repozytoria.
Przykład implementacyjny:
public interface IEntity { int Id { get; set; } } public class Customer : IEntity { public string Name { get; set; } public string Mail { get; set; } public string Address { get; set; } public string City { get; set; } public int Id { get; set; } }
public interface IRepository<T> where T : IEntity { void Add(T instance); void Remove(T instance); IEnumerable<T> Find(Predicate<T> predicate); } public class CustomerRepository : IRepository<Customer> { private List<Customer> _entities; private int _count; public CustomerRepository() { try { LoadDataFromXml(); } catch (Exception) { Console.WriteLine("Unable to load data"); } } public void LoadDataFromXml() { _entities = new List<Customer>(); XDocument doc = XDocument.Load("Records.xml"); var list = doc.Elements("records").Elements("record"); foreach (var element in list) { IEnumerable<XElement> properties = element.Elements(); var customer = new Customer(); customer.Id = _count++; customer.Name = properties.ElementAt(0).Value; customer.Mail = properties.ElementAt(1).Value; customer.Address = properties.ElementAt(2).Value; customer.City = properties.ElementAt(3).Value; _entities.Add(customer); } } public void Add(Customer instance) { instance.Id = _count++; _entities.Add(instance); } public void Remove(Customer instance) { _entities.Remove(instance); } public IEnumerable<Customer> Find(Predicate<Customer> predicate) { return _entities.Where(customer => predicate(customer)); } }
class Program { static void Main(string[] args) { IRepository<Customer> repository = new CustomerRepository(); var results = repository.Find(c => c.City.StartsWith("C")); foreach (Customer customer in results) { Console.WriteLine(customer.City); } repository.Add(new Customer() { Name = "Jonnie Walker", Address = "Elm Street 12/9", Mail = "jwalker@mit.edu", City = "NY" }); Console.ReadKey(); } }
Etykiety:
access,
add,
biznesowa,
business,
data,
design,
encja,
entity,
find,
logic,
logika,
patterns,
projektowe,
remove,
repository,
repozytorium,
wzorce
Subskrybuj:
Posty (Atom)
