Изменение свойства объекта в привязанном ObservableCollection не меняет значения пользовательского интерфейса - PullRequest
0 голосов
/ 04 августа 2020

Я пытаюсь создать приложение UWP в C#, которое может управлять моим освещением в моем доме. Я могу получить данные с сервера и создать объекты лампы для каждой отдельной лампы. Эти объекты лампы затем помещаются в ObservableCollection в начале приложения. Этот ObservableCollection привязан к GridView с DataTemplate. Когда приложение запустилось, я вижу свои огни с правильными данными. Затем я повторно загружаю данные, чтобы проверять, менялось ли какое-либо свойство лампы каждые 500 мс. Я ясно вижу, что свойства объекта обновлены успешно, но связанные данные не распознают это изменение. Так что UI тоже не меняется. Я попытался использовать класс NotifyPropertyChange в классе Lamp, но это тоже ничего не дало.

После множества проб и ошибок я обнаружил, что пользовательский интерфейс изменяется только тогда, когда я добавляю, удаляю или заменяю объект в 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 просто анализирует строку и устанавливает свойства 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# код обновления

Edit

Я попытался реализовать INotifyPropertyChanged в своем классе лампы, как описано в документации Microsoft. Однако, похоже, это ничего не делает. Я также попытался передать имя в функции NotifyPropertyChanged(), но это сделало мое приложение только sh.

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 голосов
/ 05 августа 2020

На основе фрагмента кода вы вызвали метод NotifyPropertyChanged () в методе SetStatus (), а CallerMemberName позволяет получить имя метода или свойства вызывающего объекта для Если вы не передадите свойство propertyName методу NotifyPropertyChanged (), он автоматически получит имя метода SetStatus . Однако пользовательский интерфейс не связан с SetStatus, поэтому пользовательский интерфейс не обновляется. Если вы хотите обновить пользовательский интерфейс, связанный со свойствами Status и Brightness в этом сценарии, вы можете передать эти два имени свойств методу 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");
         }
    }
    ......
}

Однако, каждый раз, когда вы меняете значения свойств Status и Brightness в методе SetStatus () или других методах в вашем классе Lamp, вам нужно вызывать метод NotifyPropertyChanged («xxx»), это немного сложно. Вы можете объявить частную переменную и переопределить методы get и set в методе set, вызывая метод NotifyPropertyChanged (), каждый раз, когда устанавливаете новое значение для вашего свойства, он вводит метод set и затем уведомляет пользовательский интерфейс об обновлении. Возьмите в качестве примеров Статус и Яркость:

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;
                }
            }
        }
    }
}
...