WPF:绑定更新时,绑定丢失 [英] WPF: Binding is lost when bindings are updated
问题描述
我有一个WPF应用程序,用于控制WCF-RESTful服务,即用于启动,初始化和停止它。因此,我有一个 MainWindow
UI,其中包含一个 UserControl
来配置设置。当我初始化服务时,一些数据会加载到 DependencyProperties
和 ObservableCollections
中,以在GUI中显示。这是我更新这些设置的方法的一部分:
I have a WPF-Application for controlling a WCF-RESTful service, i.e. for starting, initializing and stopping it. Therefore I have a MainWindow
UI which contains a UserControl
to configure settings. When I initialize my service, some data is loaded into DependencyProperties
and ObservableCollections
to display it in the GUI. Here is the part of the method where I update these settings:
public partial class MainWindow : Window {
private void InitializeService (bool reInitialize = false) {
var restService = (RestService)this.ServiceHost.SingletonInstance;
var settings = restService.GetSettings();
//UCSettings is the "x:name" of the embedded UserControl "UserControlSettings" in this window
this.UCSettings.ExecutionTimes.Clear();
settings.ExecutionTimes.ForEach(x => this.UCSettings.ExecutionTimes.Add(x));
this.UCSettings.TableConfigurationLoader = settings.Timer.Find(x => x.Name == "TableConfigLoader");
}
}
public partial class UserControlSettings : UserControl {
public ObservableCollection<ExecutionTime> ExecutionTimes { get; set; }
public static readonly DependencyProperty TableConfigurationLoaderProperty = DependencyProperty.Register("TableConfigurationLoader", typeof(Setting), typeof(UserControlSettings), new FrameworkPropertyMetadata(default(Setting)));
public Setting TableConfigurationLoader {
get { return (Setting)this.GetValue(TableConfigurationLoaderProperty); }
set { this.SetValue(TableConfigurationLoaderProperty, value); }
}
}
public class Setting {
public string Name { get; set; }
public bool IsEnabled { get; set; }
public int ExecutionTimeId { get; set; }
}
public class ExecutionTime {
public int Id { get; set; }
public string Value { get; set; }
}
在代码设计器中( UserControlSettings.xaml .cs
),这些属性在 ComboBox
的某些绑定中使用:
In the Code-Designer (UserControlSettings.xaml.cs
) these properties are used in some bindings for a ComboBox
:
<UserControl x:Class="InsightTool.Gui.UserControlSettings" x:Name="UCSettings">
<ComboBox x:Name="CbConfigLoadingExecutionTime" ItemsSource="{Binding ElementName=UCSettings, Path=ExecutionTimes}" DisplayMemberPath="Value" SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}" SelectedValuePath="Id"/>
</UserControl>
当我第一次使用 InitializeService $ c $加载数据时c>方法,一切正常。
ComboBox
填充了 ObservableCollection
的数据,匹配值由 ExecutionTimeId
。
当我尝试重新初始化服务时,我再次调用相同的方法,但是 SelectedValue
绑定不再起作用。我在调试器中检查了这些属性的值,但是再次在此方法中正确设置了它们。我在这里做错了什么?一些示例:
When I first load in the data with the InitializeService
method, everything works fine. The ComboBox
is filled with the data of the ObservableCollection
and the matching value is selected automatically by the ExecutionTimeId
.
When I try to "reinitialize" the service, I call the same method again, but the SelectedValue
binding does not work anymore. I checked the values of these properties in the debugger, but they are set correctly in this method again. What am I doing wrong here? Some samples:
正确显示第一次加载:
Correct display first load:
不正确的显示秒加载:
Incorrect display seconds load:
推荐答案
TableConfigurationLoader
是依赖项属性。这意味着很多事情,但是其中之一是,当您将 TableConfigurationLoader
的值更改为 Setting $ c $的另一个实例时c>,将引发一个事件,此
Binding
处理该事件并在组合框上更新 SelectedValue
:
TableConfigurationLoader
is a dependency property. That means a lot of things, but one of them is that when you change the value of TableConfigurationLoader
to a different instance of Setting
, an event is raised, and this Binding
handles that event and updates SelectedValue
on the combo box:
SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}"
但是, Setting.ExecutionTimeId
不是依赖项属性。这是.NET CLR的常规属性,其值更改时不会通知任何人。因此,如果您更改与 TableConfigurationLoader中相同的旧
Setting
的 ExecutionTimeId
属性, code>,没人知道,什么也没有发生。
However, Setting.ExecutionTimeId
isn't a dependency property. It's a regular .NET CLR property, which doesn't notify anybody of anything when its value changes. So if you change the ExecutionTimeId
property of the same old Setting
that's already in TableConfigurationLoader
, nobody knows and nothing happens.
由于设置
不是控件类,因此您并不需要或不希望其属性为依赖项属性。相反,您可以将其视为视图模型。在实现方面,所有viewmodel实际上就是实现 INotifyPropertyChanged
的任何类。通过对下面显示的 Setting
进行更改,如果我正确理解了您的问题,我认为绑定应该可以按您期望的那样工作。我更改了 IsEnabled
,因此它也会提高 PropertyChanged
;您可能实际上并不需要,但这只是说明性的。
Since Setting
is not a control class, you don't particularly need or want its properties to be dependency properties. Instead, you can treat it as a viewmodel. In implementation terms, all a viewmodel really is, is any class that implements INotifyPropertyChanged
. With changes to Setting
shown below, I think the binding should work as you expect, if I correctly understand your problem. I've changed IsEnabled
so it will raise PropertyChanged
as well; you may not actually need that, but it's illustrative.
您可能需要对 ExecutionTime
类执行相同的操作。
You may need to do the same with your ExecutionTime
class.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public class Setting : ViewModelBase
{
public string Name { get; set; }
#region IsEnabled Property
private bool _isEnabled = false;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
OnPropertyChanged();
}
}
}
#endregion IsEnabled Property
#region ExecutionTimeId
private int _executionTimeId = 0;
public int ExecutionTimeId
{
get { return _executionTimeId; }
set
{
if (value != _executionTimeId)
{
_executionTimeId = value;
OnPropertyChanged();
}
}
}
#endregion ExecutionTimeId
}
在WPF中,有三种(ish)机制用于通知事物属性已更改,如果您希望事物正确更新,则需要以某种方式使用一种或另一种:
There are three (ish) mechanisms in WPF for notifying things that properties have changed, and you need to be using one or another somehow if you want things to update correctly:
- 依赖项对象的依赖项属性:对于控件的属性
-
INotifyPropertyChanged
:视图模型的属性 -
INotifyCollectionChanged
:用于集合。
- Dependency properties of dependency objects: For properties of controls
INotifyPropertyChanged
: For properties of viewmodelsINotifyCollectionChanged
: For collections.
当您向其分配新的集合实例时,集合属性也应引发 INotifyPropertyChanged.PropertyChanged
。集合的给定实例在其内容更改时将处理引发自己的事件。
A collection property should also raise INotifyPropertyChanged.PropertyChanged
when you assign a new collection instance to it. A given instance of the collection will handle raising its own events when its contents change.
ObservableCollection< T>
和 ReadOnlyObservableCollection< T>
实现 INotifyCollectionChanged
,因此您不必这样做;正确实施该解决方案很麻烦,因此您真的不想去那里。
ObservableCollection<T>
and ReadOnlyObservableCollection<T>
implement INotifyCollectionChanged
so you don't have to; it's a big hassle to implement that one properly so you really don't want to go there.
这篇关于WPF:绑定更新时,绑定丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!