текстовый блок не обновляется при изменении свойства в модели

Мой текстовый блок не обновляется, обновляйте значение из моей модели. Если я обновляю текстовый блок в ViewModel, он работает, поэтому мои привязки кажутся правильными. Я считаю, что проблема заключается в том, как я обновляю ее в модели, но я не уверен, почему моя наблюдаемая коллекция обновляется только потому, что я передаю значения туда и обратно, не уверенный, что это хорошая стратегия MVVM.

XAML-часть:

<Grid>
    <TextBox x:Name="NewLabelBx" HorizontalAlignment="Left" Height="23" Margin="54,449,0,0" TextWrapping="Wrap" Text="{Binding NewLabel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="314"/>
    <Button x:Name="NewLabelBtn" Content="Add Label" HorizontalAlignment="Left" Margin="293,490,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.518,-0.709" Command="{Binding Path=NewLabelBtn}" />
    <TextBlock x:Name="FilesProcessedBlck" HorizontalAlignment="Left" Margin="54,507,0,0" TextWrapping="Wrap" Text="{Binding FilesProcessedBlck,  UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" RenderTransformOrigin="-0.7,0.562" Width="65"/>
</Grid>

Часть ViewModel:

    public class P4LabelBatteryViewModel : BindableBase
{
    private P4LabelBatteryModel p4LabelBatteryModel = new P4LabelBatteryModel();

    public P4LabelBatteryViewModel()
    {
        P4LabelBatteryModel p4LabelBatteryModel = new P4LabelBatteryModel();

        this.GetBatteryBtn = new DelegateCommand(chooseFile, canChooseFile);
        this.NewLabelBtn = new DelegateCommand(chooseNewLabel, canNewLabel).ObservesProperty(() => NewLabel);
        this.FilesProcessedBlck = 2;  //this works.
    }

    //other code here

    private void chooseNewLabel()
    {
        if (ScriptCollection.Count > 0)
        {
            ScriptCollection = P4LabelBatteryModel.TagsFilesModel(NewLabel, ScriptCollection);
        }
    }


    private int _filesProcessedBlck;
    public int FilesProcessedBlck
    {
        get
        {
            return _filesProcessedBlck;
        }
        set
        {
            SetProperty(ref _filesProcessedBlck, value);
        }

    }

    private ObservableCollection<ScriptModel> _scriptCollection = new ObservableCollection<ScriptModel>();
    public ObservableCollection<ScriptModel> ScriptCollection
    {
        get
        {
            return _scriptCollection;
        }
        set
        {
            SetProperty(ref _scriptCollection, value);
        }

    }        

}

Часть модели:

   class P4LabelBatteryModel
{

   public static ObservableCollection<ScriptModel> TagsFilesModel(string NewLabel, ObservableCollection<ScriptModel> observableCollection)
    {
        string newLabel = NewLabel;
        var scriptsToTagColl = observableCollection;
        string[] files = null;

        var _p4LabelBatteryViewModel = new P4LabelBatteryViewModel();
        _p4LabelBatteryViewModel.FilesProcessedBlck++;  //xaml is never updated with this value.

        //This will generate an IPC when returned
        ObservableCollection<ScriptModel> newCollection = new ObservableCollection<ScriptModel>();

        //code here that modifies newCollection  xaml updates when this returns, _p4LabelBatteryViewModel.FilesProcessedBlck++; does not.

        return newCollection;
    }
}

Когда я запускаю отладчик, я вижу, что P4LabelBatteryViewModel.FilesProcessedBlck изменяется, но XAML не обновляется.


person coolercargo    schedule 26.10.2016    source источник
comment
Похоже, проблема с вашим .DataContext. Похоже, вы не назначили его правильно, поэтому ваш TextBlock указывает на один экземпляр P4LabelBatteryViewModel, в то время как вы создаете второй экземпляр в своем коде ObservableCollection.   -  person Rachel    schedule 26.10.2016


Ответы (1)


    var _p4LabelBatteryViewModel = new P4LabelBatteryViewModel();
    _p4LabelBatteryViewModel.FilesProcessedBlck++;  //xaml is never updated with this value.

Итак, ваш XAML должен иметь копию модели представления, если этот TextBlock когда-либо отображает то, что вы ожидаете в первую очередь. Но затем в этом методе вы создаете новый экземпляр того же класса модели представления, устанавливаете для него свойство и больше ничего с ним не делаете. Он выходит за пределы области видимости, и сборщик мусора в конце концов съедает его. Он никогда не был DataContext какого-либо представления и нигде, поэтому, конечно, он не влияет на пользовательский интерфейс.

_p4LabelBatteryViewModel — локальная переменная. Никто за пределами этого метода никогда не видит его и даже не знает, что он существует. Если вы хотите изменить копию модели представления, которая фактически отображается в пользовательском интерфейсе, вам нужно изменить этот экземпляр. Кроме того, пожалуйста, не добавляйте к локальным переменным префикс _. По соглашению ведущее подчеркивание указывает на частное поле, принадлежащее классу. Лучше придерживаться этого соглашения, чтобы избежать путаницы.

Модель представления должна обновить собственное свойство FilesProcessedBlck. В любом случае не рекомендуется, чтобы модель отвечала за поддержание состояния модели представления. Это проблема вьюмоделя, пусть он с ней разбирается.

private void chooseNewLabel()
{
    if (ScriptCollection.Count > 0)
    {
        ScriptCollection = P4LabelBatteryModel.TagsFilesModel(NewLabel, ScriptCollection);
        ++FilesProcessedBlck;
    }
}

А в модели...

public static ObservableCollection<ScriptModel> TagsFilesModel(string NewLabel, IList<ScriptModel> scriptsToTagColl)
{
    string newLabel = NewLabel;
    string[] files = null;

    //  This will generate an IPC when returned
    ObservableCollection<ScriptModel> newCollection = new ObservableCollection<ScriptModel>();

    //code here that modifies newCollection  xaml updates when this returns, _p4LabelBatteryViewModel.FilesProcessedBlck++; does not.

    return newCollection;
}

Я сделал еще несколько мелких изменений, чтобы упростить TagsFilesModel. Например, нет причин требовать от вызывающих абонентов передачи ObservableCollection<T>. Возможно, у вас никогда не будет причин давать ему что-то еще, но если вы привыкнете к такой гибкости в своем коде, это окупится.

Еще один пункт. Это безвредно, но стоит знать:

<TextBlock 
    x:Name="FilesProcessedBlck" 
    HorizontalAlignment="Left" 
    Margin="54,507,0,0" 
    TextWrapping="Wrap" 
    Text="{Binding FilesProcessedBlck}" 
    VerticalAlignment="Top" 
    RenderTransformOrigin="-0.7,0.562" 
    Width="65"
    />

UpdateSourceTrigger=PropertyChanged не служит никакой цели в этом Binding. «Источником» привязки является свойство модели представления; «цель» — это свойство элемента управления пользовательского интерфейса. UpdateSourceTrigger=PropertyChanged указывает Binding обновлять свойство модели представления при каждом изменении свойства элемента управления. Это кажется глупым, но вы также можете установить его на UpdateSourceTrigger=LostFocus; TextBox.Text по умолчанию имеет значение LostFocus, потому что обычный случай с TextBox заключается в том, что пользователь какое-то время печатает, но вы действительно не заботитесь об обновлении своей модели представления, пока он не закончит печатать и не переключит фокус на другой элемент управления. Обновление свойства модели просмотра может иметь много побочных эффектов, поэтому, если вы обновляете связанное свойство модели представления каждый раз, когда изменяется Text, в некоторых случаях вы можете получить патологическое поведение: каждый раз, когда пользователь вводит символ, появляется много кода. приходит в движение настолько сильно, что пользовательский интерфейс увязает. Таким образом, LostFocus.

Это все не по теме этого вопроса, потому что это не TextBox. Это TextBlock, который вообще не может обновлять исходное свойство, поэтому этот флаг не будет иметь никакого эффекта.

Кстати, что значит "черный"? Это потому, что он отображается в TextBlock? Что произойдет, если добавить другое место в пользовательском интерфейсе, где оно отображается, но новое будет лабом;; следует ли переименовать его в FilesProcessedBlckAndLbl? Лучше назвать его FilesProcessedCount и не допускать, чтобы модель представления заботилась о том, что делает пользовательский интерфейс.

person 15ee8f99-57ff-4f92-890c-b56153    schedule 26.10.2016
comment
Эд, Спасибо за помощь. Черный — это блочный, пережиток соглашения об именах, принятого в том месте, где я работал. Я пытался использовать код без кода, но, возможно, зашел слишком далеко. В модели у меня есть цикл, который подсчитывает, сколько элементов обработано, и я пытался обновить это (FilesProcessedBlck) из модели, прежде чем вернуться к ViewModel, очевидно, это плохо. - person coolercargo; 26.10.2016