属性 在模型中更改时文本块未更新

textblock not updating when property changed in model

我的文本块没有更新更新我模型中的值。如果我更新 ViewModel 中的文本块,它会起作用,所以我的绑定似乎是正确的。我认为问题在于我在模型中更新它的方式,但我不确定为什么我的 observableCollection 也只更新,因为我来回传递值不确定这是好的 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>

视图模型部分:

    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 没有被更新。

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

好的,所以你的 XAML 必须有一个视图模型的副本,如果那个 TextBlock 首先显示你所期望的。但随后在该方法中,您创建了相同视图模型 class 的新实例,在其上设置了 属性,然后您什么都不做。它超出范围,垃圾收集器最终将其吃掉。它从来不是任何地方任何视图的 DataContext,因此它当然对 UI 没有影响。

_p4LabelBatteryViewModel 是局部变量。该方法之外的任何人都看不到它,甚至不知道它存在。如果您想更改 UI 中实际显示的视图模型的副本,则必须更改它的 实例 。另外,请不要在局部变量前加上 _。按照惯例,前导下划线表示属于 class 的私有字段。最好坚持该约定,以避免混淆。

视图模型应该更新它自己的 FilesProcessedBlck 属性。在任何情况下,让模型负责维护视图模型的状态都不是一个好主意。那是viewmodel的问题,让他来处理吧。

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=PropertyChangedBinding 中没有任何作用。绑定的 "source" 是视图模型 属性; "target" 是 UI 控件 属性。 UpdateSourceTrigger=PropertyChanged 告诉 Binding 在控件 属性 发生变化时更新视图模型 属性。这看起来很愚蠢,但您也可以将其设置为 UpdateSourceTrigger=LostFocusTextBox.Text 默认为 LostFocus 因为 TextBox 的通常情况是用户输入了一段时间,但在他完成输入并改变焦点之前你真的不关心更新你的视图模型到另一个控件。更新视图模型 属性 会产生很多副作用,因此如果每次 Text 更改时都更新绑定的视图模型 属性,在某些情况下,您可能会出现病态行为:当用户键入一个字符时,大量代码被投入运行,以至于 UI 陷入困境。因此 LostFocus

这不是这个问题的主题,因为那不是 TextBox。这是一个 TextBlock,根本无法更新源 属性,因此该标志将无效。

顺便问一下,"Blck" 是什么意思?那是因为它显示在 TextBlock 中吗?如果在显示的 UI 中添加另一个位置,但新位置是标签,会发生什么情况;;然后你应该重命名它 FilesProcessedBlckAndLbl 吗?最好称它为 FilesProcessedCount 并让 viewmodel 不关心 UI 做什么。