在未将新实例分配给属性的情况下通知属性更改时,Wpf绑定不起作用

时间:2016-11-29 17:20:07

标签: c# wpf binding

所以,我有这个非常简单的代码,当我在使用注释的代码行时单击一个按钮时,GUI中的值是通过更新的绑定更新,一切正常。如果我使用另外两行(没有发生),即使我注意到改变,也没有任何反应。

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Spot _position = new Spot(0, 0);
    public Spot Position
    {
        get { return _position; }
        set { _position = value; OnPropertyChanged("Position"); }
    }

    public MainWindow()
    {
        InitializeComponent();
        win.DataContext = this;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        //Position = new Spot(5,5);
        Position.UpdateX(5);
        OnPropertyChanged("Position");
    }
}

public class Spot
{
    public Spot(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void UpdateX(int x)
    {
        X += x;
    }
    public void UpdateY(int y)
    {
        Y += y;
    }

    public override string ToString()
    {
        return string.Format("{0}; {1}", X, Y);
    }

    public int X { get; set; } = 0;
    public int Y { get; set; } = 0;
}

XAML

<StackPanel x:Name="win" Orientation="Vertical" Margin="100">
    <Label Content="{Binding Position, UpdateSourceTrigger=PropertyChanged}" Width="100" Height="25"></Label>
    <Button Width="100" Height="25" Click="Button_Click">Click</Button>
</StackPanel>

提前感谢任何建议。

1 个答案:

答案 0 :(得分:0)

这是这笔交易。

首先,摆脱UpdateSourceTrigger=PropertyChanged:该标志控制Mode=TwoWay Binding何时更新其源属性。 Label.Content默认绑定OneWay,因为它不是可编辑的控件。它无法更新其来源。所以不管怎么说,这是一个无操作。

第二

<Label Content="{Binding Position}" ... />

这样做会在ToString()上致电Position。但如果ToString()的值未发生变化,则不会再次致电Position。从相关意义上讲,它还没有Spot的同一个实例,它是最后一次调用ToString()。这可能是一个优化&#34;。

这将有效:

<Label>
    <TextBlock>
        <TextBlock.Text>
            <MultiBinding StringFormat="{}{0}: {1}">
                <Binding Path="Position.X" />
                <Binding Path="Position.Y" />
            </MultiBinding>
        </TextBlock.Text>
    </TextBlock>
</Label>

这也可以:

<Label>
    <TextBlock>
        <TextBlock Text="{Binding Position.X, StringFormat='{}{0}: '}" />
        <TextBlock Text="{Binding Position.Y}" />
    </TextBlock>
</Label>

我将TextBlock放在Label中,因为您使用的是Label,而Label通常有默认的边距/填充/与布局相关的任何样式看起来正确。

无效,因为Label.ContentObject,而非String,因此StringFormat将被忽略。相反,您会在VS 输出窗格中获得有关需要转换器的MultiBinding的例外情况:

<Label
    >
    <Label.Content>
        <MultiBinding StringFormat="{}{0}: {1}">
            <Binding Path="Position.X" />
            <Binding Path="Position.Y" />
        </MultiBinding>
    </Label.Content>
</Label>

执行此操作的常用方法是Spot实施INotifyPropertyChanged。如果Spot的总体使用情况不适合这样做,那么您就会陷入或多或少的丑陋行为。我自己,我给了Position所有者一个UpdatePosition(int deltaX, int deltaY)方法,该方法会创建Spot的新实例并将其分配给Position。如果viewmodel的属性(或伪装成viewmodel的窗口)不实现INotifyPropertyChanged,则将属性值视为不可变。