我正在编写测试,以检查XAML中指定的Binding
元素的正确性。它们到目前为止工作,唯一的问题是我不知道如何正确地强制数据绑定发生。令人惊讶的是,仅仅在DataContext
中设置内容是不够的,在显示控件/窗口之前不会发生绑定。请注意,我正在写'单元'测试,我想避免显示任何窗口。
看一下以下代码:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
这里我正在创建一个Window并将一个数组作为DataContext
放到该窗口中。我将Window.Title
绑定到Array.Length
,因此我希望在控制台中看到数字5
。但是直到我Show
窗口(注释行),我才会得到空字符串。如果我取消注释该行,那么我将在控制台输出中收到所需的5
。
有没有办法在不显示窗口的情况下进行绑定?在启动测试时查看~20个窗口非常烦人。
P.S。:我知道我可以让窗户更透明等等,但我正在寻找更优雅的解决方案。
更新以上代码是我真正拥有的简化版本。在实际代码中,我收到View
(带绑定的UIElement
)和object ViewModel
。我不知道在View
上设置了哪个完全绑定,但我仍然希望所有这些都被初始化。
更新2 :回答有关我测试的内容和我的原因的问题。我不打算测试像Binding
,BindingBase
等类正在按预期工作,我认为它们正在工作。我正在尝试在我的所有XAML文件中测试我已正确编写绑定。因为绑定是字符串类型的东西,所以它们在编译期间不会被验证,并且默认情况下它们只会导致输出窗口中的错误,我偶尔会错过这些错误。因此,如果我们从上面拿出我的示例,如果我们在绑定中发出错字:{Binding Lengthhh}
那么我的测试将通知您没有可用于绑定的名称为Lengthhh
的属性。所以我有大约100个XAML文件,每个XAML我都有一个测试(3-5行代码),在启动我的测试后,我知道肯定我的解决方案中没有绑定错误。< / p>
答案 0 :(得分:3)
调度程序使用DispatcherPriority.DataBind更新绑定 - 因此,如果您等待具有SystemIdle优先级的虚拟任务,则可以确保完成任何挂起的数据绑定。
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
答案 1 :(得分:1)
如果您要测试视图的正确性,建议您测试一下您的观点: - )
为什么不从单元测试运行UI并编写在更改数据后检查UI内容的代码。
VS2010确实有GUI测试,或者您可以查看Snoop等工具的代码。
<小时/> 编辑以下评论:
如果您要做的只是测试一些简单的绑定,请尝试使用视图模型上的反射和XAML上的正则表达式编写作为后期构建事件运行的静态代码测试。在VM上添加属性或使用配置文件,以便您的测试将知道哪个视图接收哪个View Model作为DataContext。将View Models中的属性名称和类型与View中的绑定字符串进行比较(自动搜索XAML)并在字符串不匹配时抛出异常(因此构建失败)。
如果您的绑定更复杂(转换器,多重绑定......),实现起来可能会有点复杂。
答案 2 :(得分:0)
我认为你应该首先设置DataContext,然后进行Binding,例如:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
我不确定这是否是您的一般问题的真正解决方案,但它适用于这种情况。
答案 3 :(得分:0)
我不相信Window
的绑定会在不调用Show
或ShowDialog
的情况下运行,因为这是与UI消息循环/调度程序关联的唯一方式。
您最好的选择是将其设置为尽可能不可见,可能使用扩展方法进行清理:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
答案 4 :(得分:0)
我遇到了同样的问题,来自sixlettervarariables给了我一个想法。这很简单。 我在WinForms应用程序中使用WPF,因此我使用ElementHost控件来托管WinForms控件上的Wpf控件。要强制执行WinForms控件初始化,您只需读取Handle的值(实际上是Windows HWND),这将强制控件完全初始化自身,包括子ElementHost和所有Wpf绑定工作。 我没有尝试为纯Wpf控件执行相同的操作。但您可以轻松地使用ElementHost初始化您的Wpf控件,如下所示:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS:发现,一切都运行得更好,如果你先设置DataContext然后强制创建一个Handle(就像我的例子)。但是,我认为,这已经是你的情况,所以不应该是一个问题。
答案 5 :(得分:0)
您是否尝试过使用IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
同时检查一下:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
我的另一个问题是你为什么要尝试对经过技术测试的东西进行UNIT测试?顺便说一句,我不是批评,只是想要更好地理解。
答案 6 :(得分:-1)
不确定,但也许这样的东西会起作用?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();