如何根据变为动态的静态 属性 重新绑定 SourceItems?
How to rebind SourceItems based on a static property that becomes dynamic?
在我的 XAML 代码中,我有一个绑定到静态 属性 的组合框,如下所示。
<ComboBox x:Name="DifferentKinds"
ItemsSource="{x:Static local:MainWindow.DifferentKinds}"/>
以及 属性 的代码及其来源。
public static Kind[] DifferentKinds
=> (Kind[])Enum.GetValues(typeof(Kind));
public enum Kind { WeeHee, BuuHuu }
刚知道以后还会有更多种类。它们不会特别频繁地创建,但不确定随着时间的推移它们会变成多少。因此,我不会向 enum 添加新元素,而是从数据库中读取这些元素。
为了示例的简单性,假设我们每次访问 属性 时都读入这些值。该解决方案成为一个私有字段,在 开始执行 InitializeComponent() 之前从数据库中读取。然后,我仍然将这些值作为静态 属性 提供,就像这样。
public MainWindow()
{
PopulateDifferentKinds();
InitializeComponent();
}
private static IEnumerable<Kind> _allDifferentKinds;
public static IEnumerable<Kind> AllDifferentKinds
=> _allDifferentKinds.Where(element => element.Active);
public class Kind
{
public String Name { get; set; }
public bool Active { get; set; }
public override string ToString() { return Name; }
}
- 这种方法是否会造成我想念的大问题?
- 有没有更好的方法将炸弹框中的项目绑定到数据库中的值?
我在这里看到的主要问题是在视图的构造函数中调用 PopulateDifferentKinds 方法会产生性能问题。虽然此方法是 运行 并且正在查询数据库,但您的 UI 正在被阻止。
这可以通过使用 class 在后台线程上加载数据并使用 PropertyChanged 事件来表示数据已加载来改进:
public class Kind
{
public string Name { get; set; }
public bool Active { get; set; }
public int Value { get; set; }
}
public class AppEnumValues : INotifyPropertyChanged
{
private static readonly Lazy<AppEnumValues> current
= new Lazy<AppEnumValues>(() => new AppEnumValues(), LazyThreadSafetyMode.ExecutionAndPublication);
public static AppEnumValues Current
{
get { return current.Value; }
}
public Kind[] AllDifferentKinds { get; private set; }
public bool IsLoaded { get; private set; }
private AppEnumValues()
{
Task.Run(() => this.LoadEnumValuesFromDb())
.ContinueWith(t => this.OnAllPropertiesChanged());
}
protected virtual void OnAllPropertiesChanged()
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(string.Empty));
}
}
private void LoadEnumValuesFromDb()
{
// This simulates some latency
Thread.Sleep(2000);
// Call your data service here and load the values
var kinds = new[]
{
new Kind {Active = true, Name = "WeeHee", Value = 1},
new Kind {Active = true, Name = "BuuHuu", Value = 2}
};
this.AllDifferentKinds = kinds;
this.IsLoaded = true;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
您可以使用应用程序中需要的每个可扩展 "enum" 的属性来扩展它。实现单例模式,这将在第一次使用时在后台加载其数据。您可以像这样绑定组合框:
<ComboBox ItemsSource="{Binding Source={x:Static wpfApplication2:AppEnumValues.Current},Path=AllDifferentKinds}"
IsEnabled="{Binding Source={x:Static wpfApplication2:AppEnumValues.Current},Path=IsLoaded}"
DisplayMemberPath="Name" />
加载数据时,组合框将被禁用。
我建议研究 MVVM 和依赖注入。这将增强您的 WPF 应用程序体系结构并使事情变得简单:您不会提供静态 属性 或单例,它们的可测试性和可扩展性很差,但您可以使用构造函数注入将 AppEnumValues 提供程序提供给您的视图建模然后将您的视图绑定到它。
在我的 XAML 代码中,我有一个绑定到静态 属性 的组合框,如下所示。
<ComboBox x:Name="DifferentKinds"
ItemsSource="{x:Static local:MainWindow.DifferentKinds}"/>
以及 属性 的代码及其来源。
public static Kind[] DifferentKinds
=> (Kind[])Enum.GetValues(typeof(Kind));
public enum Kind { WeeHee, BuuHuu }
刚知道以后还会有更多种类。它们不会特别频繁地创建,但不确定随着时间的推移它们会变成多少。因此,我不会向 enum 添加新元素,而是从数据库中读取这些元素。
为了示例的简单性,假设我们每次访问 属性 时都读入这些值。该解决方案成为一个私有字段,在 开始执行 InitializeComponent() 之前从数据库中读取。然后,我仍然将这些值作为静态 属性 提供,就像这样。
public MainWindow()
{
PopulateDifferentKinds();
InitializeComponent();
}
private static IEnumerable<Kind> _allDifferentKinds;
public static IEnumerable<Kind> AllDifferentKinds
=> _allDifferentKinds.Where(element => element.Active);
public class Kind
{
public String Name { get; set; }
public bool Active { get; set; }
public override string ToString() { return Name; }
}
- 这种方法是否会造成我想念的大问题?
- 有没有更好的方法将炸弹框中的项目绑定到数据库中的值?
我在这里看到的主要问题是在视图的构造函数中调用 PopulateDifferentKinds 方法会产生性能问题。虽然此方法是 运行 并且正在查询数据库,但您的 UI 正在被阻止。
这可以通过使用 class 在后台线程上加载数据并使用 PropertyChanged 事件来表示数据已加载来改进:
public class Kind
{
public string Name { get; set; }
public bool Active { get; set; }
public int Value { get; set; }
}
public class AppEnumValues : INotifyPropertyChanged
{
private static readonly Lazy<AppEnumValues> current
= new Lazy<AppEnumValues>(() => new AppEnumValues(), LazyThreadSafetyMode.ExecutionAndPublication);
public static AppEnumValues Current
{
get { return current.Value; }
}
public Kind[] AllDifferentKinds { get; private set; }
public bool IsLoaded { get; private set; }
private AppEnumValues()
{
Task.Run(() => this.LoadEnumValuesFromDb())
.ContinueWith(t => this.OnAllPropertiesChanged());
}
protected virtual void OnAllPropertiesChanged()
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(string.Empty));
}
}
private void LoadEnumValuesFromDb()
{
// This simulates some latency
Thread.Sleep(2000);
// Call your data service here and load the values
var kinds = new[]
{
new Kind {Active = true, Name = "WeeHee", Value = 1},
new Kind {Active = true, Name = "BuuHuu", Value = 2}
};
this.AllDifferentKinds = kinds;
this.IsLoaded = true;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
您可以使用应用程序中需要的每个可扩展 "enum" 的属性来扩展它。实现单例模式,这将在第一次使用时在后台加载其数据。您可以像这样绑定组合框:
<ComboBox ItemsSource="{Binding Source={x:Static wpfApplication2:AppEnumValues.Current},Path=AllDifferentKinds}"
IsEnabled="{Binding Source={x:Static wpfApplication2:AppEnumValues.Current},Path=IsLoaded}"
DisplayMemberPath="Name" />
加载数据时,组合框将被禁用。
我建议研究 MVVM 和依赖注入。这将增强您的 WPF 应用程序体系结构并使事情变得简单:您不会提供静态 属性 或单例,它们的可测试性和可扩展性很差,但您可以使用构造函数注入将 AppEnumValues 提供程序提供给您的视图建模然后将您的视图绑定到它。