在WPF中强制绑定

时间:2011-03-22 19:37:09

标签: wpf unit-testing data-binding

我正在编写测试,以检查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 :回答有关我测试的内容和我的原因的问题。我不打算测试像BindingBindingBase等类正在按预期工作,我认为它们正在工作。我正在尝试在我的所有XAML文件中测试我已正确编写绑定。因为绑定是字符串类型的东西,所以它们在编译期间不会被验证,并且默认情况下它们只会导致输出窗口中的错误,我偶尔会错过这些错误。因此,如果我们从上面拿出我的示例,如果我们在绑定中发出错字:{Binding Lengthhh}那么我的测试将通知您没有可用于绑定的名称为Lengthhh的属性。所以我有大约100个XAML文件,每个XAML我都有一个测试(3-5行代码),在启动我的测试后,我知道肯定我的解决方案中没有绑定错误。< / p>

7 个答案:

答案 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的绑定会在不调用ShowShowDialog的情况下运行,因为这是与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();