WPF数据绑定到对象的属性

时间:2019-01-11 16:02:17

标签: c# wpf xaml data-binding datacontext

我正在尝试使用WPF构建计算器。我正在使用多个页面,以便可以练习在页面之间使用数据绑定。但是,我的显示没有正确绑定。当我点击“ 0”时,我希望显示屏显示“ 0”。当我输入数字时,显示不会更新。

我意识到关于SO有很多类似的问题。这些问题和我的问题之间的区别在于,我已经在实现INotifyPropertyChanged,但这并没有解决我的问题。

代码设置

“ NumberPad”保存数字按钮0-9。 “显示”只是顶部的一个大按钮,用于显示单击的数字。

Calculator Screenshot

NumberPad.xaml.cs

public partial class NumberPad : Page
{
    Display m_display;

    public NumberPad()
    {
        InitializeComponent();
        m_display = new Display();
        this.DataContext = m_display;
    }

    private void Button0_Click(object sender, RoutedEventArgs e)
    {
        m_display.EquationText = "0";
    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        m_display.EquationText = "1";
    }
 }

Display.xaml.cs

public partial class Display : Page, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string m_equationText;

    public Display()
    {
        InitializeComponent();
        EquationText = "Hi";
    }

    public string EquationText
    {
        get { return m_equationText; }
        set
        {
            m_equationText = value;
            OnPropertyChanged("EquationText");
        }
    }

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

Display.xaml

<Page x:Class="WPFCalculator.Display"
    x:Name="_this"
    ....
    Title="Display">

    <Button Content="{Binding ElementName=_this, Path=EquationText, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource buttonStyle}" FontSize="60">

    </Button>
</Page>

预期:我应该可以按0或1,并且其对应的数字将显示在显示屏的按钮中。实际:在整个执行过程中仅显示“ Hi”。

关于Display.xaml的重要说明

我在“ ElementName = _this,Path =“中添加了内容,以显示“ Hi”。如果我只有“ Binding EquationText”,它将不会显示“ Hi”或0或1或其他任何内容。我之所以包含此代码,是因为这是我可以使Display当前显示任何内容的唯一方法。

结论

总而言之,我相信DataContext被设置为与实际绑定不同的对象的EquationText属性。如何将显示数据绑定到属性,以便NumberPad可以更新它?

谢谢!

3 个答案:

答案 0 :(得分:1)

让我首先尝试帮助您找到解决问题的方法:

没有必要定义:

<Button Content="{Binding ElementName = _this, ....}"></Button>

由于在文件背后的代码中将新创建的显示实例设置为实际窗口实例(this.DataContext)的DataContext属性时,对于WPF而言,要提供给窗口的数据显然位于显示实例设置为DataContext属性。 当我们使用DataBinding时,WPF在元素树中分层搜索具有DataContext的下一个元素。这意味着,由于您的按钮本身没有DataContext,因此WPF会升至一个级别并进行查看(因为您在按钮上方没有层次结构的其他控件),因此是否设置了Window的DataContext。由于在您的情况下,DataContext已在文件后面的代码中设置,因此WPF现在知道您要绑定到窗口的DataContext中的数据。好吧,您要做的就是绑定到要显示的属性。您可以这样做:

<Button Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
 AncestorType=Display}, Path=EquationText}"...>

我建议您使用MVVM模式来获得要解决的运动的体面解决方案(:!

您尝试编写的代码混合了不同的职责,需要明确区分。如上图所示,一个窗口应该代表一个视图。这意味着显示框不必是单独的页面。最好使用诸如网格之类的容器来构造视图。 此外,文件到xaml文件的代码中不应包含逻辑。与其使用按钮单击手柄,不如使用在ViewModel中定义的命令。借助MVVM,您想要实现的理想状态是您希望彼此完全分解视图和模型。这使您的代码更具可维护性,并且更易于替换。回到您的点击处理:建议不要为每个数字创建一个回调。这使代码更加混乱,特别是引入了冗余,因为在每个回调中几乎都发生相同的事情。您将字符串分配给相同的属性。因此,从逻辑上讲,您将拥有9个键,9个回调以及9x相同的代码。

我对您的建议是看一下MVVM模式,我相信您的代码会更清晰。

答案 1 :(得分:0)

如果没有所有代码,我不是100%不确定会发生什么,但是请尝试以下操作:

<Button Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
 AncestorType=Display}, Path=EquationText}" Style="{StaticResource buttonStyle}" FontSize="60">

答案 2 :(得分:0)

欢迎使用S / O,它是寻找和提供帮助的绝佳免费资源。尽管有很多关于执行MVVM模式和学习绑定的文章,但是有时您还是必须要有自己的心态,然后才能“点击”到那一刻。您的视觉样本完全正确并且是已知目标。我已经在类似的上下文中创建了WPF窗口。但是,我创建了一个方法来接受所有按钮,因此不必为每个调用重复创建一个函数。通过查看发送者(进入的按钮对象)并将其作为“按钮”并查看其Content属性,可以根据需要获得相应的0、1、2和+,-,*,/。对于数字,我只是添加到显示中,对于符号和等式,我将其留给您。但是,由于该值与公共属性相关联(通过get / set),因此文本就是“绑定到”的内容,您将看到它正在更新。

现在将数学函数的逻辑添加到内部“ _equationText”属性中,您显然会合并并根据需要清除+,-,*,/函数之间的显示,并继续学习。 HTH。我创建了一个名为MyCalculator的窗口,您几乎可以复制/粘贴并从那里运行。

MyCalculator.xaml                                                                                               

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
            <ColumnDefinition Width="60" />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
            <RowDefinition Height="60" />
        </Grid.RowDefinitions>


        <TextBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" 
            IsReadOnly="True"
            Text="{Binding DisplayText}"
            Height="28" FontSize="16"
            TextAlignment="Right"
            VerticalAlignment="Center"/>


        <Button Content="7" Grid.Row="2" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="8" Grid.Row="2" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="9" Grid.Row="2" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="+" Grid.Row="2" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="4" Grid.Row="3" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="5" Grid.Row="3" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="6" Grid.Row="3" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="-" Grid.Row="3" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="1" Grid.Row="4" Grid.Column="0"
            Click="AnyButton_Click" />

        <Button Content="2" Grid.Row="4" Grid.Column="1" 
            Click="AnyButton_Click" />

        <Button Content="3" Grid.Row="4" Grid.Column="2" 
            Click="AnyButton_Click" />

        <Button Content="*" Grid.Row="4" Grid.Column="3" 
            Click="AnyButton_Click" />


        <!--<Button Content="1" Grid.Row="5" Grid.Column="0"
            Click="AnyButton_Click" />-->

        <Button Content="0" Grid.Row="5" Grid.Column="1" 
            Click="AnyButton_Click" />

        <!--<Button Content="3" Grid.Row="5" Grid.Column="2" 
            Click="AnyButton_Click" />-->

        <Button Content="/" Grid.Row="5" Grid.Column="3" 
            Click="AnyButton_Click" />


        <Button Content="=" Grid.Row="6" Grid.Column="3" 
            Click="AnyButton_Click" />
    </Grid>
</Window>

MyCalculator.xaml.cs

using System.ComponentModel;
using System.Windows;

namespace StackHelp
{
    /// <summary>
    /// Interaction logic for MyCalculator.xaml
    /// </summary>
    public partial class MyCalculator : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public MyCalculator()
        {
            DataContext = this;
            InitializeComponent();
        }

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

        private string m_equationText;

        private string _displayText = "Hi";
        public string DisplayText
        { get { return _displayText; }
            set { _displayText = value;
                    OnPropertyChanged( "DisplayText" );
            }
        }


        private void AnyButton_Click(object sender, RoutedEventArgs e)
        {
            var whichBtn = sender as System.Windows.Controls.Button;
            if (whichBtn == null)
                return;

            // pre-clear just because it was sample anyhow.
            if (_displayText == "Hi")
                _displayText = "";

            switch( whichBtn.Content )
            {
                case "0":
                case "1":
                case "2":
                case "3":
                case "4":
                case "5":
                case "6":
                case "7":
                case "8":
                case "9":
                    DisplayText += whichBtn.Content;
                    break;

                case "+":
                case "-":
                case "*":
                case "/":
                case "=":
                    // how to handle...  using your computation learning method
                    // then finish by setting the display text with the new string 
                    // represented answer
                    break;

                default:
                    // invalid button
                    break;
            }

        }
    }
}