2016-07-07 3 views
2

Я упростил приложение, чтобы показать мой вопросИзменения ViewModel

При нажатии кнопки, он изменяет Text свойства ViewModel и TextBlock.Text обновляются.

MainPage.xaml

<StackPanel> 
    <Button Click="ButtonBase_OnClick">Button to change text</Button> 
    <TextBlock Text="{x:Bind ViewModel.Text, Mode=OneWay}"></TextBlock> 
</StackPanel> 

MainPage.xaml.cs

public MainPage() 
    { 
     ViewModel = new ViewModel(); 
     this.InitializeComponent(); 
    } 
private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
    { 
     ViewModel.Text = "x:Bind works"; 
    } 

ViewModel класс имеет одно свойство строки (текст) и реализован интерфейс INotifyPropertyChange.


Проблема начинается тогда, когда ViewModel не установлен в CTOR (т.е. ViewModel является недействительным и изменен во время выполнения):

public MainPage() 
    { 
     //ViewModel = new ViewModel();//this line has been removed 
     this.InitializeComponent(); 
    } 
private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
    { 
     ViewModel = new ViewModel();//this line has been added 
     ViewModel.Text = "x:Bind does not work"; 
    } 

Complited связывание не работает (текст не изменяется) и я не мог понять, почему это так ... Мне нужно изменить viewModel из null (vm имеет значение null, потому что он ждет некоторых данных в реальном приложении)

+0

Конечно, потому что нет никакого уведомления, что ViewModel изменилось –

+0

Вы имеете в виду свойство ViewModel? Как я могу уведомить об изменении - я не хочу реализовывать INotifyPropertychange в MainPage – Alamakanambra

+0

Без магии вам нужно было уведомить страницу о том, что содержимое ViewModel изменилось. Неважно, как вы это делаете, но у вас есть –

ответ

0

ли вы реализовать INotifyPropertyChanged на странице, как так

public sealed partial class MainPage : Page, INotifyPropertyChanged 
{ 
    private ViewModel viewModel; 

    public ViewModel ViewModel 
    { 
     get { return viewModel; } 
     set 
     { 
      viewModel = value; 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ViewModel))); 
     } 
    } 

    public MainPage() 
    { 
     ViewModel = new ViewModel { }; 
     this.InitializeComponent(); 
    } 

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
    { 
     ViewModel = new ViewModel { };//this line has been added 
     ViewModel.Text = "x:Bind does not work"; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Это работает для меня.

1

{x: Bind} привязки (часто называемые скомпилированными привязками) используют сгенерированный код для достижения своих преимуществ. При времени загрузки XAML {x: Bind} преобразуется в то, что вы можете рассматривать как объект привязки, и этот объект получает значение из свойства в источнике данных. Эти сгенерированный код можно найти в папке obj с такими именами, как (на C#) <view name>.g.cs.

Для вашего кода, сгенерированный код будет, как следующее:

// Update methods for each path node used in binding steps. 
private void Update_(global::UWP.BlankPage3 obj, int phase) 
{ 
    if (obj != null) 
    { 
     if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0) 
     { 
      this.Update_ViewModel(obj.ViewModel, phase); 
     } 
    } 
} 
private void Update_ViewModel(global::UWP.ViewModel obj, int phase) 
{ 
    this.bindingsTracking.UpdateChildListeners_ViewModel(obj); 
    if (obj != null) 
    { 
     if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0) 
     { 
      this.Update_ViewModel_Text(obj.Text, phase); 
     } 
    } 
} 

... 

private global::UWP.ViewModel cache_ViewModel = null; 
public void UpdateChildListeners_ViewModel(global::UWP.ViewModel obj) 
{ 
    if (obj != cache_ViewModel) 
    { 
     if (cache_ViewModel != null) 
     { 
      ((global::System.ComponentModel.INotifyPropertyChanged)cache_ViewModel).PropertyChanged -= PropertyChanged_ViewModel; 
      cache_ViewModel = null; 
     } 
     if (obj != null) 
     { 
      cache_ViewModel = obj; 
      ((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_ViewModel; 
     } 
    } 
} 

Здесь я просто скопировать какой-то метод, который, связанные с вашей проблемой. Из этого метода вы можете обнаружить, что перед обновлением TextBlock или PropertyChanged слушателей он проверит, ViewModel - null. Если это null, ничего не будет сделано. Итак, чтобы сделать {x: Bind}, мы должны инициализировать ViewModel перед загрузкой страницы. И вот почему {x: Bind} не работает, когда вы инициализируете ViewModel в Button.Click события.

Чтобы устранить эту проблему, вы можете реализовать INotifyPropertyChanged интерфейс для ViewModel как Filip сказал так, что сгенерированный код может быть уведомлен, когда ViewModel изменен (от null до new ViewModel()) и обновлять вам пользовательский интерфейс.

Но я думаю, вы можете просто инициализировать ViewModel в конструкторе. При инициализации ViewModel, вы можете установить свойство, которые вы ждете для null первых, как:

public MainPage() 
{ 
    ViewModel = new ViewModel() { Text = null }; 
    this.InitializeComponent(); 
} 

А затем обновлять эти свойства, когда ваша дата готова. Таким образом, вы не можете реализовать интерфейс INotifyPropertyChanged на своей странице.

Кроме этого, есть еще более дешевый способ, вы можете вызвать this.Bindings.Update(); метод, чтобы заставить привязки обновляться после инициализации ViewModel как следующее:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
{ 
    ViewModel = new ViewModel(); 
    ViewModel.Text = "x:Bind does not work"; 
    this.Bindings.Update(); 
} 
Смежные вопросы