XAML 计算字段在UI中编辑后才更新

au9on6nz  于 2022-12-07  发布在  其他
关注(0)|答案(3)|浏览(162)

作为一名程序员新手,我正在尝试使用XAML和C#测试数据绑定。我有两个绑定到属性的滑块,我希望使用滑块设置的两个属性值之和来更新TextBox。
我正在使用INotifyPropertyChanged并尝试更改我能找到的每个属性,但直到编辑文本框时才能更新文本框,此时,文本框将更新为正确的值。使用UpdateSourceTrigger=PropertyChanged只会在编辑文本框时更新文本框,而不会在选择另一个元素时更新。我曾尝试编写一个单独的事件处理程序,该处理程序不我不使用[CallerNameMember],而是使用一个指定属性,但它似乎没有改变任何东西。

<Grid>
    <Grid.RowDefinitions>

    </Grid.RowDefinitions>

    <TextBox Grid.Row="0"
             Text="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             FontSize="20"
             FontWeight="Bold"
             AllowDrop="False" />

    <Slider Grid.Row="1"
            Value="{Binding BoundNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Maximum="100"
            Minimum="10"
            IsSnapToTickEnabled="True"
            TickFrequency="10" />
    <TextBox Grid.Row="2"
             Text="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             AllowDrop="False" />
    <Slider Grid.Row="3"

            Value="{Binding BoundNumber2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Maximum="100"
            Minimum="10"
            IsSnapToTickEnabled="True"
            TickFrequency="10" />

    <TextBox Grid.Row="4"
            Name="MathBox"
             Text="{Binding QuickMath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}">
           </TextBox>

</Grid>
public partial class OrderScreen : INotifyPropertyChanged
{
    public OrderScreen()
    {
        DataContext = this;

        InitializeComponent();
     }

    private int quickMath;
    public int QuickMath
    {
        get { return _boundNumber + _boundNumber2; }
        set
        {

            if (value != quickMath)
            {
                quickMath = value;
                OnPropertyChanged();

            }
        }
    }
    private int _boundNumber;
    public int BoundNumber
    {
        get { return _boundNumber; }
        set
        {
            if (_boundNumber != value)
            {
                _boundNumber = value;
               // MathBox.Text = quickMath.ToString();
                OnPropertyChanged();

            }
        }
    }

    private int _boundNumber2;
    public int BoundNumber2
    {
        get { return _boundNumber2; }
        set
        {
            if (_boundNumber2 != value)
            {
                _boundNumber2 = value;
                MathBox.Text = quickMath.ToString();
                OnPropertyChanged();

            }
        }
    }

 public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

我可以让它与注解掉的MathBox.Text = quickMath.ToString();一起工作,但我希望有一个更好的方法来与数据绑定一起工作。期待中的感谢!

ijxebb2r

ijxebb2r1#

绑定机制订阅DataSource对象的PropertyChanged事件,因此不需要将该事件与INPC实现沿着“初始化”,但正如您可能已经注意到的,当BoundNumberBoundNumber2发生更改时,QuickMath属性的PropertyChanged事件确实从未触发。
您可以用不同的方式修复它,例如,为所有受影响的属性显式调用OnPropertyChanged:

private int _boundNumber;
public int BoundNumber
{
    get { return _boundNumber; }
    set
    {
        if (_boundNumber != value)
        {
            _boundNumber = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(QuickMath));
        }
    }
}

请注意,这种方法可以使QuickMath属性保持只读。这种方法在其他情况下也能很好地工作,比如与时间相关的属性,比如说,如果数据源属性根据记录的时间戳和当前时间将字符串格式化为 “Edited 2 minutes ago”,并且将PropertyChanged作为定时任务调用。

public int QuickMath => _boundNumber + _boundNumber2;

或者,您可以更新QuickMath沿着修改BoundNumberBoundNumber2,以触发QuickMath setter中的OnPropertyChanged()调用:

private int _boundNumber2;
public int BoundNumber2
{
    get { return _boundNumber2; }
    set
    {
        if (_boundNumber2 != value)
        {
            _boundNumber2 = value;
            OnPropertyChanged();
            QuickMath = BoundNumber + BoundNumber2;
        }
    }
}

如果QuickMath中的逻辑不允许将其设为只读属性,则这是有意义的。在这种情况下,您必须相应地调整getter,并在那里使用private或protected setter,以避免数据不一致和意外行为。

private int _quickMath;
public int QuickMath
{
    get { return _quickMath; }
    private set
    {
        if (value != _quickMath)
        {
            _quickMath = value;
            OnPropertyChanged();
        }
    }
}

在这两种情况下,都不需要双向绑定到QuickMath

<TextBlock Grid.Row="4" Text="{Binding QuickMath, Mode=OneWay}"/>

顺便提一下,看看代码的其余部分,确实值得一提的是,绑定机制应该将UI与数据隔离,其中XAML知道数据源对象的属性(名称和类型),但不知道它的内部实现,而数据源对象可能根本不知道XAML。
1.数据对象不应调用FrameworkElement,如MathBox.Text
1.将数据对象类与页面或控件类完全分开被认为是一种好的设计。
希望这对你有帮助。

nnt7mjpx

nnt7mjpx2#

你还没有初始化你的PropertyChanged事件,所以它永远不会被调用。声明并初始化它,如下所示:
public event PropertyChangedEventHandler PropertyChanged = delegate { };

w8rqjzmb

w8rqjzmb3#

绑定到计算属性QuickMath的TextBox应接收来自它的PropertyChanged事件,以便更新字段中的文本。尽管OrderScreen实现了INotifyPropertyChanged接口,但当QuickMath更改时,它不会引发该事件,因为它的setter(事件引发的位置)。您可以修复它,例如,按照其他答案中的建议从独立属性setter中调用QuickMath setter,或者将其委托给DependenciesTracking lib:

public class OrderScreen : INotifyPropertyChanged
    {
        private readonly IDependenciesMap<OrderScreen> _dependenciesMap =
            new DependenciesMap<OrderScreen>()
                .AddDependency(i => i.QuickMath, i => i.BoundNumber + i.BoundNumber2, i => i.BoundNumber, i => i.BoundNumber2);

        public OrderScreen() => _dependenciesMap.StartTracking(this);

        private int _boundNumber2;
        private int _boundNumber;
        private int _quickMath;

        public int QuickMath
        {
            get => _quickMath;
            private set
            {

                if (value != _quickMath)
                {
                    _quickMath = value;
                    OnPropertyChanged();

                }
            }
        }

        public int BoundNumber
        {
            get => _boundNumber;
            set
            {
                if (_boundNumber != value)
                {
                    _boundNumber = value;
                    OnPropertyChanged();
                }
            }
        }

        public int BoundNumber2
        {
            get => _boundNumber2;
            set
            {
                if (_boundNumber2 != value)
                {
                    _boundNumber2 = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public class Tests_SO_56623403
    {
        [Test]
        public void Test_SO_56623403()
        {
            var sut = new OrderScreen();

            var raisedEventsCount = 0;
            sut.PropertyChanged += (_, args) =>
            {
                if (args.PropertyName == nameof(OrderScreen.QuickMath))
                    ++raisedEventsCount;
            };
            Assert.Multiple(() =>
            {
                Assert.That(sut.QuickMath, Is.EqualTo(0));
                Assert.That(raisedEventsCount, Is.EqualTo(0));
            });

            sut.BoundNumber = 12;
            Assert.Multiple(() =>
            {
                Assert.That(sut.QuickMath, Is.EqualTo(12));
                Assert.That(raisedEventsCount, Is.EqualTo(1));
            });

            sut.BoundNumber2 = 40;
            Assert.Multiple(() =>
            {
                Assert.That(sut.QuickMath, Is.EqualTo(52));
                Assert.That(raisedEventsCount, Is.EqualTo(2));
            });
        }
    }

相关问题