当属性在用户界面中连接多个源时,可以使用什么模式来确保属性被更新。
例如,我有一个窗口标题的字符串属性。它显示应用程序名称(常量字符串),程序集版本(只读字符串)以及根据用户输入加载的类型的实例属性。
有没有一种方法可以使title属性订阅实例属性,以便在加载实例时标题自动更新?
现在,在加载配方时,它将更新title属性。但是我想扭转这种情况,以使食谱不知道标题。它只是广播它已被加载,然后任何需要对正在加载的配方使用react的事件都将单独处理该事件。
哪种设计模式适合于此?
最佳答案
我在MVVM库中使用以下类,以允许属性更改层叠到相关属性。如果您认为它对您有用,请随时使用:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace AgentOctal.WpfLib
{
public class PropertyChangeCascade<T> where T : ObservableObject
{
public PropertyChangeCascade(ObservableObject target)
{
Target = target;
Target.PropertyChanged += PropertyChangedHandler;
_cascadeInfo = new Dictionary<string, List<string>>();
}
public ObservableObject Target { get; }
public bool PreventLoops { get; set; } = false;
private Dictionary<string, List<string>> _cascadeInfo;
public PropertyChangeCascade<T> AddCascade(string sourceProperty,
List<string> targetProperties)
{
List<string> cascadeList = null;
if (!_cascadeInfo.TryGetValue(sourceProperty, out cascadeList))
{
cascadeList = new List<string>();
_cascadeInfo.Add(sourceProperty, cascadeList);
}
cascadeList.AddRange(targetProperties);
return this;
}
public PropertyChangeCascade<T> AddCascade(Expression<Func<T, object>> sourceProperty,
Expression<Func<T, object>> targetProperties)
{
string sourceName = null;
var lambda = (LambdaExpression)sourceProperty;
if (lambda.Body is MemberExpression expressionS)
{
sourceName = expressionS.Member.Name;
}
else if (lambda.Body is UnaryExpression unaryExpression)
{
sourceName = ((MemberExpression)unaryExpression.Operand).Member.Name;
}
else
{
throw new ArgumentException("sourceProperty must be a single property", nameof(sourceProperty));
}
var targetNames = new List<string>();
lambda = (LambdaExpression)targetProperties;
if (lambda.Body is MemberExpression expression)
{
targetNames.Add(expression.Member.Name);
}
else if (lambda.Body is UnaryExpression unaryExpression)
{
targetNames.Add(((MemberExpression)unaryExpression.Operand).Member.Name);
}
else if (lambda.Body.NodeType == ExpressionType.New)
{
var newExp = (NewExpression)lambda.Body;
foreach (var exp in newExp.Arguments.Select(argument => argument as MemberExpression))
{
if (exp != null)
{
var mExp = exp;
targetNames.Add(mExp.Member.Name);
}
else
{
throw new ArgumentException("Syntax Error: targetProperties has to be an expression " +
"that returns a new object containing a list of " +
"properties, e.g.: s => new { s.Property1, s.Property2 }");
}
}
}
else
{
throw new ArgumentException("Syntax Error: targetProperties has to be an expression " +
"that returns a new object containing a list of " +
"properties, e.g.: s => new { s.Property1, s.Property2 }");
}
return AddCascade(sourceName, targetNames);
}
public void Detach()
{
Target.PropertyChanged -= PropertyChangedHandler;
}
private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e)
{
List<string> cascadeList = null;
if (_cascadeInfo.TryGetValue(e.PropertyName, out cascadeList))
{
if (PreventLoops)
{
var cascaded = new HashSet<string>();
cascadeList.ForEach(cascadeTo =>
{
if (!cascaded.Contains(cascadeTo))
{
cascaded.Add(cascadeTo);
Target.RaisePropertyChanged(cascadeTo);
}
});
}
else
{
cascadeList.ForEach(cascadeTo =>
{
Target.RaisePropertyChanged(cascadeTo);
});
}
}
}
}
}
ObservableObject
只是实现INotifyPropertyChanged
的基类。您应该能够相当容易地替换自己的。您可以这样使用它:
class CascadingPropertyVM : ViewModel
{
public CascadingPropertyVM()
{
new PropertyChangeCascade<CascadingPropertyVM>(this)
.AddCascade(s => s.Name,
t => new { t.DoubleName, t.TripleName });
}
private string _name;
public string Name
{
get => _name;
set => SetValue(ref _name, value);
}
public string DoubleName => $"{Name} {Name}";
public string TripleName => $"{Name} {Name} {Name}";
}
构造函数中的行将
Name
属性的更改级联为DoubleName
和TripleName
属性。默认情况下,出于性能原因,它不会检查级联中的循环,因此它依赖于您不创建它们。您可以选择将级联上的PreventLoops
设置为true
,这将确保对每个属性仅将PropertyChanged
引发一次。关于c# - 使用MVVM更新WPF中的派生属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45175355/