绑定的ObservableCollection中的对象属性更改不会更改UI值

时间:2020-08-04 15:02:34

标签: c# uwp uwp-xaml

我正在尝试用C#创建一个UWP应用程序,以控制家里的灯光。我能够从服务器获取数据并为每个单独的灯创建灯对象。然后将这些灯对象放置在应用程序开头的ObservableCollection中。该ObservableCollectionGridView绑定到DataTemplate。当应用启动时,我可以看到带有正确数据的灯。然后,我重新获取数据以检查是否每500毫秒更改任何灯泡属性。我可以清楚地看到对象属性已成功更新,但是绑定数据无法识别此更改。因此,UI也不更改。我尝试在Lamp类中使用NotifyPropertyChange,但这也没有执行。

经过大量的试验和错误后,我发现ui仅在我添加,删除或替换ObservableCollection中的对象时才更改,但是替换对我来说并不是一个实际的选择,因为它会导致很多不稳定并且看起来不像是必须解决此问题的方法。

<GridView ItemsSource="{x:Bind LampCollection}" Margin="10 0" HorizontalAlignment="Center">
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="local:Lamp">
                    <Border BorderBrush="#555555" BorderThickness="1" CornerRadius="8" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" >
                        <Grid Width="300" Height="200">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="2*"/>
                                <ColumnDefinition Width="3*"/>
                            </Grid.ColumnDefinitions>

                            <Grid.RowDefinitions>
                                <RowDefinition Height="2*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>

                            <Image Grid.Row="0"  Grid.Column="0" Source="{x:Bind ImageUri, Mode=OneWay}" Width="80"  HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" >
                                <TextBlock Name="txt"  VerticalAlignment="Bottom" FontSize="20" FontWeight="Bold" Margin="10,0,0,20" Text="{x:Bind Name, Mode=OneTime}"/>
                                <TextBlock Name="status" VerticalAlignment="Bottom" FontSize="11" FontWeight="Bold" Margin="10,0,0,20" Text="{x:Bind Status, Mode=OneWay}"/>
                            </StackPanel>

                            <Rectangle Grid.Row="1"  Grid.Column="0" Visibility="{x:Bind ColorLamp}"  Width="50" Height="50" Fill="Maroon"/>
                            <Slider Visibility="{x:Bind Dimmable}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="10,0,10,0" Value="{x:Bind Brightness, Mode=TwoWay}"/>
                        </Grid>
                    </Border>
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>

Xaml代码

lamp.SetStatus函数仅解析字符串并设置绑定到UI的属性Brightness和Status。

foreach (Lamp lamp in LampCollection) {
    string response = await GetAsync(UrlString + lamp.IDX.ToString());
    dynamic json = JsonConvert.DeserializeObject(response);
    if (json.status == "OK") {
        lamp.SetStatus(json.result[0].Status.ToString());
    }
}

C#更新代码

修改

我尝试按照Microsoft文档中的描述在我的灯类中实现INotifyPropertyChanged。它似乎什么也没做。我还尝试在NotifyPropertyChanged()函数中传递名称,但这只会使我的应用崩溃。

class Lamp : INotifyPropertyChanged {
        public uint IDX { get; internal set; }
        public string Name { get; internal set; }
        public bool Status { get; internal set; }
        public string ImageUri { get; internal set; }

        public bool Dimmable { get; internal set; }
        public bool ColorLamp { get; internal set; }
        public uint Brightness { get; set; }
        public float[] Color { get; set; }

        public Lamp(uint idx, string name, string status, bool dimmable, bool colorLamp) {
            IDX = idx;
            Name = name;
            Color = new float[3];
            Dimmable = dimmable;
            ColorLamp = colorLamp;
            if (status == "Off") {
                ImageUri = "Images/lamp-off.svg";
                Status = false;
            } else {
                ImageUri = "Images/lamp-on.svg";
                Status = true;

                if(dimmable) {
                    Brightness = uint.Parse(Regex.Match(status, @"\d+").Value, NumberFormatInfo.InvariantInfo);
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public void Switch(bool status) {
            Status = status;
            if(status) ImageUri = "Images/lamp-on.svg";
            else ImageUri = "Images/lamp-off.svg";
            NotifyPropertyChanged();
        }
        public void SetColor(float r, float g, float b) { if (ColorLamp) { Color[0] = r; Color[1] = g; Color[2] = b; } }

        public void SetStatus(string status) {
            if (status == "Off") {
                if (Status) {
                    ImageUri = "Images/lamp-off.svg";
                    Status = false;
                    if (Dimmable) Brightness = 0;
                    Debug.WriteLine(Name + "(" + IDX + ") has turned off");
                    NotifyPropertyChanged();
                }
            } else {
                if (Dimmable) {
                    uint _tmpBright = uint.Parse(Regex.Match(status, @"\d+").Value, NumberFormatInfo.InvariantInfo);
                    if(!Status || Brightness != _tmpBright) {
                        ImageUri = "Images/lamp-on.svg";
                        Status = true;
                        Brightness = _tmpBright;
                        Debug.WriteLine(Name + "(" + IDX + ") has turned on or changed brighntess");
                        NotifyPropertyChanged();
                    }
                } else {
                    if (!Status) {
                        ImageUri = "Images/lamp-on.svg";
                        Status = true;
                        Debug.WriteLine(Name + "(" + IDX + ") has turned on");
                        NotifyPropertyChanged();
                    }
                }
            }
        }
    }

1 个答案:

答案 0 :(得分:0)

基于代码段,您在SetStatus()方法中调用了 NotifyPropertyChanged ()方法,并且 CallerMemberName 允许您获取调用者的方法或属性名称对于该方法,如果不将任何propertyName传递给NotifyPropertyChanged()方法,它将自动获取方法名称为 SetStatus 。但是,没有与SetStatus绑定的UI,因此该UI将不会更新。如果要在这种情况下更新与Status和Brightness属性绑定的UI,可以将这两个属性名称传递给NotifyPropertyChanged()方法,例如:

public void SetStatus(string status)
{
    if (status == "Off")
    {
        if (Status)
        {
            ImageUri = "Assets/2.jpg";
            Status = false;
            if (Dimmable) Brightness = 0;
            Debug.WriteLine(Name + "(" + IDX + ") has turned off");
            NotifyPropertyChanged("Status");
            NotifyPropertyChanged("Brightness");
         }
    }
    ......
}

但是,每次在SetStatus()方法或Lamp类中的其他方法中更改Status和Brightness属性的值时,都需要调用NotifyPropertyChanged(“ xxx”)方法,有点复杂。您可以声明一个私有变量并覆盖get和set方法,在set方法中调用NotifyPropertyChanged()方法,每次为属性设置一个新值时,它将进入set方法,然后通知UI更新。以状态和亮度为例:

public class Lamp : INotifyPropertyChanged
{
    private bool status { get; set; }
    private uint brightness { get; set; }
   
    public bool Status {
        get {
            return status;
        }
        set {
            status = value;
            NotifyPropertyChanged();
        }
    }

    public uint Brightness
    {
        get
        {
            return brightness;
        }
        set
        {
            brightness = value;
            NotifyPropertyChanged();
        }
    }
    
    // The same behavior to the following properties
    public uint IDX { get; internal set; }
    public string Name { get; internal set; }

    public string ImageUri { get; internal set; }

    public bool Dimmable { get; internal set; }
    public bool ColorLamp { get; internal set; }
    
    public float[] Color { get; set; }

    public Lamp(uint idx, string name, string status, bool dimmable, bool colorLamp)
    {
        IDX = idx;
        Name = name;
        Color = new float[3];
        Dimmable = dimmable;
        ColorLamp = colorLamp;
        if (status == "Off")
        {
            ImageUri = "Assets/2.jpg";
            Status = false;
        }
        else
        {
            ImageUri = "Assets/3.jpg";
            Status = true;

            if (dimmable)
            {
                Brightness = uint.Parse(Regex.Match(status, @"\d+").Value, NumberFormatInfo.InvariantInfo);
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public void Switch(bool status)
    {
        Status = status;
        if (status) ImageUri = "Assets/3.jpg";
        else ImageUri = "Assets/2.jpg";
    }
    public void SetColor(float r, float g, float b) { if (ColorLamp) { Color[0] = r; Color[1] = g; Color[2] = b; } }

    public void SetStatus(string status)
    {
        if (status == "Off")
        {
            if (Status)
            {
                ImageUri = "Assets/2.jpg";
                Status = false;
                if (Dimmable) Brightness = 0;
            }
        }
        else
        {
            if (Dimmable)
            {
                uint _tmpBright = 30;
                if (!Status || Brightness != _tmpBright)
                {
                    ImageUri = "Assets/3.jpg";
                    Status = true;
                    Brightness = _tmpBright;
                }
            }
            else
            {
                if (!Status)
                {
                    ImageUri = "Assets/3.jpg";
                    Status = true;
                }
            }
        }
    }
}