我正在尝试使用WPF构建计算器。我正在使用多个页面,以便可以练习在页面之间使用数据绑定。但是,我的显示没有正确绑定。当我点击“ 0”时,我希望显示屏显示“ 0”。当我输入数字时,显示不会更新。
我意识到关于SO有很多类似的问题。这些问题和我的问题之间的区别在于,我已经在实现INotifyPropertyChanged,但这并没有解决我的问题。
代码设置
“ NumberPad”保存数字按钮0-9。 “显示”只是顶部的一个大按钮,用于显示单击的数字。
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可以更新它?
谢谢!
答案 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;
}
}
}
}