Wpf 基于视图模型中的更改更新依赖项属性
我在WPF中的数据绑定方面遇到了一些问题。下面是一个场景:我制作了一个模拟拨号盘的用户控件(即,一个由12个按钮组成的数组,数字从“0”到“9”,再加上“#”和“清除”键)。该控件位于类库中,它是按照MVVM模式实现的,主要是因为我需要类库中的组件能够轻松地进行单元测试 控件的视图模型非常简单,每次用户按下拨号键按钮时,它基本上都会更新一个公共的“DialedNumber”字符串(该字符串在内部连接到该模型)。绑定工作正常,通过使用调试器,我可以确认viewmodel中的“DialledNumber”变量在按下拨号板中的按钮时得到更新 这个拨号板控件由一个单独的XAML文件(Panel.XAML)使用,该文件列出了几个属于我的自定义类库的控件 现在,我想在面板文件中添加一个文本块,以便显示拨号板中的“DialledNumber”字符串。这是Panel.xaml中的代码段:Wpf 基于视图模型中的更改更新依赖项属性,wpf,mvvm,viewmodel,dependency-properties,Wpf,Mvvm,Viewmodel,Dependency Properties,我在WPF中的数据绑定方面遇到了一些问题。下面是一个场景:我制作了一个模拟拨号盘的用户控件(即,一个由12个按钮组成的数组,数字从“0”到“9”,再加上“#”和“清除”键)。该控件位于类库中,它是按照MVVM模式实现的,主要是因为我需要类库中的组件能够轻松地进行单元测试 控件的视图模型非常简单,每次用户按下拨号键按钮时,它基本上都会更新一个公共的“DialedNumber”字符串(该字符串在内部连接到该模型)。绑定工作正常,通过使用调试器,我可以确认viewmodel中的“DialledNumb
<PanelControls:DialPad x:Name="MyDialPad" DialedNumber="55325"/>
<TextBlock Text="{Binding ElementName=MyDialPad, Path=DialedNumber}" />
以下是拨号板视图模型:
public class DialPadViewModel : ObservableObject
{
public DialPadViewModel()
{
_dialPadModel = new DialPadModel();
}
#region Fields
private readonly DialPadModel _dialPadModel;
private ICommand _dialPadKeyPressed;
#endregion
#region Public Properties/Command
public DialPadModel DialPadModel
{
get { return _dialPadModel; }
}
public ICommand DialPadKeyPressedCommand
{
get
{
if (_dialPadKeyPressed == null)
{
_dialPadKeyPressed = new RelayCommand(DialPadKeyPressedCmd);
}
return _dialPadKeyPressed;
}
}
public string DialedNumber
{
get { return _dialPadModel.DialedNumber; }
set
{
_dialPadModel.DialedNumber = value;
RaisePropertyChanged("DialedNumber");
}
}
#endregion
#region Private Helpers
private void DialPadKeyPressedCmd(object parameter)
{
string keyPressedString = parameter.ToString();
if (keyPressedString.Length > 0)
{
if (char.IsDigit(keyPressedString[0]))
{
DialedNumber += keyPressedString[0].ToString(CultureInfo.InvariantCulture);
}
else if (keyPressedString == "C" || keyPressedString == "Clr" || keyPressedString == "Clear")
{
DialedNumber = "";
}
}
}
#endregion
}
让我重申一下我的问题:Panel.xaml中的textblock在开始时显示正确的数字(55325),但当我按下拨号键时,它的值永远不会更新。我在DialPadKeyPressedCmd中放置了一个断点,我可以确认每次我在拨号板中按一个键时都会执行该方法。听起来您可以向视图中添加一个文本框,并将其文本属性绑定到视图模型的DialedNumber属性
<TextBox Text="{Binding Path=DialedNumber}"></TextBox>
如果我误解了您的问题,请告诉我。如果您将拨号板放在视图中,您可以在ViewModel中创建DialPadViewModel属性(公共+全局):
public DialPadViewModel DialPadViewModel = new DialPadViewModel();
<TextBox Text="{Binding Path=DialPadViewModel.DialedNumber}"/>
现在将视图的DataContext绑定设置为ViewModel,并将拨号盘DataContext也绑定到它,如
<local:DialPad DataContext="{Binding}"/>
编辑2:我发现了问题:
在DialPad.xaml中,您的所有命令都从参考资料绑定到DialPadViewModel,而TextBloc绑定到DialPads DataContext,后者是DialPadViewModel的另一个实例
因此,每次点击拨号板按钮时,您都会更改资源的DPVM实例中的DialledNumber值,而不是DataContext的DPVM实例中的DialledNumber值。DependencyProperties用于指向其他一些属性以获取其值。因此,您可以将它指向您的
DialPadViewModel.DialedNumber
,也可以在使用UserControl时将它指向其他字符串(绑定或硬编码值,如“551”),但不能同时执行这两项操作
在您的情况下,当有人绑定到dialledNumber
依赖项属性时,他们将用新值替换当前值(绑定到DialPadViewModel.dialledNumber
)
根据您的代码外观和您想做什么,有几种方法可以解决这个问题
首先,您可以坚持希望使用您的控件的人也使用您的ViewModel,并且不要将DialledNumber
作为公共依赖属性
因此,我们不允许创建一个具有someotherdialledNumber
属性和绑定的自定义类
<DialPad DialedNumber="{Binding SomeOtherDialedNumber}">
我能想到的另一种选择是将DependencyProperty与ViewModel属性以及一些PropertyChange通知同步
您需要随时更新DialPadViewModel.DialedNumber
依赖项属性更改(您可能需要用于属性更改通知),您还必须编写一些内容来随时更新dialledNumber
依赖项属性的源DialPadViewModel.dialledNumber
更改
就个人而言,如果我的UserControl有一个ViewModel,那么我使用第一个选项。如果没有,我将完全摆脱ViewModel,并在代码隐藏中为UserControl构建逻辑,而不使用ViewModel
原因是WPF使用两层:UI层和数据层。
DataContext
是数据层,而ViewModel通常是数据层的一部分。通过在UserControl的构造函数中显式设置数据层(DataContext
),您将数据层与UI层相结合,这与使用MVVM的最大原因之一:关注点分离背道而驰。UserControl实际上应该只是一个漂亮的shell,您应该能够将其放置在任何数据层的顶部。尝试将Source设置为Source=DialPadViewModel……因为您是通过路径指定DialedNumber属性。@colinsmith:我试过了,但运气不好。绑定到按钮的命令将被调用,viewmodel/model将被更新,但视图中的dependency属性未获得新值。感谢您的回复!在回答的某个时候,您说“并且您还必须编写一些内容,以便在DialPadViewModel.DialedNumber发生更改时更新DialedNumber依赖项属性的源。”。这正是我需要实现的,但我不知道如何实现。我已经知道如何将从DialledNumber到DialPadViewModel.DialledNumber的更改进行通信,但我不知道如何以另一种方式进行通信。如何根据viewmodel中的状态更改更新视图?谢谢您的回复!是的,我这样做了,但我的问题是有一点移动涉及。基本上,每次我按下拨号板按钮时,我的viewmodel都会更新,但我不知道如何将更新后的状态信息提供给视图。这应该可以工作。确保视图控件的绑定模式为双向…模式=双向。此外,您将在DialledNumber属性中从模型返回值。确保它有价值。我试过了,但没用。我想我的问题可以归结为如何在拨号号码loca中输入更新
<TextBox Text="{Binding Path=DialPadViewModel.DialedNumber}"/>
public string DialedNumber
{
get
{
return DialPadViewModel.DialedNumber;
}
set
{
DialPadViewModel.DialedNumber = value;
}
}
<DialPad DialedNumber="{Binding SomeOtherDialedNumber}">
<DataTemplate DataType="{x:Type DialPadViewModel}">
<local:DialPad />
</DataTemplate>