是否可以将一个颜色资源指向 Xamarin.Forms 中的另一个颜色资源

本文关键字:颜色 资源 Xamarin Forms 另一个 一个 是否 | 更新日期: 2023-09-27 18:32:11

我正在构建一个Xamarin Forms应用程序,我目前正在起草我的应用程序Resources,主要是我的颜色。

例如,我有以下内容:

  <Color x:Key="Slate">#404040</Color>
  <Color x:Key="Blue">#458623</Color>
  <Color x:Key="SelectedItemColour">#458623</Color>

如您所见,我的SelectedItemColourBlue相同.

我尝试了以下方法,但没有用:

  <Color x:Key="Slate">#404040</Color>
  <Color x:Key="Blue">#458623</Color>
  <Color x:Key="SelectedItemColour" Color="{StaticResource Blue}"/>

我知道WPF你能不能做到这里所说的答案

是否可以将Colour Resource指向 Xamarin.Forms 中的另一个Colour Resource

是否可以将一个颜色资源指向 Xamarin.Forms 中的另一个颜色资源

这可能是一个老问题,但我试图为今天的项目完成同样的事情,并且想要一个比这里提出的解决方案更优雅的解决方案。似乎没有办法纯粹使用 XAML 来完成它,但这是我最终使用的解决方案。

首先,我定义了一个名为 ColorReference 的实用程序类:

public class ColorReference : INotifyPropertyChanged
{
    private Color color = Color.Black;
    public Color Color
    {
        get => this.color;
        set
        {
            this.color = value;
            this.OnPropertyChanged();
        }
    }
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    [ NotifyPropertyChangedInvocator ]
    protected virtual void OnPropertyChanged([ CallerMemberName ] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
    public static implicit operator Color(ColorReference colorReference) => colorReference.Color;
}

我不是 100% 肯定 INotifyPropertyChanged 实现是必要的,但我认为它不会受到伤害(可能允许在运行时更改颜色;我还没有测试过这个)。

要使用它,只需将其用作ResourceDictionary中的资源:

<Color x:Key="FirstColor">#51688f</Color>
...
<utility:ColorReference x:Key="SomeOtherColorName" Color="{StaticResource FirstColor}" />

在我的用例中,我使用它来使用主题中定义的颜色设置 Telerik 控件的样式,这样如果我创建新主题,我就不需要到处复制相同的颜色值。明显的缺点是,对于除Color以外的任何类型,都需要定义一个新类,但我怀疑我需要太多的类型才能像这样别名。希望这可以帮助其他人将来尝试做我正在做的同样的事情。

您可以将

x:Static与静态类一起使用,以便按名称直接引用这些颜色。这样做的好处是将颜色集中到一个类中,并最大程度地减少 XAML 的数量。

namespace ResourceColors
{
    public static class Colors
    {
        public static Color Slate = Color.FromHex("#404040");
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:ResourceColors;assembly=ResourceColors" x:Class="ResourceColors.PageOne">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Color x:Key="Blue">#458623</Color>
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
            <Label Text="Test" TextColor="{x:Static local:Colors.Slate}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

所以我这样做的方式非常复杂,所以请仔细听。

我发现这里真正的问题是我需要"主题"我的Xamarin.Forms应用程序。

这就是我所做的。

首先做了一个abstract Theme课:

public abstract class Theme
{
    public Dictionary<string, Color> Colours { get; set; } = new Dictionary<string, Color>();
    protected void DictionaryThemes(Type type)
    {
        var properties = type.GetRuntimeProperties();
        foreach (PropertyInfo propInfo in properties)
        {
            if (propInfo.PropertyType == typeof(Color))
            {
                var key = propInfo.Name;
                var value = propInfo.GetValue(this);
                Colours.Add(key, (Color)value); 
            }
        }
    }
    public abstract Color TitleColour { get; }
}

用一种很好的方法使用反射来填充一本充满我Resources的字典

然后,我用我的实际主题扩展了这个类:

public class MyTheme : Theme
{
    public MyTheme()
    {
        DictionaryThemes(typeof(MyTheme));
    }
    //Slate
    public override Color TitleColour { get; } = Color.FromHex("#00ff00");
}

还和我在一起吗? 好...

然后,我必须将此主题加载到我的Application Resources中,并将其与其他应用程序资源合并。我必须将MyTheme资源与其他Resources分开,以便以后可以在主Resources文件中使用它。

让我向您展示,这是我CurrentTheme资源文件:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styling="clr-MyApp.Styling;assembly=MyApp"
             x:Class="MyApp.Styling.CurrentTheme">
  <ContentPage.Resources>
    <ResourceDictionary>
  <styling:MyTheme x:Key="CurrentTheme"/>
</ResourceDictionary>

我必须将其作为页面的一部分来执行,因为Xamarin.Forms Sealed ResourceDictionary类(见此处),这意味着您无法扩展它并创建自己的页面。羞耻。反正我离题了。

然后,我将应用程序资源设置为CurrentTheme如下所示:

 var currentTheme = new CurrentTheme();
 Xamarin.Forms.Application.Current.Resources = currentTheme.Resources;

然后,我可以从我的主Resource文件中合并其他样式,在本例中称为 Styles

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:styling="clr-MyApp.Styling;assembly=MyApp"
             x:Class="MyApp.Styling.Styles">
  <ContentPage.Resources>
    <ResourceDictionary>
       <Style x:Key="TitleStyle" TargetType="Label">
          <Setter Property="FontAttributes" Value="Bold" />
          <Setter Property="FontSize" Value="30" />
          <Setter Property="TextColor" Value="{styling:Colour TitleColour}" />
       </Style>
</ResourceDictionary>

将其合并到我的主要应用程序资源中,如下所示:

 var styles = new Styles();
 foreach (var item in styles.Resources)
 {
       Application.Current.Resources.Add(item.Key,item.Value);
 }

现在,您可以看到 Styles 类中的一个 setter 属性如下所示:

你说这个值意味着什么?

这是拼图的最后一块。一种扩展方法,允许您像上面一样定义xaml

首先是ColourExtension类:

// You exclude the 'Extension' suffix when using in Xaml markup
[ContentProperty("Text")]
public class ColourExtension : IMarkupExtension
{
    public string Text { get; set; }
    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Text == null)
            throw new Exception("No Colour Key Provided");
        return StylingLookup.GetColour(Text);
    }
}

最后是StylingLookup类:

public static class StylingLookup
{
    public static Color GetColour(string key)
    {
        var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);
        return currentTheme.Colours[key];
    }
}

现在这一切都说得通了,为什么我们必须从主要Styles资源中分离CurrentTheme。因为这条线:

var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);

如果有人有更好的样式化应用程序模式,我很想听听

@Arconox有正确的答案,这里有一些额外的提示。

public class ReferenceColor
{
    public Color Color { get; set; }
    public ReferenceColor()
    {
    }
    public static implicit operator Color(ReferenceColor referenceColor) => referenceColor.Color;
}

是我用来允许自己在运行时为我的 Xamarin.Forms 应用程序设置主题的代码。因此,对于Arconox的观点,除非您在应用程序运行后更改主题/颜色,否则不需要INotifyPropertyChanged。这允许这样的用例。

<Color x:Key="Merlot">#750411</Color>
<local:ReferenceColor x:Key="Page.Static.Background" Color="{StaticResource Merlot}"/>
<Grid BackgroundColor="{DynamicResource Page.Static.Background}">
</Grid>