我有一个位于ElementHost中的WPF用户控件,因此它可以在WinForms应用程序中使用。在这个WPF用户控件上有一个ComboBox,我试图将一个相当简单的自定义类的ObservableCollection数据绑定到它。这个ObservableCollection是我的“视图模型”类(MVVM方法并不完美)的公共属性,我将其绑定到用户控件(视图),并且我有一个在设计时绑定的模拟子类。
ComboBox完全按照XAML设计器中的预期呈现,但在运行时,它完全是空的,下拉区域大约有3行高,无论我向其中添加多少项(在应用程序的生命周期中,项的数量从未改变)。还有其他控件,比如TextBlocks和自制的NumericUpDown,它们绑定到其他属性,在运行时运行得很好,但ComboBox不合作。
还有一件事,如果有关系的话--视图模型示例是从一个文件反序列化的。我还没有在序列化(反序列化)中包含我添加到它的属性,但这很重要,因为示例将被创建,然后我必须调用一个函数来完成初始化,因为构造函数被绕过,而这个完成包括初始化数据绑定项集合。我不知道这是否与问题有关,但我想我应该提到它,以防万一。
物料类。如果用户选择新语言,说明文本可能会更改:
namespace Company._DataBinding {
public class FlavorOption {
public event PropertyChangedEventHandler PropertyChanged;
private string _descriptionText;
public EnumFlavor Flavor { get; set; }
public string DescriptionText {
get { return _descriptionText; }
set {
if (_descriptionText != value) {
_descriptionText = value;
try {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("DescriptionText"));
} catch (Exception anException) {
throw anException;
}
}
}
}
}
用户控件的简略XAML。代码隐藏在构造函数内部调用InitializeComponent并实现IProcessOrder。否则,它不会触及任何内容,包括JuiceChoicesText、JuiceFlavorOptions、ChosenJuice或此处的控件:
<UserControl x:Class="Company._Specifics.JuiceOrderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Company"
xmlns:bound="clr-namespace:Company._DataBinding"
mc:Ignorable="d"
d:DesignHeight="405" d:DesignWidth="612">
<Grid Background="White" d:DataContext="{d:DesignInstance Type=bound:MockProtoVM, IsDesignTimeCreatable=True}">
<StackPanel>
<TextBlock Text="{Binding JuiceChoicesText}" Grid.Column="2" />
<ComboBox Margin="5" ItemsSource="{Binding JuiceFlavorOptions}"
SelectedValuePath="Flavor" DisplayMemberPath="DescriptionText"
SelectedValue="{Binding ChosenJuice, Mode=TwoWay}" />
</StackPanel>
</Grid>
相关“视图模型”代码:
namespace Maf._Specifics {
public class ProtoVM : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _juiceChoicesText;
private EnumFlavor _chosenJuice;
public ObservableCollection<FlavorOption> JuiceFlavorOptions { get; set; }
public string JuiceChoicesText {
get { return _juiceChoicesText; }
set {
if (_juiceChoicesText != value) {
_juiceChoicesText = value;
NotifyListeners();
}
}
}
public EnumFlavor ChosenJuice {
get { return _chosenJuice; }
set {
if (_chosenJuice != value) {
_chosenJuice = value;
NotifyListeners();
}
}
}
public ProtoVM() {
FinishInitialization();
}
public void FinishInitialization() {
JuiceChoicesText = "Choose your juice flavor:";
if (JuiceFlavorOptions == null) {
JuiceFlavorOptions = new ObservableCollection<FlavorOption> {
new FlavorOption { Flavor = EnumFlavor.CHERRY, DescriptionText = "Cherry" },
new FlavorOption { Flavor = EnumFlavor.ORANGE, DescriptionText = "Orange" },
new FlavorOption { Flavor = EnumFlavor.GRAPE, DescriptionText = "Grape" }
};
ChosenJuice = EnumFlavor.CHERRY;
}
protected void NotifyListeners([CallerMemberName] string propertyName = "") {
try {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} catch (Exception anException) {
throw anException;
}
}
}
}
在其父控件中设置用户控件。在数据绑定之后,不会向ComboBox(的DataContext)添加或从ComboBox(的DataContext)删除任何选项:
var juiceOrderHost = new WPFWrapperUC();
var orderJuiceTab = new TabPage(tabName);
_juiceOrderView = new JuiceOrderView();
juiceOrderHost.SetChild(_juiceOrderView);
orderJuiceTab.Controls.Add((UserControl)juiceOrderHost);
tbcThingsToOrder.TabPages.Add(orderJuiceTab);
...
_juiceOrderKindaVM.FinishInitialization(); // _juiceOrderKindaVM is a ProtoVM
_juiceOrderView.DataContext = _juiceOrderKindaVM;
WinForms UserControl,它上面只有一个System.Windows.Forms.Integration.ElementHost:
namespace Company {
public partial class WPFWrapperUC : UserControl, IProcessOrder {
public WPFWrapperUC(UIElement hosting = null) {
InitializeComponent();
if (hosting != null)
SetChild(hosting);
}
public void SetChild(UIElement hosting) {
elhHostForWPFControl.Child = hosting;
}
// ...various methods that forward calls to
// IProcessOrder methods to elhHostForWPFControl.Child,
// should it be a IProcessOrder, which JuiceOrderView is.
}
}
mock:
namespace Company._DataBinding {
public class MockProtoVM : ProtoVM {
public MockProtoVM() : base() {
JuiceChoicesText = "User sees different text at runtime, like 'Choose your juice flavor:'";
ChosenJuice = EnumFlavor.ORANGE;
}
}
}
我错过了什么?谢谢...
1条答案
按热度按时间xeufq47z1#
经过更多的研究/spitballing,事实证明,在上面显示的DataContext赋值之前,DataContext在FinishInitialization被调用之前就被赋值给了ProtoVM示例,因此JuiceFlavorOptions为null。然后它在同一个示例上再次调用FinishInitialization,但显然设置JuiceFlavorOptions还不够?然后它将示例分配给DataContext,这修复了TextBlock,但没有修复ComboBox。至少,这是我对发生的事情的最佳猜测。
此外,我的FlavorOption.DescriptionText的翻译/交换需要的不仅仅是迭代JuiceFlavorOptions和更新每个FlavorOption的DescriptionText值。这将显示正确的文本时,选择一个新的选项,但下拉菜单仍将有所有旧选项的文本。您必须清除JuiceFlavorOptions并使用新的FlavorOption示例及其新的DescriptionText值重新填充它。为了使ComboBox看起来不被选中,你必须调用ChosenJuice上的属性changed delegate来完全更新绑定:
希望这能帮助那些设法进入我奇怪处境的人。