Силовое связывание в WPF
Я пишу тесты, которые будут проверять правильность элементов Binding
, указанных в XAML. Они работают до сих пор, единственная проблема заключается в том, что я не знаю, как правильно заставить привязку данных произойти. Удивительно, что недостаточно просто установить что-то в DataContext
, привязка не произойдет, пока вы не покажете свой элемент управления/окно. Пожалуйста, не то, что я пишу "unit" -тесты, и я бы хотел не показывать какие-либо окна.
Взгляните на следующий код:
// 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);
}
}
Здесь я создаю Окно и помещаю массив в DataContext
в это окно. Я привязываю Window.Title
к Array.Length
, поэтому я ожидаю увидеть номер 5
, напечатанный на консоли. Но до тех пор, пока окно Show
(строка комментариев) не получит пустую строку. Если я раскомментирую эту строку, тогда я получу желаемый 5
в выводе консоли.
Есть ли способ сделать привязку, не показывая окно? Весьма раздражает смотреть на ~ 20 окон при запуске тестов.
P.S.: Я знаю, что я могу сделать окна более прозрачными и т.д., но я ищу более элегантное решение.
UPDATE Код выше - это упрощенная версия того, что у меня действительно есть. В реальном коде я получаю View
(некоторые UIElement
с привязками) и object ViewModel
. Я не знаю, какая именно привязка была установлена на View
, но я все же хочу, чтобы все они были инициализированы.
ОБНОВЛЕНИЕ 2: Отвечая на вопросы относительно того, что я тестирую, и почему. Я не намерен тестировать, что классы, такие как Binding
, BindingBase
и т.д. Работают как ожидалось, я предполагаю, что они работают. Я пытаюсь проверить, что во всех моих файлах XAML я правильно написал привязки. Поскольку привязки являются строгими типизированными вещами, они не проверяются во время компиляции, и по умолчанию они вызывают только ошибки в окне вывода, которые я иногда пропускаю. Поэтому, если мы возьмем мой пример сверху и если мы сделаем опечатку там в bind: {Binding Lengthhh}
, тогда мои тесты уведомят вас о том, что для привязки нет свойства с именем Lengthhh
. Поэтому у меня около 100 файлов XAML, и для каждого XAML у меня есть тест (3-5 строк кода), и после запуска моих тестов я знаю для уверенного, что в моем решении нет ошибок привязки.
Ответы
Ответ 1
Если вы пытаетесь проверить правильность своего представления, я предлагаю вам проверить ваше мнение: -)
Почему бы не запустить пользовательский интерфейс из unit test и написать код, который проверяет содержимое пользовательского интерфейса после изменения данных.
VS2010 имеет тестирование GUI, или вы можете взглянуть на код таких инструментов, как Snoop.
<ч/" > Изменить следующий комментарий:
Если ВСЕ, что вы хотите сделать, это проверить несколько простых привязок, попробуйте написать статический тест кода, который запускается как событие пост-сборки, используя отражение на моделях просмотров и регулярных выражений на XAML. Добавьте атрибуты в виртуальную машину или используйте файл конфигурации, чтобы ваш тест знал, какой вид получает, который рассматривает модель как DataContext. Сравните имена и типы свойств в View Model со строками привязки в представлении (автоматически для поиска XAML для них) и генерируйте исключение (таким образом, сбой сборки), если строки не совпадают.
Если ваши привязки более сложны (преобразователи, multibindings,...), это может быть несколько сложнее реализовать.
Ответ 2
Связи обновляются диспетчером с DispatcherPriority.DataBind - поэтому, если вы ждете фиктивной задачи с приоритетом SystemIdle, вы уверены, что все ожидающие привязки данных выполняются.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
Ответ 3
Я думаю, вы должны сначала установить DataContext, а затем выполнить привязку, например:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
Я не уверен, что это реальное решение для вашей общей проблемы, но оно работает в этом случае.
Ответ 4
Я не верю, что привязки Window
будут выполняться без вызова Show
или ShowDialog
, потому что это единственный способ связать его с конвейером/диспетчером пользовательского интерфейса.
Лучше всего было бы установить его как можно менее видимым, потенциально используя метод расширения для очистки вещей:
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();
}
}
}
Ответ 5
У меня была такая же проблема, и из шестизначных переменных мне дали идею. Это очень просто.
Я использую WPF в приложении WinForms, поэтому я использую элемент управления ElementHost для размещения элементов управления Wpf в элементе управления WinForms. Чтобы обеспечить инициализацию управления 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 (как и в моем примере). Но, я думаю, это уже относится к вам, поэтому не должно быть проблемой.
Ответ 6
Вы пытались использовать 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 test на том, что уже было технически проверено? Кстати, я не критикую, просто хочу понять немного лучше.
Ответ 7
Не уверен, но может быть что-то подобное будет работать?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();