Принудительная привязка в WPF - PullRequest
19 голосов
/ 22 марта 2011

Я пишу тесты, которые проверят правильность элементов Binding, указанных в XAML.Пока они работают, единственная проблема в том, что я не знаю, как правильно заставить привязку данных происходить.Удивительно, но недостаточно просто установить что-либо в 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);
    }
}

Здесь я создаюОкно и помещение массива как DataContext в это окно.Я привязываю Window.Title к Array.Length, поэтому я ожидаю увидеть число 5, напечатанное на консоли.Но пока у меня Show окно (закомментированная строка), я получу пустую строку.Если я раскомментирую эту строку, я получу желаемый 5 в выводе консоли.

Есть ли способ, которым я могу сделать привязку, не показывая окно?При запуске тестов довольно раздражающе смотреть на ~ 20 окон.

PS: я знаю, что могу сделать окна более прозрачными и т. Д., Но я ищу более элегантное решение.

ОБНОВЛЕНИЕ Код выше - упрощенная версия того, что у меня действительно есть.В реальном коде я получаю View (некоторые UIElement с привязками) и object ViewModel.Я не знаю, какая именно привязка была установлена ​​на View, но я все еще хочу, чтобы все они были инициализированы.

ОБНОВЛЕНИЕ 2 : Ответы на вопросы о том, что я тестирую и почему.Я не собираюсь проверять, что такие классы, как Binding, BindingBase и т. Д. Работают должным образом, я предполагаю, что они работают.Я пытаюсь проверить, что во всех моих файлах XAML я правильно написал привязки.Поскольку привязки являются типами со строковым типом, они не проверяются во время компиляции, и по умолчанию они вызывают только ошибки в окне вывода, которое я иногда пропускаю.Поэтому, если мы возьмем мой пример сверху и сделаем опечатку в привязке: {Binding Lengthhh}, тогда мои тесты сообщат вам, что для привязки нет свойства с именем Lengthhh.Таким образом, у меня есть около 100 файлов XAML, и для каждого XAML у меня есть тест (3-5 строк кода), и после запуска моих тестов я знаю , точно , что в моем решении нет ошибок привязки.

Ответы [ 7 ]

3 голосов
/ 05 мая 2016

Привязки обновляются диспетчером с помощью DispatcherPriority.DataBind - поэтому, если вы ожидаете фиктивную задачу с приоритетом SystemIdle, вы уверены, что любое ожидание привязки данных выполнено.

  try
  {
    this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
  }
  catch
  {
    // Cannot perfom this while Dispatcher in suspended mode
  }
1 голос
/ 07 мая 2011

Если вы пытаетесь проверить правильность своего взгляда, я предлагаю вам проверить свое мнение: -)

Почему бы не запустить пользовательский интерфейс из модульного теста и не написать код, который проверяет содержимое пользовательского интерфейса после изменения данных.

VS2010 имеет тестирование GUI, или вы можете взглянуть на код таких инструментов, как Snoop.

<ч /> Изменить следующий комментарий:

Если ВСЕ, что вы хотите сделать, это протестировать несколько простых привязок, попробуйте написать статический тест кода, который запускается как событие после компоновки с использованием отражения в моделях представления и регулярных выражений в XAML. Добавьте атрибуты на ВМ или используйте файл конфигурации, чтобы ваш тест знал, какое представление получает какую модель представления в виде DataContext. Сравните имена и типы свойств в моделях представления со связывающими строками в представлении (автоматически ищите их в XAML) и сгенерируйте исключение (что приведет к ошибке сборки), если строки не совпадают.

Если ваши привязки более сложные (конвертеры, мультипривязки, ...), это может быть немного сложнее для реализации.

0 голосов
/ 26 апреля 2011

Вы пытались использовать 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 на предмете, который уже был технически протестирован?Кстати, я не критикую, просто хочу немного лучше понять.

0 голосов
/ 22 апреля 2011

У меня была такая же проблема, и из шестерых переменных мне подсказали.Это очень простоЯ использую 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, а только затем принудительно создаете дескриптор (как и мойпример).Но, думаю, это уже так для вас, поэтому не должно быть проблем.

0 голосов
/ 22 марта 2011

Я не верю, что привязки 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();
        }
    }
}
0 голосов
/ 22 марта 2011

Я думаю, что вы должны сначала установить DataContext, а затем выполнить привязку, например:

view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));

Я не уверен, является ли это реальным решением для вашей общей проблемы, но это работает в этом случае.

0 голосов
/ 22 марта 2011

Не уверен, но, может быть, что-то подобное будет работать?

view.GetBindingExpression(Window.TitleProperty).UpdateTarget();
...