Xamarin.Forms 中是否可以将一种颜色资源指向另一种颜色资源?
Is it possible to point one Color resource to another Color resource in Xamarin.Forms?
我正在构建一个 Xamarin Forms
应用程序,我目前正在起草我的应用程序 Resources
,主要是我的颜色。
例如我有以下内容:
<Color x:Key="Slate">#404040</Color>
<Color x:Key="Blue">#458623</Color>
<Color x:Key="SelectedItemColour">#458623</Color>
如您所见,我的 SelectedItemColour
与 Blue
相同。
我尝试了以下方法但没有用:
<Color x:Key="Slate">#404040</Color>
<Color x:Key="Blue">#458623</Color>
<Color x:Key="SelectedItemColour" Color="{StaticResource Blue}"/>
我知道 WPF
你是否可以回答here
是否可以将 Colour Resource
指向 Xamarin.Forms 中的另一个 Colour Resource
?
您可以将 x:Static
与静态 class 结合使用,以便按名称直接引用这些颜色。这样做的好处是可以将颜色集中在一个 class 中并最大限度地减少 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>
所以我必须这样做的方法非常复杂,所以请仔细听。
我发现这里真正的问题是我需要 "Theme" 我的 Xamarin.Forms 申请。
这就是我所做的。
先发一个abstract Theme
class:
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
然后我用我的实际主题扩展了这个 class:
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
class (see here) 意味着你不能扩展它并创建你自己的..耻辱。反正我跑题了。
然后我将我的应用程序资源设置为 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
class 中的 setter 属性之一看起来像这样:
你是说那个值是什么意思?
最后一块拼图来了。一种扩展方法,允许您像上面那样定义 xaml
。
首先是ColourExtension
class:
// 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
class:
public static class StylingLookup
{
public static Color GetColour(string key)
{
var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);
return currentTheme.Colours[key];
}
}
现在一切都明白了,为什么我们必须将 CurrentTheme
从主要 Styles
资源中分离出来。因为行:
var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);
如果有人有更好的样式应用程序模式,我很想听听
这可能是一个老问题,但我今天正试图为一个项目完成同样的事情,并且想要一个比这里提出的解决方案更优雅的解决方案。似乎没有办法完全用 XAML 来完成它,但这是我最终使用的解决方案。
首先,我定义了一个名为 ColorReference
:
的实用程序 class
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
以外的任何类型,都需要定义一个新的 class,但我怀疑我需要太多类型才能像这样使用别名。希望这对以后尝试做我正在做的同样事情的其他人有所帮助。
@Arconox 有正确的答案,这里有一些额外的提示。
public class ReferenceColor
{
public Color Color { get; set; }
public ReferenceColor()
{
}
public static implicit operator Color(ReferenceColor referenceColor) => referenceColor.Color;
}
我正在使用的代码允许我在运行时为我的 Xamarin.Forms 应用程序设置主题。因此,就 Arconox 而言,INotifyPropertyChanged 不是必需的,除非您在应用 运行 之后更改 themes/colors。这允许这样的用例。
<Color x:Key="Merlot">#750411</Color>
<local:ReferenceColor x:Key="Page.Static.Background" Color="{StaticResource Merlot}"/>
<Grid BackgroundColor="{DynamicResource Page.Static.Background}">
</Grid>
我正在构建一个 Xamarin Forms
应用程序,我目前正在起草我的应用程序 Resources
,主要是我的颜色。
例如我有以下内容:
<Color x:Key="Slate">#404040</Color>
<Color x:Key="Blue">#458623</Color>
<Color x:Key="SelectedItemColour">#458623</Color>
如您所见,我的 SelectedItemColour
与 Blue
相同。
我尝试了以下方法但没有用:
<Color x:Key="Slate">#404040</Color>
<Color x:Key="Blue">#458623</Color>
<Color x:Key="SelectedItemColour" Color="{StaticResource Blue}"/>
我知道 WPF
你是否可以回答here
是否可以将 Colour Resource
指向 Xamarin.Forms 中的另一个 Colour Resource
?
您可以将 x:Static
与静态 class 结合使用,以便按名称直接引用这些颜色。这样做的好处是可以将颜色集中在一个 class 中并最大限度地减少 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>
所以我必须这样做的方法非常复杂,所以请仔细听。
我发现这里真正的问题是我需要 "Theme" 我的 Xamarin.Forms 申请。
这就是我所做的。
先发一个abstract Theme
class:
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
然后我用我的实际主题扩展了这个 class:
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
class (see here) 意味着你不能扩展它并创建你自己的..耻辱。反正我跑题了。
然后我将我的应用程序资源设置为 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
class 中的 setter 属性之一看起来像这样:
你是说那个值是什么意思?
最后一块拼图来了。一种扩展方法,允许您像上面那样定义 xaml
。
首先是ColourExtension
class:
// 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
class:
public static class StylingLookup
{
public static Color GetColour(string key)
{
var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);
return currentTheme.Colours[key];
}
}
现在一切都明白了,为什么我们必须将 CurrentTheme
从主要 Styles
资源中分离出来。因为行:
var currentTheme = (Application.Current.Resources["CurrentTheme"] as Theme);
如果有人有更好的样式应用程序模式,我很想听听
这可能是一个老问题,但我今天正试图为一个项目完成同样的事情,并且想要一个比这里提出的解决方案更优雅的解决方案。似乎没有办法完全用 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
以外的任何类型,都需要定义一个新的 class,但我怀疑我需要太多类型才能像这样使用别名。希望这对以后尝试做我正在做的同样事情的其他人有所帮助。
@Arconox 有正确的答案,这里有一些额外的提示。
public class ReferenceColor
{
public Color Color { get; set; }
public ReferenceColor()
{
}
public static implicit operator Color(ReferenceColor referenceColor) => referenceColor.Color;
}
我正在使用的代码允许我在运行时为我的 Xamarin.Forms 应用程序设置主题。因此,就 Arconox 而言,INotifyPropertyChanged 不是必需的,除非您在应用 运行 之后更改 themes/colors。这允许这样的用例。
<Color x:Key="Merlot">#750411</Color>
<local:ReferenceColor x:Key="Page.Static.Background" Color="{StaticResource Merlot}"/>
<Grid BackgroundColor="{DynamicResource Page.Static.Background}">
</Grid>