【问题标题】:How can I change a property (TextColor) of a template control based on a Selected Bool Parameter?如何根据 Selected Bool 参数更改模板控件的属性 (TextColor)?
【发布时间】:2019-04-04 08:51:29
【问题描述】:

我创建了一个按钮模板。它基于一个框架,它显示为一个方形按钮,当单击该按钮时,它会调用一个命令,以及一个在短时间内改变颜色的后端方法。

按钮有一个绑定参数(Enabled),可以是真或假。

我希望标签文本颜色为 Color.Red 为 true 时为 Color.Green。

<?xml version="1.0" encoding="UTF-8"?>
<t:ButtonBase xmlns="http://xamarin.com/schemas/2014/forms" 
                      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                      xmlns:t="clr-namespace:Japanese.Templates" 
                      xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
                      x:Class="Japanese.Templates.SquareButton" x:Name="this" >
    <t:ButtonBase.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding TapCommand, Source={x:Reference this}}" 
                              CommandParameter="{Binding TapCommandParam, Source={x:Reference this}}" 
                              NumberOfTapsRequired="1" />
        <TapGestureRecognizer Tapped="Tapped" />
    </t:ButtonBase.GestureRecognizers>
    <Label Text="{Binding Text, Source={x:Reference this}}" x:Name="ButtonLabel" 
           TextColor="{Binding LabelTextColor, Source={x:Reference this}}" />
</t:ButtonBase>

和后端c#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Japanese.Templates
{
    public partial class SquareButton : Frame
    {

    public static readonly BindableProperty EnabledProperty = BindableProperty.Create(nameof(Enabled), typeof(bool), typeof(ButtonBase), true);           
    public static readonly BindableProperty LabelTextColorProperty = BindableProperty.Create(nameof(LabelTextColor), typeof(Color), typeof(ButtonBase), default(Color));
    public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(ButtonBase), default(string));

    public bool Enabled { get { return (bool)GetValue(EnabledProperty); } set { SetValue(EnabledProperty, value); } }
    public Color LabelTextColor { get { return (Color)GetValue(LabelTextColorProperty); } set { SetValue(LabelTextColorProperty, value); } }
    public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }

        public SquareButton()
        {
            InitializeComponent();
            BackgroundColor = (Color)Application.Current.Resources["SquareButtonBackgroundColor"];
        }

        protected async void Tapped(Object sender, EventArgs e)
        {
            BackgroundColor = (Color)Application.Current.Resources["CSquareButtonBackgroundColor"];
            await Task.Delay(500);
            BackgroundColor = (Color)Application.Current.Resources["SquareButtonBackgroundColor"];
        }
    }
}

模板使用

 <template:SquareButton Grid.Column="1" 
     Enabled="{Binding Btns[0].IsSelected}" 
     Text="{Binding Btns[0].Name}" 
     LabelTextColor="{Binding Btns[0].TextColor}" 
     TapCommand="{Binding BtnsBtnCmd }" 
     TapCommandParam="{Binding Btns[0].Name}" />

任何人都可以就如何使启用参数的值在红色和绿色之间更改标签的颜色提供任何建议。如果可能的话,我想在 C# 后端代码中执行此操作。

不确定这是否有帮助,但我正在查看此活动:

公共事件 System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

我能否在后端 C# 中检测到这一点,并在更改时检查启用状态来设置颜色?

【问题讨论】:

    标签: xamarin xamarin.forms


    【解决方案1】:

    我认为您不需要在自定义控件中设置所有这些绑定(我假设它是自定义控件?)。它只会创建比所需更多的代码复杂性。您也不需要有两个TapGestureRecognizers 到同一个控件(下面进一步解释)。此外,强烈建议不要调用元素this,因为this 是对当前实例的有效C# 引用,会导致问题。所以你可以摆脱所有这些:

    <?xml version="1.0" encoding="UTF-8"?>
    <t:ButtonBase 
        xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:t="clr-namespace:Japanese.Templates" 
        xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
        x:Class="Japanese.Templates.SquareButton" >
        <t:ButtonBase.GestureRecognizers>
            <TapGestureRecognizer 
                Tapped="Tapped" />
        </t:ButtonBase.GestureRecognizers>
        <Label 
            x:Name="ButtonLabel"
            Text="SomeDefaultText"  
            TextColor="Color.Default" />
    </t:ButtonBase>
    

    接下来,在您的SquareButton 课程中,您与PropertyChangedEventHandler 走在了正确的轨道上。但是,这通常与标准属性一起使用,并且必须在继承 INotifyPropertyChanged 时使用。

    您真正要查找的是BindableProperty 创建方法中的propertyChanged 参数。这会分配一个事件,以便当属性更改时,它将触发此事件。例如:

    // Create your bindable property
    public static readonly BindableProperty EnabledProperty = 
        BindableProperty.Create(
            propertyName: nameof(Enabled), 
            returnType: typeof(bool), 
            declaringType: typeof(ButtonBase), 
            defaultValue: true,
            propertyChanged: HandleEnabledPropertyChanged);  // <= The property changed handler!!
    
    // The property
    public bool Enabled 
    { 
        get => (bool)GetValue(EnabledProperty); 
        set => SetValue(EnabledProperty, value); 
    }
    
    // Create your property-changed handler
    private static void HandleEnabledPropertyChanged(
        BindableObject bindable, object oldValue, object newValue)
    {
        var control = (SquareButton)bindable;
        if (control != null)
        {
            control.ButtonLabel.TextColor = 
                ((bool)newValue) ? 
                Color.Red : 
                Color.Green; 
        }
    }
    

    如果您想为TapGestureRecognizerTapped 事件添加额外的功能,您需要为此实现另一个处理程序。例如:

    private EventHandler onTapAdditionalHandler;
    
    // Assignable property (not bindable, but probably can be)
    public event EventHandler OnTapAdditionalHandler 
    { 
        add
        {
            // Clear all other handlers subscribed, and add a new one.
            onTapAdditionalHandler = null;
            onTapAdditionalHandler = value;
        }
        remove
        {
            // This will create a warning saying something like "you're not
            // removing the value from the parameter". Only use, If you are
            // 120% sure that you will only have one additional handler.
            onTapAdditionalHandler = null;
        }
    }
    
    // Your TapGestureRecognizer Tapped handler
    protected void Tapped(Object sender, EventArgs e)
    {
        // Call this method (no need to await)
        ChangeTheColours();
    
        // Do the additional handling
        onTapAdditionalHandler?.Invoke(sender, e);
    }
    
    private async void ChangeTheColours()
    {
        Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            BackgroundColor = (Color)Application.Current.Resources["CSquareButtonBackgroundColor"]);
        await Task.Delay(500); // <= Don't call on the main thread!!
        Xamarin.Forms.Device.BeginInvokeOnMainThread(() => 
            BackgroundColor = (Color)Application.Current.Resources["SquareButtonBackgroundColor"]);
    }
    

    【讨论】:

    • 非常感谢您抽出宝贵时间为我的问题提供一个好的答案。我会检查你写的所有东西,如果我有任何问题,我会告诉你。与此同时,我有一个小问题。你能告诉我你为什么使用“Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>”吗?我注意到我的代码似乎正确地延迟了 1/2 秒的代码,所以我想知道添加的原因是什么"Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>" 谢谢
    • @Alan2 没问题!我输入了BeginInvokeOnMainThread,因为应该在 UI 线程上调用与 UI 相关的指令(如颜色更改、可见性、文本)。如果它对你有用,你可以把它拿出来。我个人喜欢明确并在 UI 线程上调用它们,以防万一该方法在后台线程中被调用并且不更新 UI。
    • 感谢汤姆的快速回复。给你几个问题。你能解释一下 OnTapAdditionalHandler 的“添加”和“删除”是什么吗?我还在我的问题中添加了一行来展示我如何使用模板。在您的实现中,当我使用模板时,我如何/应该指定 TapCommand="{Binding BtnsBtnCmd }" TapCommandParam="{Binding Btns[0].Name}"?
    • 对于add/remove,请查看this Microsoft document。我将在代码中添加注释以更好地解释您看到的警告。
    • 您可能更喜欢使用ActionFunc 而不是EventHandler,具体取决于您希望它如何工作。这完全取决于您。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 2020-11-24
    • 2011-11-20
    相关资源
    最近更新 更多