【问题标题】:Binding ObservableCollection<class> fields to ListBox DataTemplate将 ObservableCollection<class> 字段绑定到 ListBox DataTemplate
【发布时间】:2018-08-01 16:10:39
【问题描述】:

我是一名刚刚完成暑期实习的学生,在开学前我带了一个项目回家做短暂的工作。这个项目中有一个秒表,我宁愿使用绑定到我的 ListBox 的 ObservableCollection 来作为我的分段时间,而不是使用 listbox.Items.Add()。当我添加到 ObservableCollection 时,ListBox UI 不会更新。谁能指出我错过了什么或做错了什么的正确方向?

我有我的 TimeSplits 课程:

public class TimeSplits : INotifyPropertyChanged
{

    private int _hours;
    private int _minutes;
    private int _seconds;

    public int hours
    {
        get
        {
            return _hours;
        }
        set
        {
            _hours = value;
            NotifyPropertyChanged(hours);
        }
    }
    public int minutes
    {
        get
        {
            return _minutes;
        }
        set
        {
            _minutes = value;
            NotifyPropertyChanged(minutes);
        }
    }
    public int seconds
    {
        get
        {
            return _seconds;
        }
        set
        {
            _seconds = value;
            NotifyPropertyChanged(seconds);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(int propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(propertyName)));
        }
    }

    public override string ToString()
    {
        return hours.ToString() + ":" + minutes.ToString() + ":" + seconds.ToString();
    }
}

以及我页面中的 ObservableCollection:

public partial class StopwatchPage : Page , INotifyPropertyChanged
{
...
    public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
...
    public StopwatchPage()
    {
        DataContext = this;
        InitializeComponent();
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += new EventHandler(stopwatchTimer);
    }
...
    private void splitButton_Click(object sender, RoutedEventArgs e)
    {
        TimeSplits split = new TimeSplits();
        split.hours = Hours;
        split.minutes = Minutes;
        split.seconds = Seconds;
        splits.Add(split);
    }
...
}

还有我的 xaml:

<ListBox x:Name="newSplitListBox" HorizontalAlignment="Left" Margin="139,0,0,47" Width="185" Height="268" VerticalAlignment="Bottom" ItemsSource="{Binding splits}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding hours}"/>
                    <TextBlock Text="{Binding minutes}"/>
                    <TextBlock Text="{Binding seconds}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

我确信这是我不知道的小事,因为我今年夏天才开始学习数据绑定。任何帮助是极大的赞赏!提前致谢。

【问题讨论】:

    标签: c# data-binding observablecollection


    【解决方案1】:

    您的 nameof() 似乎放错了位置。您当前的代码读取方式,它总是将“propertyName”的值作为更改的属性的名称发送,而不管实际更改的属性是什么。

    试试这个:

    public int hours
    {
        get
        {
            return _hours;
        }
        set
        {
            _hours = value;
            NotifyPropertyChanged();
        }
    }
    

    然后,在您的NotifyPropertyChanged() 中,执行以下操作:

    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
        }
    }
    

    编辑:为以下内容添加了修复:

    此外,ObservableCollection 必须是一个属性。更改此代码:

    public ObservableCollection<TimeSplits> splits = new ObservableCollection<TimeSplits>();
    

    到这里:

    public ObservableCollection<TimeSplits> Splits { get; set; } = new ObservableCollection<TimeSplits>();
    

    我从 Xamarin 的 ViewModel 模板中学到了一个对我帮助很大的技巧。这是它生成的处理可观察视图模型的代码(很像 ObservableCollection)。

        protected bool SetProperty<T>(ref T backingStore, T value,
            Action onChanged = null,
            [CallerMemberName]string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;
    
            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            return true;
        }
    
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;
    
            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    

    然后,要使用它,只需将其添加到您的属性中:

    private string _title = string.Empty;
    public string Title
    {
        get => _title;
        set => SetProperty(ref _title, value);
    }
    

    【讨论】:

    • 是否需要将 Xamarin 的 ViewModel 技巧添加到我的代码中才能正常工作?我使用 nameof 进行了更改,但 UI 仍然没有更新。 (我在这个项目中没有使用 MVVM 方法,只是一个个人项目,尝试一些新的编码想法)
    • 我没有在我的主页或秒表页面上看到绑定上下文。我会把它设置成什么?
    • 它看起来像是用于 MVVM,我只是使用带有页面的 NavigationWindow 来回导航。
    • 您不必使用 Xamarin 使用的完全相同的方法,我只是将其添加到答案中以便为您提供选择。但是,我建议在您的NotifyPropertyChanged() 方法中使用[CallerMemberName] 功能,如下所述:msdn.microsoft.com/en-us/library/…
    • @Bbylsma - 我更新了答案以包含对 ObservableCollection 的修复。它需要是页面上的属性,而不仅仅是字段。
    猜你喜欢
    • 2014-09-28
    • 2012-12-16
    • 2018-09-11
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 2013-10-15
    相关资源
    最近更新 更多