我已经定义了以下组合框:
<ComboBox x:Name="cmbCurrency"
ItemsSource="{Binding IsoCurrenciesList}"
DisplayMemberPath="Description"
SelectedValuePath="LocaleID"
SelectedValue="{Binding CurrencyId, Mode=TwoWay">
</ComboBox>
其中IsoCurrenciesList
是IEnumerable<IsoCurrency>
- 我们定义的类型,并在视图模型中声明为:
private IEnumerable<IsoCurrency> isoCurrenciesList;
public IEnumerable<IsoCurrency> IsoCurrenciesList
{
get { return isoCurrenciesList; }
set
{
isoCurrenciesList = value;
RaisePropertyChangedEvent("IsoCurrenciesList");
}
}
我的单元测试会创建一个视图和视图模型的实例,并在本地列表中设置一些虚拟货币数据:
[TestInitialize]
public void TestInit()
{
_target = new View();
_viewModel = new ViewModel();
var ukp = new IsoCurrency { Code = "GBP", Description = "Pound Sterling", LocaleID = 826 };
var usd = new IsoCurrency { Code = "USD", Description = "US Dollar", LocaleID = 840 };
var eur = new IsoCurrency { Code = "EUR", Description = "Euro", LocaleID = 978 };
_currencies = new List<IsoCurrency> { ukp, usd, eur };
GetUIElement<Grid>("LayoutRoot").DataContext = _viewModel;
}
private T GetUIElement<T>(string name) where T : UIElement
{
return (T)_target.FindName(name);
}
然后调用测试方法。这应该设置货币ComboBox.Items
(通过ItemsSource
属性)
[Asynchronous]
[TestMethod]
public void TestCurrencySelection()
{
_target.Loaded += (s, e) =>
{
// Set the currency list explicitly
_viewModel.IsoCurrenciesList = _currencies;
var currencyCombo = GetUIElement<ComboBox>("cmbCurrency");
// This assert fails as Items.Count == 0
CollectionAssert.AreEquivalent(currencyCombo.Items, _currencies, "Failed to data-bind currencies.");
EnqueueTestComplete();
};
TestPanel.Children.Add(_target);
}
我遵循了Jeremy Likeness's blog的指南,但我无法通过绑定测试。
我已经尝试过测试其他属性的绑定 - 简单的字符串,布尔值和整数,但是两端的更改都没有反映到另一端。
我唯一能想到的是,在将视图添加到TestPanel
以“激活”绑定后,我需要做另一个步骤,但我不知道它可能是什么。< / p>
更新
我应该指出代码在实际应用程序中运行良好。基于评论(特别是来自Adam Sills的评论),看起来问题在于我没有发布的代码 - 也就是说,这是我们构建XAML的方式或者存在差异我们设置DataContext
的方式。至少我可以把精力集中在(希望)正确的领域。
视图中控件的位置似乎很重要。页面XAML是这样的:
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
...
</VisualStateManager.VisualStateGroups>
<toolkit:BusyIndicator x:Name="activityControl"
IsBusy="{Binding IsBusy}"
BusyContent="{Binding BusyContent}" >
<Grid>
... The page definition including sub grids, stack panels
and the combo box I'm testing along with other controls
<ops:SaveConfirmation Grid.Row="1" Margin="5"
x:Name="saveConfirmation"
SavedState="{Binding VendorSaved, Mode=TwoWay}" />
</Grid>
</toolkit:BusyIndicator/>
</Grid>
BusyIndicator
是Silverlight Toolkit中的一个,SaveConfirmation
是我们编写的控件。
如果我测试IsBusy
上的BusyIndicator
绑定按预期工作。但是,如果我测试失败的SavedState
上的SaveConfirmation
绑定 - 我在测试中将VendorSaved
属性设置为true,但是当我获得控件时绑定值为false。
var busyIndicator = GetUIElement<BusyIndicator>("activityControl");
Assert.AreEqual(busyIndicator.IsBusy, _viewModel.IsBusy, "Failed to data-bind busy indicator.");
var saveConfirmation = GetUIElement<SaveConfirmation>("saveConfirmation");
Assert.AreEqual(saveConfirmation.SavedState, _viewModel.VendorSaved, "Failed to data-bind saved state");
所以第一次测试通过,但第二次测试失败。
我需要做些什么才能确保设置树中所有元素的绑定?
答案 0 :(得分:2)
我的猜测是因为你在Loaded处理程序中执行_viewModel.IsoCurrenciesList = _currencies;
而不是填充现有的ObservableCollection属性,就像博客中的示例一样。在Dispatcher上的当前调用完成之后,绑定可能不会更新。虽然我对Silverlight不太熟悉,但肯定会这么说。
要对此进行测试,您可以尝试在将viewModel设置为控件上的DataContext之前设置_viewModel.IsoCurrenciesList
。
答案 1 :(得分:1)
您已为您的ComboBox添加了XAML,但未包含页面的其余部分。在您的测试中,您有GetUIElement<Grid>("LayoutRoot").DataContext = _viewModel;
。在您关注的示例中,他定义:
<Grid x:Uid="LayoutRoot" x:Name="LayoutRoot" Background="White"
DataContext="{Binding Source={StaticResource VMLocator},Path=Cascadia}">
然后他正在测试绑定的控件直接嵌套在该网格中。您的页面设置方式是否相同?
答案 2 :(得分:1)
感谢Joel C和Adam Sills我已经开始工作了。
线索是,在示例中,正在测试的控件是LayoutRoot
的直接子项,实际上当我在页面上测试这些控件时,这些测试也通过了。
所以我有两个解决方案:
1)更改测试触发的事件:
[TestMethod]
[Description("Tests that the currency ComboBox is databound correctly")]
public void TestCurrencySelection()
{
_target.LayoutUpdated += (s, e) =>
{
SetupViewModel();
var currencyCombo = GetUIElement<ComboBox>("cmbCurrency");
CollectionAssert.AreEquivalent(currencyCombo.Items,
_currencies,
"Failed to data-bind currencies list.");
Assert.AreEqual(currencyCombo.SelectedValue,
_viewModel.CurrencyId,
"Failed to data-bind selected currency.");
};
TestPanel.Children.Add(_target);
}
在视图为Loaded
之后,绑定才会初始化,但它们会在LayoutUpdated
触发时生成。通过进行此更改,我现在可以可靠地测试可视树的任何级别的绑定。
如果我在一次测试中测试页面上的所有绑定,那就没关系 - 这不是很好的做法。
2)使用我正在测试的控件的父UIElement
而不是LayoutRoot
,并仍处理Loaded
事件。这意味着我必须为每个容器元素添加名称,但这确实意味着我可以更逻辑地拆分测试。