我正在处理使用和坚持MVVM模式的一些WPF问题。
我的大部分属性都是这样的:
public string Period
{
get { return _primaryModel.Period; }
set
{
if (_primaryModel.Period != value)
{
_primaryModel.Period = value;
RaisePropertyChanged("Period");
}
}
}
这非常好。
但是我也有一些这样的属性:
public bool EnableConsignor
{
get
{
return (ConsignorViewModel.Id != 0);
}
}
它没有setter,因为id“自动”更改(每次调用ConsignorViewModel
的保存时。但这会导致“系统”不知道何时bool的问题从false更改为true(因为没有调用RaisePropertyChanged
)。
答案 0 :(得分:4)
对于这些类型的属性,您需要在更改从属数据时引发PropertyChanged
。类似的东西:
public object ConsignorViewModel
{
get { return consignorViewModel; }
set
{
consignorViewModel = value;
RaisePropertyChanged("ConsignorViewModel");
RaisePropertyChanged("EnableConsignor");
}
}
可以在任何方法中调用 RaisePropertyChanged
,因此只需执行任何会改变EnableConsignor
返回值的操作。以上只是一个例子。
答案 1 :(得分:2)
我刚刚写了这篇文章,它一直很好用
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class CalculatedProperty : Attribute
{
private string[] _props;
public CalculatedProperty(params string[] props)
{
this._props = props;
}
public string[] Properties
{
get
{
return _props;
}
}
}
ViewModel基础
public class ObservableObject : INotifyPropertyChanged
{
private static Dictionary<string, Dictionary<string, string[]>> calculatedPropertiesOfTypes = new Dictionary<string, Dictionary<string, string[]>>();
private readonly bool hasComputedProperties;
public ObservableObject()
{
Type t = GetType();
if (!calculatedPropertiesOfTypes.ContainsKey(t.FullName))
{
var props = t.GetProperties();
foreach (var pInfo in props)
{
var attr = pInfo.GetCustomAttribute<CalculatedProperty>(false);
if (attr == null)
continue;
if (!calculatedPropertiesOfTypes.ContainsKey(t.FullName))
{
calculatedPropertiesOfTypes[t.FullName] = new Dictionary<string, string[]>();
}
calculatedPropertiesOfTypes[t.FullName][pInfo.Name] = attr.Properties;
}
}
if (calculatedPropertiesOfTypes.ContainsKey(t.FullName))
hasComputedProperties = true;
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
if (this.hasComputedProperties)
{
//check for any computed properties that depend on this property
var computedPropNames =
calculatedPropertiesOfTypes[this.GetType().FullName]
.Where(kvp => kvp.Value.Contains(propertyName))
.Select(kvp => kvp.Key);
if (computedPropNames != null)
if (!computedPropNames.Any())
return;
//raise property changed for every computed property that is dependant on the property we did just set
foreach (var computedPropName in computedPropNames)
{
//to avoid stackoverflow as a result of infinite recursion if a property depends on itself!
if (computedPropName == propertyName)
throw new InvalidOperationException("A property can't depend on itself");
OnPropertyChanged(computedPropName);
}
}
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
示例:
public class ViewModel : ObservableObject
{
private int _x;
public int X
{
get { return _x; }
set { SetField(ref _x, value); }
}
private int _y;
public int Y
{
get { return _y; }
set { SetField(ref _y, value); }
}
//use the CalculatedProperty annotation for properties that depend on other properties and pass it the prop names that it depends on
[CalculatedProperty("X", "Y")]
public int Z
{
get { return X * Y; }
}
[CalculatedProperty("Z")]
public int M
{
get { return Y * Z; }
}
}
请注意:
SetField
设置字段,如果有新值,则会提升属性SetField
,因为
{c> 5.0,[CallerMemberName]
为你做了这件事。SetField
,则必须通过
属性名称SetField
调用OnPropertyChanged(“PropertyName”),它将引发
PropertyChanged用于所有属性
依赖于它。nameof(Property)
OnPropertyChanged
会自行调用
特性XAML进行测试
<StackPanel>
<TextBox Text="{Binding X,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBox Text="{Binding Y,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="{Binding Z,Mode=OneWay}"></TextBlock>
<TextBlock Text="{Binding M,Mode=OneWay}"></TextBlock>
</StackPanel>