我试着做一些简单的事情,但似乎我错过了一些事情。 我尝试在单击按钮时更改标签的内容。 我使用MVVM模式来实现这一点。 下面是我的代码:
查看:
<Button x:Name="buttonNext"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="2"
Width="85"
Height="35"
Style="{StaticResource AccentedSquareButtonStyle}"
Command="{Binding Path=Next}">
<TextBlock Text="Next"
TextWrapping="Wrap"
TextAlignment="Center"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Button>
<Label Name="Path"
Grid.ColumnSpan="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding path}"
FontWeight="Bold"
Foreground="DeepSkyBlue"
/>
ViewModel:
public ICommand Next { get; set; }
private string _path;
public string path
{
get
{
return _path;
}
set
{
_path = value;
RaisePropertyChanged("path");
}
}
public event PropertyChangedEventHandler PropertyChanged = null;
protected virtual void RaisePropertyChanged(string propName)
{
if (PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
}
public page4ViewModel(NavigationViewModel navigationViewModel)
{
_path = "etape1";
Next = new BaseCommand(GoNext);
}
private void GoNext(object obj)
{
switch (_path)
{
case "etape1":
_path = "etape2";
break;
case : "etape2"
_path = "etape3";
break;
case "etape3":
_path = "etape4";
break;
default:
_path = " ";
break;
}
}
一开始,标签是“etape1”,就像构造函数中一样,但是当我单击next按钮时,值没有改变。 ps:功能改变工作,因为我放了一个断点看。 谢谢你的帮助
您必须使用属性,而不是备份字段。
private void GoNext(object obj)
{
switch (_path)
{
case "etape1":
path = "etape2"; // without underscore
break;
case : "etape2"
path = "etape3";
break;
case "etape3":
path = "etape4";
break;
default:
path = " ";
break;
}
这将引发更改通知
编辑
对ViewModel使用ViewModelBase
也更好
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
您可以在其中定义
protected virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
替代方案
根据这个注释,我们可以引入CallerMemberName
protected virtual void RaisePropertyChanged<T>([CallerMemberName] string propertyName = null)
另一个有用的变体是基于选择器的,当您需要从不同的setter为依赖属性引发选择器时
protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
RaisePropertyChanged(body.Member.Name);
}
您需要在ui线程上引发property changed事件。
替换
if (PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
与
if (PropertyChanged != null)
{
Application.Current.Dispatcher.Invoke(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
还设置path
而不是_path
的值
更新
正如注释中指出的,Dispatcher.invoke在这种情况下不是必需的,因为它已经在UI线程上了。
打电话就行了
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
您正在更改中的值:
private void GoNext(object obj)
{
switch (_path)
{
case "etape1":
_path = "etape2";
break;
case : "etape2"
_path = "etape3";
break;
case "etape3":
_path = "etape4";
break;
default:
_path = " ";
break;
}
但是在设置_path
时,不会调用path
中的setter:
public string path
{
set ///This is never called
{
_path = value;
RaisePropertyChanged("path");
}
}
更改为:
switch (path)
{
case "etape1":
path = "etape2";
break;
case : "etape2"
path = "etape3";
break;
case "etape3":
path = "etape4";
break;
default:
path = " ";
break;
}