Порядок инициализации управления Fiasco - PullRequest
2 голосов
/ 02 марта 2012

Рассмотрим следующий код:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Slider ValueChanged="slider_ValueChanged/>
        <TextBox x:Name="counter"/>
    </StackPanel>
</Window>

и

namespace Project1
{
    public partial class Window1 : Window
    {
        public MainWindow() { InitializeComponent(); }

        void slider_ValueChanged(object sender,
            RoutedPropertyChangedEventArgs<double> e)
        {
            counter.Text = e.NewValue.ToString();
        }
    }
}

Слайдер вызовет свое ValueChanged событие во время инициализации , пока counter все еще null.

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

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

Ответы [ 2 ]

3 голосов
/ 02 марта 2012

Есть много способов справиться с этим, в зависимости от вашей ситуации

Во-первых, вы можете просто распознать тот факт, что объект может быть не инициализирован, и проверить это перед обработкой.Например,

if (counter.Text != null)
    counter.Text = e.NewValue.ToString();

Во-вторых, вы можете прикрепить свои события к событию Loaded объекта, чтобы они не срабатывали до тех пор, пока объект не будет инициализирован.

void Counter_Loaded(object sender, EventArgs e)
{
    slider.ValueChanged += Slider_ValueChanged;
}

void Counter_Unloaded(object sender, EventArgs e)
{
    slider.ValueChanged -= Slider_ValueChanged;
}

Ии наконец, вы можете использовать WPF Dispatcher для запуска событий в потоке пользовательского интерфейса с другим DispatcherPriority .По умолчанию используется значение Normal, которое выполняется после операций Loaded, Render и DataBind

Dispatcher.BeginInvoke(DispatcherPriority.DataBind, 
    new Action(delegate() { counter.Text = e.NewValue.ToString(); }));
2 голосов
/ 29 апреля 2013

Ответ true на этот вопрос заключается в использовании шаблона MVVM , в котором код окна позади файлов содержит мало кода инициализации или не содержит его.

В этом шаблонеПользовательский интерфейс связан с остальной частью кода только с привязкой данных.Вы пишете специальные классы моделей представлений, которые реализуют INotifyPropertyChanged, берут свою бизнес-логику и представляют ее как серию свойств, с которыми связан пользовательский интерфейс.

Естественно, вы полностью контролируете, как инициализируются ваши модели представлений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...