WPF MVVM从一个View传递textBox数据以将标签或文本更新到另一个View

时间:2017-04-20 12:26:22

标签: wpf mvvm

在使用带有数据绑定概念的WPF开发的MVVM应用程序中,我想从一个视图中获取TextBox.Text值以进行一些更改(计算)并将其显示在位于其他视图中的TextBox中?是否可以直接沟通,或者我必须通过List<>输入?

谢谢!

2 个答案:

答案 0 :(得分:0)

问题:我们有一个带有两个子视图模型的主视图模型,我们希望子视图模型各自相互更新一些与另一个相似的属性。我们说每个人都有一个名为Value的属性,我们希望ChildA.ValueChildB.Value保持同步。为了增加皱纹,我们不希望价值相同;我们希望每个人都与另一个人保持任意关系。也许一个是摄氏温度,另一个很难拼写。诸如此类的事情。

值得庆幸的是,这是一个以视图模型为中心的设计,因此现有的代码不会与我们作斗争。

如果这些是正确设计的视图模型,它们将从实现INotifyPropertyChanged的公共基类派生,并且当它们的属性值发生变化时,它们会引发PropertyChanged。我正在调用基类ViewModelBase,但它可以是任何东西。我也假设它是用C#6编写的,所以OnPropertyChanged()方法(引发PropertyChanged)可以用不带参数调用的方式编写。

我们要做的是让主视图模型负责管理孩子之间的关系。孩子们不知道这种关系存在,因为这样的横向依赖通常以泪水结束。让每个班级都担心自己的属性及其子级属性。因此,MainViewModel是唯一负责担心ChildAChildB之间关系的班级。

由于ChildAChildB都实现INotifyPropertyChanged,我们可以使用它来管理关系。

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        ChildA.PropertyChanged += ChildA_PropertyChanged;
        ChildB.PropertyChanged += ChildB_PropertyChanged;
    }

    public static readonly double ConversionFactor = 2000.0 / 4.5;

    private void ChildA_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(ChildA.Value))
        {
            ChildB.Value = ChildA.Value * ConversionFactor;
        }
    }

    private void ChildB_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(ChildB.Value))
        {
            ChildA.Value = ChildB.Value / ConversionFactor;
        }
    }

    public ChildAViewModel ChildA { get; } = new ChildAViewModel();
    public ChildBViewModel ChildB { get; } = new ChildBViewModel();
}

public class ChildAViewModel : ViewModelBase
{
    private double _value = 0.0;
    public double Value
    {
        get { return _value; }
        set
        {
            //  This "if" is important: If the value is not changing, we must 
            //  not raise PropertyChanged, because that will put us in an 
            //  infinite recursive loop. 
            if (value != _value)
            {
                _value = value;
                OnPropertyChanged();
            }
        }
    }
}

public class ChildBViewModel : ViewModelBase
{
    private double _value = 0.0;
    public double Value
    {
        get { return _value; }
        set
        {
            if (value != _value)
            {
                _value = value;
                OnPropertyChanged();
            }
        }
    }
}

替代解决方案

这是另一种方法:我们可以为每个子视图模型类提供一个ValueChanged事件。这是比上述更多的初步工作,但是一旦完成初步工作,有些人会发现它更清洁,更方便,当然更自我记录。

在将C#6 nameof运算符添加到该语言之前,上述PropertyChanged版本受到"魔法字符串危险"更改属性名称与引用属性名称的字符串文字不同步。许多商店仍在使用C#5进行生产。在我工作的地方,我们已经付出了很多努力来减轻这种风险;人们仍然沉默地嘀咕着关于2014年的Ed's Magic String Jihad"。

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        ChildA.ValueChanged += (s, e) => ChildB.Value = e.NewValue * ConversionFactor;
        ChildB.ValueChanged += (s, e) => ChildA.Value = e.NewValue / ConversionFactor;
    }

    public static readonly double ConversionFactor = 2000.0 / 4.5;

    public ChildAViewModel ChildA { get; } = new ChildAViewModel();
    public ChildBViewModel ChildB { get; } = new ChildBViewModel();
}

public class ValueChangedEventArgs<T> : EventArgs {
    public ValueChangedEventArgs(T oldValue, T newValue)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }

    public T OldValue;
    public T NewValue;
}

public class ChildAViewModel : ViewModelBase
{
    public event EventHandler<ValueChangedEventArgs<double>> ValueChanged;

    private double _value = 0.0;
    public double Value
    {
        get { return _value; }
        set
        {
            //  This "if" is important: If the value is not changing, we must 
            //  not raise PropertyChanged, because that will put us in an 
            //  infinite recursive loop. 
            if (value != _value)
            {
                var args = new ValueChangedEventArgs<double>(_value, value);
                _value = value;
                ValueChanged?.Invoke(this, args);
                OnPropertyChanged();
            }
        }
    }
}

public class ChildBViewModel : ViewModelBase
{
    public event EventHandler<ValueChangedEventArgs<double>> ValueChanged;

    private double _value = 0.0;
    public double Value
    {
        get { return _value; }
        set
        {
            if (value != _value)
            {
                var args = new ValueChangedEventArgs<double>(_value, value);
                _value = value;
                ValueChanged?.Invoke(this, args);
                OnPropertyChanged();
            }
        }
    }
}

答案 1 :(得分:0)

我不确定它是否是最好的方法,但通常我使用Application.Properties。

有关详情,请参阅this answer