2012-02-28 16 views
12

Budowanie aplikacji, która ma niestandardowy motyw "High Contrast" do użytku na zewnątrz, który można włączać i wyłączać podczas uruchamiania. To działa prawidłowo przez scalanie i un-scalanie słownika zasobu, który zawiera style jak poniżej ...DynamicResource for Style BasedOn

<Style x:Key="{x:Type MenuItem}" TargetType="{x:Type MenuItem}"> 
    <Setter Property="OverridesDefaultStyle" Value="true"/> 
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/> 
    <Setter Property="Template" Value="{StaticResource Theme_MenuItemTemplate}"/> 
</Style> 

Działa to doskonale, gdy korzystanie z menuitem nie określa styl. Jest to jednak nierealne w wielu sytuacjach, ponieważ nie ma sposobu na powiązanie dzieci generowanych przez ItemsSource bez użycia stylów. Na przykład:

<ContextMenu.ItemContainerStyle> 
    <Style TargetType="MenuItem"> 
     <Setter Property="Header" Value="{Binding Path=Name}"/> 
     <Setter Property="IsCheckable" Value="True"/> 
     <Setter Property="IsChecked" Value="{Binding Path=Checked}"/> 
     <EventSetter Event="Checked" Handler="HistoryItem_Checked"/> 
    </Style> 
</ContextMenu.ItemContainerStyle> 

Co drugi post na StackOverflow mówi po prostu trzeba to zrobić ...

<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}"> 
    <!-- Your overrides --> 
</Style> 

Ale to nie działa na moją sytuację, bo moja BasedOn może i zmieni się w czasie wykonywania (i oczywiście nie można używać rozszerzenia DynamicResource w usłudze BasedOn). Robi to w mojej aplikacji obecnie prowadzi do kontroli, które zastępują utknięcie z ich stylu, gdy kontrola została załadowana, podczas gdy co drugi kontroler poprawnie przełącza się bez przeładowania.

Więc moje pytanie ...

Czy istnieje sposób, aby uzyskać przedłużenie DynamicResource pracuje basedon czy istnieje inna metoda/siekać mogę wdrożyć, aby to działało?

Odpowiedz

7

końcu zorientowali się rozwiązanie dla DynamicResouce dla Style.BasedOn stosując AttachedDependencyProperty.

Oto dylemat dla ItemsControl.ItemContainerStyle (mogą być łatwo modyfikowane, aby zmienić FrameworkElement.Style)

public class DynamicContainerStyle 
{ 
    public static Style GetBaseStyle(DependencyObject obj) 
    { 
     return (Style)obj.GetValue(BaseStyleProperty); 
    } 

    public static void SetBaseStyle(DependencyObject obj, Style value) 
    { 
     obj.SetValue(BaseStyleProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for BaseStyle. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty BaseStyleProperty = 
     DependencyProperty.RegisterAttached("BaseStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged)); 

    public static Style GetDerivedStyle(DependencyObject obj) 
    { 
     return (Style)obj.GetValue(DerivedStyleProperty); 
    } 

    public static void SetDerivedStyle(DependencyObject obj, Style value) 
    { 
     obj.SetValue(DerivedStyleProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for DerivedStyle. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty DerivedStyleProperty = 
     DependencyProperty.RegisterAttached("DerivedStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged)); 

    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
    { 
     if (!typeof(System.Windows.Controls.ItemsControl).IsAssignableFrom(target.GetType())) 
      throw new InvalidCastException("Target must be ItemsControl"); 

     var Element = (System.Windows.Controls.ItemsControl)target; 

     var Styles = new List<Style>(); 

     var BaseStyle = GetBaseStyle(target); 

     if (BaseStyle != null) 
      Styles.Add(BaseStyle); 

     var DerivedStyle = GetDerivedStyle(target); 

     if (DerivedStyle != null) 
      Styles.Add(DerivedStyle); 

     Element.ItemContainerStyle = MergeStyles(Styles); 
    } 

    private static Style MergeStyles(ICollection<Style> Styles) 
    { 
     var NewStyle = new Style(); 

     foreach (var Style in Styles) 
     { 
      foreach (var Setter in Style.Setters) 
       NewStyle.Setters.Add(Setter); 

      foreach (var Trigger in Style.Triggers) 
       NewStyle.Triggers.Add(Trigger); 
     } 

     return NewStyle; 
    } 
} 

A tutaj jest przykład ...

<!-- xmlns:ap points to the namespace where DynamicContainerStyle class lives --> 
<MenuItem Header="Recent" 
    ItemsSource="{Binding Path=RecentFiles}" 
    IsEnabled="{Binding RelativeSource={RelativeSource Self}, Path=HasItems}" 
    ap:DynamicContainerStyle.BaseStyle="{DynamicResource {x:Type MenuItem}}"> 
    <ap:DynamicContainerStyle.DerivedStyle> 
     <Style TargetType="MenuItem"> 
      <EventSetter Event="Click" Handler="RecentFile_Clicked"/> 
     </Style> 
    </ap:DynamicContainerStyle.DerivedStyle> 
    <MenuItem.ItemTemplate> 
     <DataTemplate> 
      <TextBlock Text="{Binding}"/> 
     </DataTemplate> 
    </MenuItem.ItemTemplate> 
</MenuItem> 

Tutaj jest zmodyfikowaną wersją, która ustawia FrameworkElement.Style zamiast tego w mojej odpowiedzi na inny wpis: Setting a local implicit style different from theme-style/alternative to BasedOn DynamicResource

+0

Istnieje łatwiejszy sposób na "kopiowanie" stylów bazowych. Dodałem to w nowej odpowiedzi. – aliceraunsbaek

1

Twoje style powinny znajdować się w tagu UIElement.Resources. Można to dynamicznie wyczyścić i ponownie wypełnić.

zrobić coś podobnego, ale nie jako kompleksu tak:

MobileApp.Get().Resources.MergedDictionaries.Clear(); 

Uri uri = new Uri("/Resources/DayModeButton.xaml", UriKind.Relative); 
ResourceDictionary resDict = Application.LoadComponent(uri) as ResourceDictionary; 

resDict["SelectedColor"] = selectedColor; //change an attribute of resdict 

MobileApp.Get().Resources.MergedDictionaries.Add(resDict); 
+0

To jest właśnie to, co robię przy łączeniu i rozprzęganiu słowników zasobów. Nie jestem pewien, co masz na myśli przez tag UIElement.Resoruces. – NtscCobalt

+0

Testowałem umieszczanie stylu w UIElement.Resources zamiast jawnie ustawiać styl w , ale ma ten sam efekt i uważam, że używanie jest łatwiejsze do odczytania. – NtscCobalt

3

Mam lekki chochlik rovement dla NtscCobalts odpowiedzieć:

#region Type-specific function (FrameworkElement) 
    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) 
    { 
     var mergedStyles = GetMergedStyles<FrameworkElement>(target, GetBaseStyle(target), GetDerivedStyle(target)); // NOTE: change type on copy 

     var element = (FrameworkElement)target; // NOTE: change type on copy 

     element.Style = mergedStyles; 
    } 
    #endregion Type-specific function (FrameworkElement) 


    #region Reused-function 
    public static Style GetMergedStyles<T>(DependencyObject target, Style baseStyle, Style derivedStyle) where T : DependencyObject 
    { 
     if (!(target is T)) throw new InvalidCastException("Target must be " + typeof(T)); 

     if (derivedStyle == null) return baseStyle; 
     if (baseStyle == null) return derivedStyle; 

     var newStyle = new Style { BasedOn = baseStyle, TargetType = derivedStyle.TargetType }; 
     foreach (var setter in derivedStyle.Setters) newStyle.Setters.Add(setter); 
     foreach (var trigger in derivedStyle.Triggers) newStyle.Triggers.Add(trigger); 
     return newStyle; 

    } 
    #endregion Reused-function 

Widzisz, to jest możliwe, aby po prostu ustawić styl bazowy, podczas tworzenia nowego stylu. W ten sposób style bazowe ze stylu podstawowego są automatycznie w hierarchii.

Powiązane problemy