2012-02-28 22 views
12

Tạo ứng dụng có chủ đề 'Tương phản cao' tùy chỉnh để sử dụng ngoài trời có thể được bật và tắt trong khi chạy. Điều này hoạt động tốt bằng cách hợp nhất và hủy hợp nhất từ ​​điển tài nguyên chứa các kiểu như dưới đây ...DynamicResource cho 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> 

Điều này rất hữu ích khi sử dụng menuitem không chỉ định kiểu. Điều này là không thực tế mặc dù trong nhiều tình huống vì không có cách nào để ràng buộc ItemsSource tạo ra trẻ em mà không có Styles. Ví dụ:

<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> 

Mỗi bài khác trên StackOverflow nói rằng bạn chỉ cần làm điều này ...

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

Nhưng điều này không làm việc cho hoàn cảnh của tôi bởi vì Basedon tôi có thể và sẽ thay đổi trong thời gian chạy (và tất nhiên bạn không thể sử dụng phần mở rộng DynamicResource trên thuộc tính BasedOn). Làm điều này trong ứng dụng của tôi hiện đang dẫn đến kiểm soát mà ghi đè lên bị mắc kẹt với phong cách của họ khi kiểm soát được nạp trong khi mọi điều khiển khác chuyển đổi một cách chính xác mà không cần tải lại.

Vì vậy, câu hỏi của tôi ...

Có cách nào để có được gia hạn DynamicResource làm việc cho Basedon hoặc là có một phương pháp khác/hack tôi có thể thực hiện được điều này để làm việc?

Trả lời

7

Cuối cùng tìm ra một giải pháp cho một DynamicResouce cho Style.BasedOn sử dụng một AttachedDependencyProperty.

Dưới đây là bản sửa lỗi cho ItemsControl.ItemContainerStyle (có thể dễ dàng sửa đổi để thay đổi 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; 
    } 
} 

Và đây là một ví dụ ...

<!-- 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> 

Đây là một phiên bản sửa đổi mà đặt FrameworkElement.Style thay vào đó trong câu trả lời của tôi cho một bài đăng khác: Setting a local implicit style different from theme-style/alternative to BasedOn DynamicResource

+0

Có một cách dễ dàng hơn để 'sao chép' các kiểu cơ sở. Tôi đã thêm câu trả lời này vào một câu trả lời mới. – aliceraunsbaek

1

Kiểu của bạn phải nằm trong thẻ UIElement.Resources. Điều này có thể được tự động xóa và repopulated.

tôi làm điều gì đó tương tự nhưng không phải là phức tạp như thế này:

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

Đó là khá nhiều việc tôi làm để hợp nhất và hủy hợp nhất các từ điển tài nguyên. Tôi không chắc chắn những gì bạn có nghĩa là bởi thẻ UIElement.Resoruces. – NtscCobalt

+0

Tôi đã thử nghiệm đặt phong cách trong UIElement.Resources thay vì thiết lập rõ ràng phong cách trong nhưng nó có tác dụng tương tự và tôi tìm thấy bằng cách sử dụng dễ đọc hơn. – NtscCobalt

3

Tôi có một chút imp rovement for NtscCobalts answer:

#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 

Bạn thấy đấy, có thể chỉ cần đặt kiểu cơ sở, khi tạo kiểu mới. Bằng cách này, các kiểu cơ sở từ kiểu cơ sở sẽ tự động trong cấu trúc phân cấp.

Các vấn đề liên quan