Xamarin Forms不能改变变量值

时间:2018-02-13 23:02:28

标签: c# xaml xamarin.forms

我想制作一个带有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#-class:

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"));
                    }
                }
            }
        }
    }
}

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

xamarin android模拟器上的输出:_

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

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

谢谢。

1 个答案:

答案 0 :(得分:2)

通过

设置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的价值,而是取决于内部bool或{{ 1}}。你这样做的方式,你的内部和外部行为。毕竟它是OOP,你应该使用封装。