无法将数据绑定到 WinForms 应用程序的 WPF 控件中的 ComboBox;设计师做工不错

问题描述 投票:0回答:1

我有一个位于 ElementHost 中的 WPF 用户控件,因此可以在 WinForms 应用程序中使用它。在这个 WPF 用户控件上有一个 ComboBox,我试图将一个相当简单的自定义类的 ObservableCollection 数据绑定到它。这个 ObservableCollection 是我的“视图模型”类(MVVM 方法并不完美)的公共属性,我将其绑定到用户控件(视图),并且我有一个在设计时绑定的模拟子类。

ComboBox 在 XAML 设计器中完全按照预期呈现,但在运行时它完全是空的,下拉区域大约 3 行高,无论我向其中添加多少项目(项目数量在应用程序的生命周期中永远不会改变) )。还有其他控件,如 TextBlocks 和自制的 NumericUpDown,可以绑定到其他属性并在运行时运行良好,但 ComboBox 不会配合。

另一件事,如果重要的话——视图模型实例是从文件反序列化的。我还没有在(反)序列化中包含我添加到它的属性,但这很重要,因为将创建实例,然后我必须调用一个函数来完成初始化,因为构造函数被绕过,并且完成-up 包括初始化项目的数据绑定集合。我不知道这是否与问题有关,但我想我会提到它,以防万一。

项目类别。如果用户选择新语言,描述文本可能会更改:

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)添加或删除任何选项:

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

模拟:

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

那么,我错过了什么?谢谢...

wpf winforms data-binding combobox elementhost
1个回答
0
投票

经过更多研究/吐槽,事实证明,在执行上面所示的 DataContext 分配之前,在调用 FinishInitialization 之前,DataContext 先前已分配给 ProtoVM 实例,因此 JuiceFlavorOptions 为 null。然后它在同一个实例上再次调用 FinishInitialization,但显然设置 JuiceFlavorOptions 还不够?然后,它将实例分配给 DataContext,这修复了 TextBlock,但没有修复 ComboBox。至少,这是我对发生的事情的最好猜测。

此外,我对 FlavorOption.DescriptionText 的翻译/交换需要的不仅仅是迭代 JuiceFlavorOptions 和更新每个 FlavorOption 的 DescriptionText 值。当选择新选项时,这将显示正确的文本,但下拉列表仍将包含所有旧选项的文本。您必须清除 JuiceFlavorOptions 并使用新的 FlavorOption 实例及其新的 DescriptionText 值重新填充它。为了防止 ComboBox 看起来未选中,您必须调用 ChosenJuice 上的属性更改委托来完全更新绑定:

  JuiceFlavorOptions.Clear();
  // French
  JuiceFlavorOptions.Add(new FlavorOption { Flavor = EnumFlavor.CHERRY, DescriptionText = "Cerise" });
  JuiceFlavorOptions.Add(new FlavorOption { Flavor = EnumFlavor.ORANGE, DescriptionText = "Orange" });
  JuiceFlavorOptions.Add(new FlavorOption { Flavor = EnumFlavor.GRAPE, DescriptionText = "Raisin" });
  NotifyListeners("ChosenJuice");

希望这可以帮助那些设法进入我的奇怪情况的人。

© www.soinside.com 2019 - 2024. All rights reserved.