【问题标题】:Xamarin Forms can't change variable valueXamarin Forms 无法更改变量值
【发布时间】:2018-02-14 14:17:17
【问题描述】:

我想用 xamarin 表单制作一个简单的秒表应用程序。

界面如下:

<?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:testvb"
             x:Class="testvb.MainPage">
    <StackLayout>
        <Label Text="{Binding LabelText}">
            <Label.BindingContext>
                <local:GetDuration/>
            </Label.BindingContext>
            <Label.GestureRecognizers>
                <TapGestureRecognizer
                    Command="{Binding TapCommand}">

                </TapGestureRecognizer>
            </Label.GestureRecognizers>
        </Label>
        <Label Text="{Binding SWDuration}">
            <Label.BindingContext>
                <local:GetDuration/>
            </Label.BindingContext>
        </Label>
        <Label Text="{Binding StartTimeText}">
            <Label.BindingContext>
                <local:GetDuration/>
            </Label.BindingContext>
        </Label>

    </StackLayout>

</ContentPage>

还有 c# 类:

namespace testvb
{
    class GetDuration : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        static string labeltext = "START";
        static string starttimetext = "Timer hasn't been started";
        ICommand tapCommand;
        static Stopwatch hstopwatch = new Stopwatch();
        string swduration;
        public GetDuration()
        {
            tapCommand = new Command(OnTapped);

            this.SWDuration = hstopwatch.Elapsed.ToString();

            Device.StartTimer(TimeSpan.FromSeconds(1), () =>
            {
                this.SWDuration = hstopwatch.Elapsed.ToString();
               return true;
            });

        }

        public ICommand TapCommand
        {
            get
            {
                return tapCommand;
            }
        }

        void OnTapped (object s)
        {
            if(LabelText == "START")
            {
                LabelText = "STOP";
                StartTimeText = DateTime.Now.ToString();
                hstopwatch.Restart();
            }
            else
            {
                LabelText = "START";
                StartTimeText = "Timer has been stopped";
                hstopwatch.Stop();
                hstopwatch.Reset();
            }
        }

        public string LabelText
        {
            get
            {
                return labeltext;
            }
            set
            {
                if(labeltext != value)
                {
                    labeltext = value;
                    if(PropertyChanged!=null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("LabelText"));                        
                    }
                }

            }
        }

        public string SWDuration
        {
            get
            {
                return swduration;
            }
            set
            {
                if(swduration!=value)
                {
                    swduration = value;
                    if(PropertyChanged!=null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("SWDuration"));
                    }
                }
            }
        }

        public string StartTimeText
        {
            get
            {
                return starttimetext;
            }
            set
            {
                if (starttimetext != value)
                {
                    starttimetext = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("StartTimeText"));
                    }
                }
            }
        }
    }
}

从界面开始,我有一个标签(单击它时在开始和停止之间切换),一个显示持续时间的标签,以及一个显示秒表启动时间的标签。

xamarin android 模拟器上的输出:_

  • 标签(开始/停止)效果很好
  • 持续时间也可以显示在显示屏上,但最后一个标签不会改变。
  • 其值保持在“计时器尚未启动”

我不明白为什么它不能用于最后一个标签,因为主体与第一个标签(开始/停止)相同。如果有人能指出我误解的地方或我的代码在哪一部分做错了,我会感谢你的。

谢谢。

【问题讨论】:

  • 首先我建议将所有变量设置为实例级别。我还建议向我们展示 xaml.cs 文件。
  • @NickPolideropoulos 我读了一些文章,按变量实例级别,你的意思是我不应该使用静态?
  • 如果答案有效,那么您应该使用复选标记将其标记为已接受。这样做会对每个人都有帮助。
  • @dev1998 完成 :)

标签: c# xaml xamarin.forms


【解决方案1】:

通过设置BindingContext

<Label.BindingContext>
    <local:GetDuration/>
</Label.BindingContext>

在每个Label 上,您分别为每个标签创建GetDuration 的新实例。因此,您调用 TapCommand 的实例将与附加到标签的实例不同。

通过将 XAML 更改为

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:stopwatch"
             x:Class="stopwatch.MainPage">
    <ContentPage.BindingContext>
        <local:GetDuration></local:GetDuration>
    </ContentPage.BindingContext>

    <StackLayout>
        <Label Text="{Binding LabelText}">
            <Label.GestureRecognizers>
                <TapGestureRecognizer
                    Command="{Binding TapCommand}">
                </TapGestureRecognizer>
            </Label.GestureRecognizers>
        </Label>
        <Label Text="{Binding SWDuration}" />
        <Label Text="{Binding StartTimeText}" />
    </StackLayout>
</ContentPage>

您正在创建一个 GetDuration 作为页面的绑定上下文。除非另有明确设置,否则 BindingContext 将传递给您页面的子页面,因此无需为每个标签设置它(不管它无论如何都不起作用)。

补充说明

事件调用器

您正在从每个属性中手动引发事件。创建一个为您执行此操作的事件调用器将为您节省一些代码并让您更加遵守 DRY

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

由于CallerMemberName 属性,调用属性的名称将自动传递(毕竟它是语法糖,编译器会为你做魔法)。属性将变为

public string StartTimeText
{
    get
    {
        return starttimetext;
    }
    set
    {
        if (starttimetext != value)
        {
            starttimetext = value;
            this.OnPropertyChanged();
        }
    }
}

决定的LabelText

void OnTapped(object s)
{
    if (LabelText == "START")
    {
        LabelText = "STOP";
        StartTimeText = DateTime.Now.ToString();
        hstopwatch.Restart();
    }
    else
    {
        LabelText = "START";
        StartTimeText = "Timer has been stopped";
        hstopwatch.Stop();
        hstopwatch.Reset();
    }
}

这可能是一个口味问题,但我不会让决定是开始还是停止时间取决于LabelText 的值,而是取决于内部boolenum。你做这件事的方式,你的内部和外部行为。毕竟是 OOP,你应该使用封装。

【讨论】:

  • 实施您的解决方案为我完成了这项工作。因此,通过在 xaml 上声明一个新的绑定上下文,C# 文件中的变量将被重新初始化吗?我理解正确吗?对于第一句话,谢谢,我会实施它,因为它可以节省我的行数和时间。对于第二个评论:最好调用一个函数,变量值将在哪里设置?非常感谢您花时间回答我的问题。
  • 是的,&lt;local:MyClass /&gt;“总是”创建一个新实例。 (好吧,并非总是如此,因为您可以定义一个静态工厂方法并实现单例模式 - 请参阅 here - 但虽然(技术上)您可以这样做,但这并不意味着您应该这样做。)
  • 非常感谢您抽出宝贵时间回答我的问题。
猜你喜欢
  • 1970-01-01
  • 2011-02-02
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 2019-12-18
  • 2021-10-23
  • 1970-01-01
相关资源
最近更新 更多