W WPF, jak zastosować wiele stylów do pliku FrameworkElement
? Na przykład mam kontrolkę, która ma już styl. Mam też osobny styl, który chciałbym do niego dodać bez zdmuchiwania pierwszego. Style mają różne typy TargetTypes, więc nie mogę po prostu rozszerzyć jednego z drugim.
153
Odpowiedzi:
Myślę, że prosta odpowiedź jest taka, że nie możesz zrobić (przynajmniej w tej wersji WPF) tego, co próbujesz zrobić.
Oznacza to, że do każdego elementu można zastosować tylko jeden styl.
Jednak, jak powiedzieli inni powyżej, może możesz użyć,
BasedOn
aby ci pomóc. Sprawdź poniższy fragment luźnego XAML. Zobaczysz w nim, że mam styl bazowy, który ustawia właściwość istniejącą w klasie bazowej elementu, do którego chcę zastosować dwa style. W drugim stylu, który jest oparty na stylu podstawowym, ustawiłem inną właściwość.Więc pomysł tutaj ... jest taki, że jeśli możesz w jakiś sposób oddzielić właściwości, które chcesz ustawić ... zgodnie z hierarchią dziedziczenia elementu, dla którego chcesz ustawić wiele stylów ... możesz mieć obejście tego problemu.
Mam nadzieję że to pomoże.
Uwaga:
Na szczególną uwagę zasługuje jedna rzecz. Jeśli zmienisz
TargetType
w drugim stylu (w pierwszym zestawie xaml powyżej) naButtonBase
, te dwa style nie zostaną zastosowane. Jednak zapoznaj się z poniższym xaml, aby obejść to ograniczenie. Zasadniczo oznacza to, że musisz nadać stylowi klucz i odwołać się do niego za pomocą tego klucza.źródło
derivedStyle
Musi pochodzić pobaseStyle
Bea Stollnitz zamieściła dobry post na blogu o używaniu do tego rozszerzenia znaczników, pod nagłówkiem „Jak ustawić wiele stylów w WPF?”
Ten blog jest teraz martwy, więc odtwarzam ten wpis tutaj
WPF i Silverlight oferują możliwość wyprowadzenia stylu z innego stylu za pomocą właściwości „BasedOn”. Ta funkcja umożliwia programistom organizowanie stylów przy użyciu hierarchii podobnej do dziedziczenia klas. Rozważ następujące style:
W tej składni Button, który używa RedButtonStyle, będzie miał właściwość Foreground ustawioną na Red, a właściwość Margin ustawioną na 10.
Ta funkcja jest dostępna w WPF od dłuższego czasu i jest nowa w Silverlight 3.
A co jeśli chcesz ustawić więcej niż jeden styl na elemencie? Ani WPF, ani Silverlight nie zapewniają rozwiązania tego problemu po wyjęciu z pudełka. Na szczęście istnieją sposoby na zaimplementowanie tego zachowania w WPF, które omówię w tym wpisie na blogu.
WPF i Silverlight używają rozszerzeń znaczników, aby zapewnić właściwości z wartościami, które wymagają pewnej logiki do uzyskania. Rozszerzenia znaczników są łatwo rozpoznawalne dzięki obecności nawiasów klamrowych otaczających je w języku XAML. Na przykład rozszerzenie znaczników {Binding} zawiera logikę do pobierania wartości ze źródła danych i aktualizowania jej w przypadku wystąpienia zmian; rozszerzenie znaczników {StaticResource} zawiera logikę do pobierania wartości ze słownika zasobów na podstawie klucza. Na szczęście dla nas WPF umożliwia użytkownikom pisanie własnych niestandardowych rozszerzeń znaczników. Ta funkcja nie jest jeszcze obecna w Silverlight, więc rozwiązanie w tym blogu ma zastosowanie tylko do WPF.
Inni napisali świetne rozwiązania do scalania dwóch stylów za pomocą rozszerzeń znaczników. Zależało mi jednak na rozwiązaniu dającym możliwość łączenia nieograniczonej liczby stylów, co jest nieco trudniejsze.
Pisanie rozszerzenia znaczników jest proste. Pierwszym krokiem jest utworzenie klasy, która pochodzi od MarkupExtension i użycie atrybutu MarkupExtensionReturnType, aby wskazać, że zamierzasz, aby wartość zwracana z rozszerzenia znaczników była typu Style.
Określanie danych wejściowych do rozszerzenia znaczników
Chcielibyśmy dać użytkownikom naszego rozszerzenia znaczników prosty sposób określania stylów do scalenia. Zasadniczo istnieją dwa sposoby określania danych wejściowych rozszerzenia znaczników przez użytkownika. Użytkownik może ustawić właściwości lub przekazać parametry do konstruktora. Ponieważ w tym scenariuszu użytkownik potrzebuje możliwości określenia nieograniczonej liczby stylów, moim pierwszym podejściem było utworzenie konstruktora, który pobiera dowolną liczbę ciągów za pomocą słowa kluczowego „params”:
Moim celem było napisanie danych wejściowych w następujący sposób:
Zwróć uwagę na przecinek oddzielający różne klucze stylu. Niestety, niestandardowe rozszerzenia znaczników nie obsługują nieograniczonej liczby parametrów konstruktora, więc takie podejście powoduje błąd kompilacji. Gdybym wiedział z góry, ile stylów chcę scalić, mógłbym użyć tej samej składni XAML z konstruktorem pobierającym żądaną liczbę ciągów:
Aby obejść ten problem, zdecydowałem, że parametr konstruktora będzie przyjmował pojedynczy ciąg, który określa nazwy stylów oddzielone spacjami. Składnia nie jest taka zła:
Obliczanie danych wyjściowych rozszerzenia znaczników
Aby obliczyć dane wyjściowe rozszerzenia znaczników, musimy zastąpić metodę z MarkupExtension o nazwie „ProvideValue”. Wartość zwrócona przez tę metodę zostanie ustawiona w miejscu docelowym rozszerzenia znaczników.
Zacząłem od stworzenia metody rozszerzenia dla Style, która wie, jak połączyć dwa style. Kod tej metody jest dość prosty:
Zgodnie z powyższą logiką, pierwszy styl jest modyfikowany w celu uwzględnienia wszystkich informacji z drugiego. Jeśli występują konflikty (np. Oba style mają metodę ustawiającą dla tej samej właściwości), wygrywa drugi styl. Zauważ, że oprócz kopiowania stylów i wyzwalaczy, wziąłem również pod uwagę wartości TargetType i BasedOn, a także wszelkie zasoby, które mógł mieć drugi styl. W przypadku typu TargetType połączonego stylu użyłem tego, który typ jest bardziej pochodny. Jeśli drugi styl ma styl BasedOn, rekursywnie scalam jego hierarchię stylów. Jeśli ma zasoby, kopiuję je do pierwszego stylu. Jeśli odwołujemy się do tych zasobów za pomocą {StaticResource}, są one statycznie rozwiązywane przed wykonaniem tego kodu scalającego i dlatego nie jest konieczne ich przenoszenie. Dodałem ten kod na wypadek, gdybyśmy używali DynamicResources.
Przedstawiona powyżej metoda rozszerzenia umożliwia następującą składnię:
Ta składnia jest przydatna pod warunkiem, że mam wystąpienia obu stylów w ramach ProvideValue. Cóż, ja nie. Wszystko, co otrzymuję od konstruktora, to lista kluczy ciągów dla tych stylów. Gdyby w parametrach konstruktora istniała obsługa params, mógłbym użyć następującej składni, aby uzyskać rzeczywiste wystąpienia stylu:
Ale to nie działa. I nawet gdyby ograniczenie parametrów nie istniało, prawdopodobnie trafilibyśmy na inne ograniczenie rozszerzeń znaczników, gdzie musielibyśmy użyć składni elementu właściwości zamiast składni atrybutu, aby określić zasoby statyczne, co jest rozwlekłe i kłopotliwe (wyjaśniam to błąd lepiej w poprzednim poście na blogu ). I nawet gdyby oba te ograniczenia nie istniały, nadal wolałbym pisać listę stylów używając tylko ich nazw - jest krótsza i prostsza do odczytania niż StaticResource dla każdego z nich.
Rozwiązaniem jest utworzenie StaticResourceExtension przy użyciu kodu. Biorąc pod uwagę klucz stylu typu string i dostawcę usług, mogę użyć StaticResourceExtension, aby pobrać rzeczywistą instancję stylu. Oto składnia:
Teraz mamy wszystkie elementy potrzebne do napisania metody ProvideValue:
Oto pełny przykład użycia rozszerzenia znaczników MultiStyle:
źródło
Ale możesz rozszerzyć zakres z innego… spójrz na właściwość BasedOn
źródło
WPF / XAML nie zapewnia tej funkcji natywnie, ale zapewnia rozszerzalność, aby umożliwić Ci robienie tego, co chcesz.
Napotkaliśmy tę samą potrzebę i ostatecznie utworzyliśmy własne rozszerzenie znaczników XAML (które nazwaliśmy „MergedStylesExtension”), aby umożliwić nam utworzenie nowego stylu na podstawie dwóch innych stylów (które w razie potrzeby można by prawdopodobnie użyć wielokrotnie w wiersz, aby dziedziczyć z jeszcze większej liczby stylów).
Ze względu na błąd WPF / XAML, musimy użyć składni elementu właściwości, aby go użyć, ale poza tym wydaje się działać poprawnie. Na przykład,
Niedawno pisałem o tym tutaj: http://swdeveloper.wordpress.com/2009/01/03/wpf-xaml-multiple-style-inheritance-and-markup-extensions/
źródło
Jest to możliwe dzięki utworzeniu klasy pomocniczej do używania i zawijania twoich stylów. Wspomniany tutaj CompoundStyle pokazuje, jak to zrobić. Istnieje wiele sposobów, ale najłatwiej jest wykonać następujące czynności:
Mam nadzieję, że to pomoże.
źródło
Służy
AttachedProperty
do ustawiania wielu stylów, takich jak następujący kod:Użycie:
Wynik:
źródło
jeśli nie dotykasz żadnych określonych właściwości, możesz pobrać wszystkie podstawowe i wspólne właściwości do stylu, którego typem docelowym byłby FrameworkElement. następnie możesz stworzyć specyficzne smaki dla każdego typu celu, którego potrzebujesz, bez konieczności ponownego kopiowania wszystkich tych wspólnych właściwości.
źródło
Prawdopodobnie możesz uzyskać coś podobnego, jeśli zastosujesz to do kolekcji elementów za pomocą StyleSelector, użyłem tego do rozwiązania podobnego problemu z używaniem różnych stylów w TreeViewItems w zależności od powiązanego typu obiektu w drzewie. Być może będziesz musiał nieco zmodyfikować poniższą klasę, aby dostosować się do swojego konkretnego podejścia, ale miejmy nadzieję, że to pomoże Ci zacząć
Następnie zastosuj to jako tak
źródło
Czasami można to osiągnąć, zagnieżdżając panele. Powiedzmy, że masz Styl, który zmienia pierwszy plan, a inny zmienia FontSize. Możesz zastosować ten drugi do TextBlock i umieścić go w siatce, której styl jest pierwszym. Może to pomóc, aw niektórych przypadkach może być najłatwiejszym sposobem, ale nie rozwiąże wszystkich problemów.
źródło
Gdy nadpisujesz SelectStyle, możesz uzyskać właściwość GroupBy poprzez odbicie, jak poniżej:
źródło
Jeśli próbujesz zastosować unikalny styl tylko do jednego elementu jako dodatek do stylu podstawowego, istnieje zupełnie inny sposób na zrobienie tego, który jest znacznie lepszy dla kodu IMHO, który jest czytelny i łatwy w utrzymaniu.
Niezwykle często trzeba dostosować parametry dla poszczególnych elementów. Definiowanie stylów słownikowych tylko do użytku w jednym elemencie jest niezwykle kłopotliwe w utrzymaniu lub nadaniu sensu. Aby uniknąć tworzenia stylów tylko dla jednorazowych poprawek elementów, przeczytaj moją odpowiedź na moje własne pytanie tutaj:
https://stackoverflow.com/a/54497665/1402498
źródło